顯示具有 apache 標籤的文章。 顯示所有文章
顯示具有 apache 標籤的文章。 顯示所有文章

2012年10月19日 星期五

apache 的 response time 很慢

注意到某台 web server 反應速度非常慢, 依序檢查相關資訊如下:

  • tail -f /var/log/apache2/access.log 可知 request per second 頗高, 但不確定有多少
  • vmstat 回報 ram, cpu, io 都沒什麼 load
  • 用 chrome developer tool 看 (Network 頁), 幾乎都耗在 waiting, 傳資料時間仍然很短
  • 家裡電腦 ping web server < 200ms
  • wget localhost 也要等個數秒以上

綜合以上資訊可知, 機器應該可以負擔, 不是 CPU / RAM / disk IO / 頻寬 / 連線不穩等問題。

依照以前寫 network programming 的經驗, 當 concurrent new connection 量很高, 高出接收 connection 的 daemon 能負擔的量時, 得等一陣子才能連上線, 剩下的處理時間到不會增加多少。所以推測此時 web server 應該是類似的情況, 所以要從縮短受理新 connection 的時間下手。

參考 Apache Performance Tuning 的說明, 最後提高 MaxClients, 縮短 KeepAliveTimeout, 情況就變正常了。

文中提到計算 MaxClients 的相關指令挺方便的:

ps -ylC httpd --sort:rss: 得知 httpd 用的 memory
free -k: 得知目前記憶體用量

MaxClients = 可用記憶體 / 單一 httpd 用的記憶體

2011年9月6日 星期二

在同一個 apache 裡跑兩個 port

使用 apache2 + mod_wsgi 的作法架 ReviewBoard 的話, relative path 有 bug, 無法將 ReviewBoard 架在 http://.../reviews/ 這樣的位置。網路上搜搜看到也有人提到同樣的問題。自己試著硬解了一陣子, 沒有很乾淨又一致正確的結果。後來想說, 乾脆在同一台機器的 apache 裡跑聽兩個 port 好了, 這樣就能在用專屬非 80 的 port 來架 ReviewBoard

查了一下, 架法還滿簡單的: 見 VirtualHost Examples, 另外 CommonMisconfigurations - Httpd Wiki 也滿有幫助的。使用多個 port 時, 記得要加 listen。Ubuntu 的話是加在 /etc/apache2/ports.conf 裡, 它會被 /etc/apache2/apache2.conf include 進去。

2011年6月3日 星期五

Ubuntu 移除 apache2 的方法

開啟很久沒用的 VM, 不知為啥 apache2 的 modules 都不見了, 無法正常運作 (能正常運作才有鬼哩), 用 aptitude remove 再重裝也無效, 摸索一陣子才找到解法:

  1. 先用 dpkg -l | grep apache2 找出相關套件
  2. sudo aptitude purge apache2 apache2-mpm-worker apache2-utils apache2.2-common libapache2-mod-php5 libapache2-mod-wsgi # 這裡接上個指令找到相關的套件

2011年3月4日 星期五

apache2 rotate log 的注意事項

apache2 沒有收到 SIGHUP 就關掉 log 檔重開這回事, 所以若手動改 log 檔, apache2 仍會寫入同一個檔案。Ubuntu 上 logrotate 的作法是, 在 rotate apache2 的 log 檔後, 會要求 apache2 reload config, 藉此讓 apache2 關掉重開 log 檔, 好讀到新的檔名。可從 logrotate 設定 apache2 的內容看出這件事 (/etc/logrotate.d/apache2):
/var/log/apache2/*.log {
        weekly
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 640 root adm
        sharedscripts
        postrotate
                if [ -f "`. /etc/apache2/envvars ; echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then
                        /etc/init.d/apache2 reload > /dev/null
                fi
        endscript
}

但若自己架的一些 Web 服務需要「暖機」的程序的話 (比方說在 CGI process 的記憶體內暫存一些前處理的資料), logrotate 作法會拖慢 log rotation 後第一批連進來的使用者。

apache2 有提供 piped logs 來避免 apache2 reload。概念是透過外部程式來 rotate log, 而 apache2 也會知道發生 log rotation, 藉此重開檔案。官網有提到, 除搭配內建的 rotatelogs 外, cronolog 是更彈性的選擇, 在《Apache log rotate 的另一個選擇:cronolog》有詳細的教學。另外要注意的是, cronolog 不像 logrotate 有提供設權限的機制, 自己要另外跑個 crontab 將群組改為 adm, 權限設成 640。

2011年2月18日 星期五

設定 apache2 和 django 壓縮輸出內容

Google Page Speed 分析後, 發現 javascript 和 css 可以壓個七八成, 實在滿可觀的, 就試著來設壓縮。官網 mod_deflate 看看就會設了, 下面的範例是壓縮 css 和 javascript:
AddOutputFilterByType DEFLATE text/css text/javascript application/javascript application/x-javascript
可以查 MIME 列表, 看要壓那些類型的檔案。或是先設全部都有, 再加條件不壓圖。

比較擾人的是, 早期的一些瀏覽器不能正確處理壓縮文件, 官網教學說要這麼設:
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
說明如下:
The third BrowserMatch directive fixes the guessed identity of the user agent, because the Microsoft Internet Explorer identifies itself also as "Mozilla/4" but is actually able to handle requested compression. Therefore we match against the additional string "MSIE" (\b means "word boundary") in the User-Agent Header and turn off the restrictions defined before.

剛看始看看不懂, 後來看到 Internet Explorer User Agent Strings 才知道 IE 會自稱 Mozilla/4, 但它可以處理壓縮, 所以要再加第三行取消前兩行的限制。How To: Optimize Your Apache Site with Mod Deflate
文末提到 IE6 聽說有問題, 但他測不出來, 所以他沒有另外關掉 IE6。

Django 的話, 可以讓 apache2 協助處理, 加個 text/html, 或是加入 GZipMiddleWare, 都很簡單。

測試資料是否有壓縮有很多方法, 最保險的是用 Firefox Live HTTP Headers 看回傳結果有沒有 gzip。不會看 header 的話, 可以透過 Page Speed 或 GIDZipTest 測看看。

2011年1月29日 星期六

用 apache2 轉址

原本想用 virtualhost + script 轉址, 查了一下相關作法, 才發現用 mod_rewrite 簡單多了。懶得看 mod_rewrite 超詳細說明, 可以直接看 URL Rewriting Guide, 有常見問題的設法, 第二則就是轉址 (Canonical Hostnames)。附帶一提, RewriteCond 預設是和之後條件做 AND 運算, 可以下參數改用 OR。對照一下 Guide 再查查相關 directive 大概說明, 就知道怎麼用在自己的情況了。

2015/01/04 更新

注意設定放在 Directory 下會比較複雜, 直接放在最上層或 VirtualHost 可避免「未知」的錯誤, 見 Per-directory Rewrites

2011年1月27日 星期四

REMOTE_ADDR vs. HTTP_X_FORWARDED_FOR

REMOTE_ADDR 是 apache2 設的變數, client 很難欺騙 apache, 但 HTTP_X_FORWARDED_FOR 是由 HTTP header 的 X-Forwarded-For 來的, client 可以自己設任意值。所以若要用 IP 做認證, 要用 REMOTE_ADDR。缺點就是使用者透過 proxy 連線就會將同一 proxy IP 的人一視同仁, 挺多再用 cookie 做區別。

幾篇參考資料:

2011年1月20日 星期四

Ubuntu 上編 apache2 + mod_wsgi 原始碼以及 module、設定的簡介

基本安裝流程
  • sudo aptitude install auto-apt checkinstall
  • sudo chown YOUR_ACCOUNT /usr/local/src
  • cd /usr/local/src
  • wget http://ftp.stut.edu.tw/var/ftp/pub/OpenSource/apache//httpd/httpd-2.2.17.tar.gz
  • tar zxvf httpd-2.2.17.tar.gz
  • cd httpd-2.2.17
  • auto-apt run ./configure --enable-mods-shared=most --enable-so --enable-rewrite --enable-expires --enable-cache --enable-usertrack
  • make
  • sudo checkinstall -D --fstrans=no
  • ln -s /usr/local/apache2/bin/apachectl /etc/init.d/apachectl
  • 將 /etc/init.d/apachectl 加到 /etc/rc.local 或用 udpate-rc.d 裝到 /etc/rc*.d/ 裡
安裝和設定 mod_wsgi
啟動、停止等操作:
  • 透過 apachectl 操作, 它是一個 shell script, 最新版的很精簡, 幫忙傳參數給 httpd
  • 附帶一提, 裝 Ubuntu package 的話, 操作方式變成 /etc/init.d/apache2 -> apache2ctl -> apache2, 多一層 /etc/init.d/apache2 另外包一些操作。
  • httpd 或 apache2 只是編出來的名稱, 可用 --with-program-name 設定
安裝、設定的相關資訊
  • ./configure --help:看有那些參數可選, 都不選也還 OK 啦
  • apache2 有兩種 MPM (Multi-Processing Modules) 模式: prefork (multiple process) 或 worker (multiple thread), 預設是 prefork。Python thread 有頂頂大名的 GIL, 所以我先用 prefork 減少日後擔心的項目
  • auto-apt 幫忙檢查 package dependency, 不用它也 OK
  • checkinstall: 用來打包 package 檔, 之後方便管理。上述的流程會產生 httpd 的 package 檔, 並裝進去
  • dpkg -L httpd 觀看裝了那些東西
  • dpkg -r httpd 移除 apache2
  • 用 httpd -l 觀看執行檔內編進了那些 modules, 若沒用 --enable-mods-shared=most 的話, 會編進一大堆
  • httpd -V 觀看預設參數, 像是 config 檔位置 (-D SERVER_CONFIG_FILE), 知道這個才有辦法開始設定
apache2 與 modules 的關係
  • 官網的 DSO (dynamic shared object) 和 apxs 說明得很清楚, apache2 採用 dynamic loading 的方式載入 module, 可以讓 apache2 daemon 動態載入 modules, 不需重編 apache2 執行檔 。若 module 版號差不多, 應該是不用太在意 apache2 版本和 module 的版本對應
  • 一但從原始碼編 apache2 後, 之後就要用原始碼編 module。不然裝 module package 時, 由於 package dependency 的關係, 會要求裝 apache2 的一些核心 package。雖說也可以照裝 apache2 的 package (記得要手動移掉 /etc/init.d/apache2 避免開機時啟動到舊的 apache2), 然後複製 /usr/lib/apache2/modules/ 下的東西到 /usr/local/apache2/modules/, 或直接改設定檔載入 /usr/lib/apache2/modules/。只是會編 apache2 後, 編 module 就沒那麼難了
  • 可從官網 module 文件的 status 欄位了解如何取得該 module。base 表示編完 apache2 後就會放在 /usr/local/apache2/modules/ 下, 若 ./configure 時有加上 --enable (如 --enable-usertrack), 則會產生對應的設定在產生的設定檔裡。 ./configure 時沒加 enable, 之後想用 base 的 module, 就要自己找一下載入和設定的方式
  • 若 status 為 extension (如 mod_ssl), apache2 官方的 module 似乎要透過重新 configure 重編的方式取得, 不確定有沒有更簡單的作法。
  • third-party module (如 mod_wsgi) 會有文件說明如何用 apxs 編出 module。
apache2 的設定檔和 Debian (Ubuntu) 的 apache2 配置
  • Managing Apache2 modules the Debian way 清楚的說明 Debian 配置 apache2 設定檔的方式。基本運作方式是透過預設的 /etc/apache2/apache2.conf 讀入分配好的目錄: /etc/apache2/conf.d、/etc/apache2/mods-enabled/、/etc/apache2/sites-enabled/, 來將 module 的設定和 virtualhost 的設定分散出去, 並保留 conf.d 提供其它客置需求。我覺得配得挺漂亮的。
  • 為了讓整個設定保有彈性, /etc/apache2 還有下不少工夫, 像是設好 apache2ctl  和 config 檔, 將共同變數 USER、GROUP、PID_FILE 放在 /etc/apache2/envvars。雖然架構很漂亮, 當自己編 apache2 時, 硬要將原本的設定套到 Debian 配置, 只會事倍功半。另一方面我編出來的 apachectl 和原本 package 內的 apache2ctl 參數有些差異, 搞了一陣子就決定自己切 /usr/local/apache2/conf/httpd.conf 和 /usr/local/apache2/conf/conf.d/ 即可
  • 這篇提到可用 ./configure --with-program-name=apache2 --enable-layout=Debian 編出類似的配置, 我試的結果的確滿像的, 不過還是湊不起來, 試一下失敗後就沒繼續弄了
php5
  • 我有順便編看看 php5, 去除一堆延伸功能, 至少照網路上的教學編原始碼沒問題, 挺多遇到 mysql 錯誤: "Cannot find MySQL header files under", 照這篇說的, 裝好 libmysqlclient-dev 再編就 OK 了
  • 用 checkinstall 安裝 package 時遇到 "dpkg error: trying to overwrite ...", 它嘗試覆蓋 /usr/local/apache2/conf/httpd.conf。找了一下只看到「用力覆蓋過去」的解法。也就是出現錯誤後再手動用 dpkg -i --force-overwrite php 裝。不確定之後 dpkg -r php 時, 是不是也會移除 httpd.conf。不過我的設定檔都有存在 VCS 裡, 出事的話也很容易復原
其它參考資料

                    2010年10月19日 星期二

                    小心瓶頸就在資安軟體

                    這是一個很囧的心得。

                    今天下午發覺有個簡單的操作竟然有時候要花一秒鐘, 做了許多交叉比對, 發覺以下驚人的跡象:
                    • 同一份程式、同一份資料庫, 連用 apache 跑的服務時, 有時要花一秒; 但連 django development server 時卻是瞬殺。
                    • log 顯示, 不管連那種 web server, 我的程式都只花不到 0.05s。
                    • 用 Chrome Developer Tool 的 Resource 觀看, 慢的時候, 大部份時間是 waiting。
                    • 用 VNC 連到 server, 再從本機連 apache, 也是瞬殺。
                    • 從我的電腦或同事的電腦連 apache 都是有時會延遲, 我的情況比較頻繁。
                    • 同樣的連結點第二次時會變快, 但過一陣子再點似乎又會變慢。
                    • 我主要是測 AJAX + GET。
                    綜合以上線索, 我下了一個很腦殘的推論: apache 有鬼!! 不知是 apache 讀檔案還是做些奇怪的事, 或是 mod_wsgi 做奇怪的事, 造成延遲。也許 apache 有奇妙的 cache, 造成同一連結點第二次時會變快。
                    和來和同事 R 討論後, 他問了一個問題: 「該不會是是資安軟體阻擋吧?」 (*1) 於是同事 P 在 server 隨便開個奇怪的 port, 將該 port 的封包轉到本機的 port 80。我再試的結果, 速度就變正常了!!
                    事後回顧發覺自己的推論太腦殘了, apache + 80 和 django development server + some port, 我卻忽略了 port 的事, 只看到 web server 的差異。差點要去找 apache 或 mod_wsgi profiling 的方法, 幸好有先和同事閒聊, 省了一堆工, 也放下心裡一塊大石頭。

                    註 *1: 資安軟體會攔截對外連的 URL, 阻止連某些網站。沒想到連對內也有攔截.....。

                    2010年6月3日 星期四

                    在 http response header 裡設 expire 以減少 request 數量

                    High Performance Web Sites: Rule 3 - Add an Expires Header 提到可以在 http response header 裡設 expire time 減少 client 送出 http request。藉由 last modified time 和 etag 只能省去 client 重下載檔案的時間, client 仍會送出 http request。若一個頁面載入十個檔案 (JavaScript、CSS、圖檔等), client 仍會送出十個 http request。別小看 http request 的時間, 數量一多若有其中一兩個被拖到, 網頁就會拖慢。

                    Apache 設定 expire 的方式很簡單:
                    1. a2enmod expires  # enable mod_expires
                    2. 編輯 /etc/apache/conf.d/cache.conf (檔名隨便取):

                      ExpiresActive On
                      ExpiresByType text/css "access plus 4 weeks"
                      ExpiresByType text/js "access plus 4 weeks"
                      ExpiresByType image/gif "access plus 4 weeks"
                      ExpiresByType image/jpeg "access plus 4 weeks"
                      ExpiresByType image/png "access plus 4 weeks"
                      這樣會把 CSS、JavaScript 和常用圖檔設成使用者拿到檔案的四週後才會過期。
                    也可以設成依檔案修改時間來設過期時間, 而不是使用者拿到的時間, 參見 mod_expires 看更詳細的說明。

                    但設了 expire time 後帶出一個新問題, 萬一 server 端在 expire time 前有更新檔案怎麼辦? 在該頁的留言裡有討論幾種解法的優缺點:
                    1. 加版本編號到檔名裡, 如 yahoo_2.0.6.js, Yahoo 是這麼做的。缺點是要改 code (HTML header、CSS 內文等), 也得想想怎麼和版本控制整合, 變成每次 deployment 就得改檔名。
                    2. 加上 query string, 像是 base.css?v=CREATED_TIMESTAMP。缺點是有些 CDN 業者和 Proxy 不支援靜態檔案的 query string, 若它們忽略 query string, 靜態檔案就不會被更新了。也有人提到 HTTP 1.1 規定有 query string 的檔案不能被 cache, 雖說大部份瀏覽器仍會 cache。但也有人提到 HTTP 1.1 規定有送出 expire time 就能被 cache。無論如何, CDN 業者和 Proxy 的問題較頭大。
                    3. 加上 virtual URL, 像是 /media/css/base.css 變成 /media/VERSION/css/base.css, 這樣就無 CDN、Proxy 的問題。不過不知道要怎麼在 web server 做對應的處置較好, 設 alias 忽略中間那段似乎是最簡單的解法? 即設個 alias 將 /media/VERSION/css/base.css 指到 /media/css/base.css。這樣 CSS 內用相對路徑取圖的話, 應該也不用改 CSS code。修改幅度最小。
                    附帶一提, 檢測用的相關工具:
                    • Firefox 的 YSlow 會查那些靜態檔案沒設 expire time。
                    • Firefox 的 Live HTTP Headers 會秀出目前連線的所有 http connection 的 request 和 response headers。可以用來查看 Expire 是否有被設對, 還有 client 是否真的沒發出 request。
                    • Chrome 的 Speed Tracer 可以秀出頁面載入所有檔案的開始時間, 包含 http request、response 開始和結束的時間。也有 header 內的訊息。多種願望, 一次滿足!!
                    瀏覽器對待 expire time 的行為如下 (試出來的):
                    • Firefox 按重新整理就不會理會 expire time, 在網址列按 Enter 重讀會參考 expire time。
                    • Chrome 按重新整理或在網址列按 Enter 重讀都不會理會 expire time, 只有在網頁內點選的連結才會參考 expire time。

                    在 Fedora 下裝 id-utils

                    Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...