2015/3/23

RWD Responsive Web Design

RWD Responsive Web Design 中文翻譯為響應式網頁設計或是適應性網頁設計,由於行動裝置盛行,為了能夠同時讓手機、平板電腦、一般PC電腦的瀏覽器能夠在不同的螢幕大小的條件下,都能夠在不縮放以及橫向捲動畫面的要求下,以最佳瀏覽體驗瀏覽一個網頁,同樣的網頁內容必須再藉由 RWD 要求要注意的幾項設計重點。

但我們倒可以直接將 RWD 翻譯為「負責任」的網頁設計,因為如果使用者同時會有不同螢幕大小的設備瀏覽網頁,網站設計者就必須「承擔起責任」,讓使用者能以最佳瀏覽體驗瀏覽網頁。

Ethan Marcotte

RWD 最早是在 2010 年由 Ethan Marcotte: A List Apart 提出的一項概念。

在文章的結論中提到 Fluid grids, flexible images, and media queries 是進行 RWD 時最重要的三項技術。

什麼時候要注意 RWD

在進行網頁設計前,有幾項設計需求是要先確認的,否則就可以決定不需要多花時間與人力進行 RWD 開發。

  1. 網站有沒有行動版的需求?
    如果在開發時就已經確定這個網站面對的使用者會使用手機、平板或是PC這三種類形的設備的某一種的瀏覽器,那麼我們就沒有必要花時間進行RWD

  2. 行動版跟PC版的網頁內容重複很多嗎?
    在一個網頁上進行 RWD,就表示我們在處理一個網頁的內容,但不同設備的呈現方式的問題。如果行動版的網頁內容在設計初期就決定要給使用者完全不同的呈現方式,那倒不如在使用者進入網頁後,就根據 media query 的結果,直接將使用者導向到不同的兩個網頁,一個是進入 PC 的網頁,一個是進入行動版的網頁。

如何實踐 RWD

以下內容是從 九個適應性設計小撇步,把你的網站打造成變形金剛!(上篇) 以及 九個適應性設計小撇步,把你的網站打造成變形金剛!(下篇) 兩篇文章中精簡得來的筆記。

讓圖片維持比例自動縮放

  1. 使用 img 標籤放置圖片

    當外層元素寬度縮到小於圖片時,圖片會自動縮小,不設定 width 屬性(或設定為 auto ),讓圖片維持不超過原始大小的寬高。

     <img id="imageid" src="xxx.png"></img>
    
     #imageid {
         height: auto;
         max-width: 100%;
     }
    
  2. 使用 CSS background 屬性

    可彌補 img 無法針對螢幕大小指定合適圖片的缺點。background-size 內兩個值分別代表背景圖的寬高。background-position 的兩個數值則分別指定背景圖片的水平及垂直位置,50% 表示置中。

     <div id="divid"></div>
    
     #divid {
         background-image: url(xxx.png);
         background-position: 50% 50%;
         background-repeat: no-repeat;
         background-size: 100% auto;
         display: block;
         height: 100px;
         width: 100%;
     }
    

讓 div 區塊內容自動往下排列

用 float 讓 div 區塊由右至左排列




用 inline-block 讓 div 區塊由左至右排列




讓 div 區塊置中

  1. 使用 display: block 時-固定寬度加上 margin: auto 屬性
    適用單一區塊水平置中,區塊的寬度設定為某個「小於上層元素」的數值,加上 margin: auto

  2. 使用 display: inline-block 時-在外層設定 text-align: center
    同一行內的多個 inline-block 可以一起置中

  3. 使用絕對定位時-margin: auto 加上左右定位
    用 position: absolute 做絕對定位,固定寬度,再加上 margin: auto,再設定左右位置兩個數值- left: 0; right: 0;




文字對齊

text-align: center 可以置中文字
text-align: justify 可左右分散對齊一整段文字

只要將上層元素設定為 display: table ,並在文字區塊元素上設定 display: table-cell; vertical-align: center; 即可將多行文字垂直置中。

<div id="content">
    <div id="vertical-text">
        大量的文字。
    </div>
</div>
#content > #vertical-text {
    display: table-cell;
    vertical-align: middle;
}

#content {
    border: 1px dotted gray;
    display: table;
    height: 240px;
    width: 100%;
}

排版避免設定高度及絕對位置,寬度設定儘量集中

利用 media query 修改寬度的 css class,不設定高度可讓區塊的內容自動將元件高度撐開。

當螢幕寬度小於 540px 時,文字區塊的寬度就會縮成 400px

.container {
    width: 500px;
}

@media only screen and (max-width: 540px) {
    .container {
        width: 400px;
    }
}

@media only screen and (max-width: 440px) {
    .container {
        width: 300px;
    }
}

隱藏暫不需要的選單、點擊後展開隱藏的頁籤

寬度拉到小於 400px 時,原本左邊的綠色選單就不見了,縮成一個綠色選單鈕,滑鼠放上去時選單就會自動展開




展開頁籤的內容

因為手機上畫面空間不足,通常不適合使用頁籤,這時不如把內容直接展開來,寬度縮到 400px 以下時三個頁籤的內容就會展開來。




使用 Firefox 特有開發工具

Firefox 15.0 以後,按下快速鍵 Ctrl+Shift+M (Windows 及 Linux 平台) 就可以使用適應性設計檢視 (Responsive Design View)。

References

響應式網頁設計 wiki

RWD 流言終結者

2015/3/16

HAProxy and sticky session

在處理 HTTP Load Balance 的時候,很多網路服務都必須考量需不需要使用者登入後才能使用,以往不是 connection oriented 的 HTTP 也因此發展出 HTTP Session Tracking 的技術,在導入 HAProxy 時,也必須考量到,要如何持續支援 Session Tracking 的技術。

HTTP session

以往的網站在處理 session 時,會將 user 相關的資料存放在 client 端的 cookie 裡面,但 cookie 會在每一次 http request 都放在 header 裡面,因此產生了資訊安全的疑慮。

後來就發展了 Server Side 的 Session,以 J2EE jsessionid 為例,就是將 user 相關資料存放於 Server 的記憶體中,由 browser client 端 將一個 session id 紀錄起來,藉由這個 session id,來讓每一次同一個 browser 的所有 request 都可認定為同一個 user。

session 在 J2EE Server 可設定其時效,通常是設定為 10-20分鐘,當 server side session 的資料逾時,session 就會被清除,user 也必須重新登入一次,才能再執行接下來的工作。

通常 browser client 都有 enable cookie 的功能,可以將 session id 存放在 cookie 裡面,比較古老的 browser 沒有支援 cookie,或是 user 刻意將 cookie 功能 disabled 了,這時候 J2EE Server 會自動改用 URL Rewrite 的方式,將 jesssionid 存在 url 上面。

sticky session

當 server 要進行 load balance 的時候,通常會導入硬體或軟體的 load balancer,因為 J2EE Server 有存放了 session 資料,我們可選擇將 server 藉由 cluster 的方式,將 session 互相複製,但這種作法會消耗掉過多的 server side 資源,server 之間互相 replicate session 的次數越多,只會讓 dirty data 與 traffic 更頻繁,發生 data 錯誤的機會也會變高。

sticky session 通常是指 load balancer 支援了 session id 的處理能力,可以認出 http request 裡面的 jsessionid 的資料,並將同一個 jsessionid 的多個 request 都導向到同一個後端的 server 上。

load balancer 可選擇使用 J2EE 的 jessionid,或是使用自訂的 SERVERID 作為 sticky session 的判斷基準。

sticky session in HAProxy

HAProxy 要能夠支援 session id ,可以在 backend 的設定中增加 Cookie 的設定值。

設定說明如下:

cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ] [ postonly ] [ preserve ] [ httponly ] [ secure ] [ domain <domain> ]* [ maxidle <idle> ] [ maxlife <life> ]
  • name
    就是 Cookie 的名稱,會透過 HTTP response 的 "Set-Cookie" header 將 Cookie 設定於 browser client。

  • rewrite | insert | prefix
    rewrite 表示 backend server 會提供此 cookie,但 HAproxy 必須修改此值,並將 server 的 id 填寫進去。因為必須監控所有 response 封包,所以只能在 HTTP close mode 運作。

    insert 表示此 cookie 必須由 HAPRoxy 填寫進去。如果沒有設定 preserve,而 server 也設定了此 cookie,則會先被移除,然後再重新新增進去。通常建議要搭配設定 "nocache" 與 "postonly"

    prefix 表示透過既有的一個 cookie,server id 會填寫到該 cookie 的 prefix 中,當 client 端發送 requst,其夾帶的 cookie value 會有 haproxy 填寫的 prefix,但會被 haproxy 去掉,只保留原本 server 設定的 cookie value,然後再 forward 給 server。

  • indirect
    server 設定的 cookie 都不會送到 client,這可讓 server 完全不知道 haproxy 的 sticky session 機制。

    建議不要同時使用 prefix 與 indirect

  • nocache
    搭配 insert 使用,可讓 cachable respone 貼上 non-cacheable 選項。

  • postonly
    確保 cookie insertion 只會作用在 responses to POST requests,這是 nocache 的替代方案,因為 POST response 都是 not cachable 的,就可以確保 persistence cookie 永遠不會被 cache。

  • preserve
    只能搭配 insert 與 indirect 使用。在這樣的狀況下,haproxy 會保留不更動 response cookie。

    可用在 logout request 之後,要 end persistence的地方,通常 server 會將 cookie 設定為空值。

  • httponly
    當 haproxy 增加新 cookie 時,同時設定了 HttpOnly 的屬性。

  • secure
    當 haproxy 增加新 cookie 時,同時設定了 Secure 的屬性。

  • domain
    設定 cookie 的 domain

  • maxidle
    idle 時間逾時後,cookie 就會沒有作用,只能用在 insert-mode cookies

  • maxlife
    life time 逾時後,cookie 就會沒有作用,只能用在 insert-mode cookies

範例:

cookie JSESSIONID prefix
cookie SRV insert indirect nocache
cookie SRV insert postonly indirect
cookie SRV insert indirect nocache maxidle 30m maxlife 8h

實際上比較完整的範例為

backend web_server_cluster
    mode http
    balance roundrobin
    cookie SERVERID insert indirect nocache
    # Web Server Cluster
    server web1 192.168.1.10:80 check cookie s1
    server web2 192.168.1.20:80 check cookie s2

直接在 JSESSIONID 上加上 server id 的 prefix

frontend ft_web
  bind 0.0.0.0:80
  default_backend bk_web

backend bk_web
  balance roundrobin
  cookie JSESSIONID prefix indirect nocache
  server s1 192.168.1.10:80 check cookie s1
  server s2 192.168.1.20:80 check cookie s2

appsession

設定說明

appsession <cookie> len <length> timeout <holdtime> [request-learn] [prefix] [mode <path-parameters|query-string>]
  • cookie
    application 使用的 cookie name,可讓 haproxy 了解到該 application 處理 session 的 cookie。

  • length
    該 cookie value 儲存的最大字元長度。

  • holdtime
    該 cookie 值不被使用時,保留在 haproxy 記憶體中的時間

  • request-learn
    設定後,haproxy 會由 request 中發現新的 session

  • prefix
    設定 haproxy 將會認定的 cookie prefix

    範例

      appsession ASPSESSIONID len 64 timeout 3h prefix
    

    如果 cookie 是 ASPSESSIONIDXXXX=XXXXX,appsession 的 value 將會是 XXXX=XXXXX

  • mode
    修改 URL parser mode,目前有兩種

    • path-parameters
      這是預設模式,在 path parameter 的裡面尋找 appsession。適用於 JSESSIONID,通常會是在分號的後面。

      當 browser client 不支援 cookie 時,jsessionid 就會放在 url 上面

        a.jsp;jsessionid=5AC6256DD8D4D5D1FDF5D41E9F2FD900?param1=value1&param2=value2
      
    • query-string
      在 query string 裡面尋找 appsession

範例

# Load balancing PHP sessions:

appsession PHPSESSID len 64 timeout 3h request-learn prefix

# Load balancing ASP.Net sessions:

appsession ASP.NET_SessionId len 64 timeout 3h request-learn prefix

# Load balancing ASP sessions:

appsession ASPSESSIONID len 64 timeout 3h request-learn prefix

# Load balancing Java Server sessions:

appsession JSESSIONID len 52 timeout 3h request-learn prefix

比較完整的範例:

global
    maxconn 10000
    daemon

defaults
    log      global
    mode      http
    contimeout      5000
    clitimeout      50000
    srvtimeout      50000

listen webfarm 0.0.0.0:80
    mode http
    balance roundrobin
    option httpchk HEAD /index.html HTTP/1.0
    appsession JSESSIONID len 128 timeout 1m
    server serverA 192.168.1.10:80 check
    server serverB 192.168.1.20:80 check

結語

綜合兩個設定方式的說明,如果認定 browser 一定會支援 cookie,就可以利用 cookie 設定來處理 session id,但如果要同時支援 cookie 以及 url 形式的 session tracking ,就要使用 appsession。

網路上找到的實際範例,多數都是使用 cookie,而不是 appsession。目前除非使用者刻意 disable browser cookie 的機制,否則要遇到不支援 cookie 的 browser 也很困難了。

如果要用 haproxy 專用的 server id 機制,當然就選擇利用 cookie,設定自訂的 cookie name。但如果是要沿用標準的 ASPSESSIONID/PHPSESSID/JSESSIONID,就直接用 appsession 來設定就可以了。

References

富人用 L4 Switch,窮人用 Linux HAProxy!

Configuring haproxy to load balance multiple engine.io servers

Trick My Proxy: Front Apache Tomcat with HAProxy instead of Apache

Sticky Session Load Balancing with HAProxy

load balancing, affinity, persistence, sticky sessions: what you need to know

Haproxy 安裝手記,版本升級並追加 log 機制

HAProxy configuration

2015/3/9

HAProxy

Server提供網路服務時,面臨到一台 Server 的 capacity 瓶頸,這時候,就可以用 L4 Load Balancer 或是 HAProxy 來進行網路分流與 load balance。

HAProxy 目前主要支援兩種 traffic,http 以及一般的 tcp,http traffic 是最主要支援的部份,HAProxy 可深入到 HTTP Traffic 的封包內容,並根據設定的條件修改封包資料。

安裝

我們可以選擇直接用 yum 安裝,目前的版本為 haproxy-1.5.2-2.el6.i686

yum install haproxy

安裝完成後,執行檔會放在 /usr/sbin 目錄中。

也可以選擇直接從 source code 編譯。

tar -zxvf haproxy-1.5.11.tar.gz
cd haproxy-1.5.11

make clean

#  linux 核心為 2.6.x 可以用使用 TARGET=linux26 參數進行最佳化,如果真的不知道可以用 TARGET=generic 即可
make TARGET=linux26 USE_OPENSSL=1 ADDLIB=-lz
make install

安裝完成後,執行檔會裝在 /usr/local/sbin 目錄中。

自行編譯後,還需要製作 service 啟動檔,這也是從 yum 安裝的 haproxy 抄來的,改一下執行檔的路徑就可以用了。

vi /etc/init.d/haproxy

#!/bin/sh
#
# haproxy
#
# chkconfig:   - 85 15
# description:  HAProxy is a free, very fast and reliable solution \
#               offering high availability, load balancing, and \
#               proxying for TCP and  HTTP-based applications
# processname: haproxy
# config:      /etc/haproxy/haproxy.cfg
# pidfile:     /var/run/haproxy.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0

exec="/usr/local/sbin/haproxy"
prog=$(basename $exec)

[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog

lockfile=/var/lock/subsys/haproxy

check() {
    $exec -c -V -f /etc/$prog/$prog.cfg
}

start() {
    $exec -c -q -f /etc/$prog/$prog.cfg
    if [ $? -ne 0 ]; then
        echo "Errors in configuration file, check with $prog check."
        return 1
    fi

    echo -n $"Starting $prog: "
    # start it up here, usually something like "daemon $exec"
    daemon $exec -D -f /etc/$prog/$prog.cfg -p /var/run/$prog.pid
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}

stop() {
    echo -n $"Stopping $prog: "
    # stop it here, often "killproc $prog"
    killproc $prog 
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

restart() {
    $exec -c -q -f /etc/$prog/$prog.cfg
    if [ $? -ne 0 ]; then
        echo "Errors in configuration file, check with $prog check."
        return 1
    fi
    stop
    start
}

reload() {
    $exec -c -q -f /etc/$prog/$prog.cfg
    if [ $? -ne 0 ]; then
        echo "Errors in configuration file, check with $prog check."
        return 1
    fi
    echo -n $"Reloading $prog: "
    $exec -D -f /etc/$prog/$prog.cfg -p /var/run/$prog.pid -sf $(cat /var/run/$prog.pid)
    retval=$?
    echo
    return $retval
}

force_reload() {
    restart
}

fdr_status() {
    status $prog
}

case "$1" in
    start|stop|restart|reload)
        $1
        ;;
    force-reload)
        force_reload
        ;;
    check)
        check
        ;;
    status)
        fdr_status
        ;;
    condrestart|try-restart)
      [ ! -f $lockfile ] || restart
    ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|try-restart|reload|force-reload}"
        exit 2
esac

設定

設定檔會放在 /etc/haproxy/haproxy.cfg,設定內容有兩大部份,global 以及 proxy,而 proxy 中,有四個主要項目:defaults, listen, frontend 以及 backend 的部份。

global

範例

global
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    nbproc        1

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

此區塊可使用的設定項目

  • chroot
  • daemon
    以 daemon 的方式運行
  • nbproc
    daemon mode 才能使用,設定 process 的數量
  • user/uid
  • group/gid
  • log
  • pidfile
  • stats

  • maxconn
    可接受最大的連線數量

  • noepoll
    不使用 linux 的 epoll
  • nokqueue
    不使用 BSD 的 kqueue polling
  • debug
    將所有訊息顯示到 console
  • quiet
    不顯示任何訊息

獨立的 haproxy logfile

我們必須調整一些 rsyslog 的設定,才能將 haproxy 的 log 放到獨立的 logfile 裡面。

vi /etc/rsyslog.conf

# 把這兩行的註解刪除
# Provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 514

# 限制 rsyslog UDP 只會用在 127.0.0.1
$UDPServerAddress 127.0.0.1

新增一個檔案 /etc/rsyslog.d/haproxy.conf

vi /etc/rsyslog.d/haproxy.conf

local2.*    /var/log/haproxy/haproxy.log

也可以用這個方式,將 log file 區分不同的 log level,要不然就放在同一個 log file 裡面就好了。

local2.=info     /var/log/haproxy-info.log
local2.notice    /var/log/haproxy-allbutinfo.log

重新啟動系統 log 記錄器

service rsyslog restart

編輯 haproxy.logrotate,讓 haproxy.log 檔案每天產生一個,然後保留 120 天的資料。

vi /etc/logrotate.d/haproxy.logrotate

/var/log/haproxy/haproxy.log {
    missingok
    notifempty
    sharedscripts
    rotate 120
    daily
    compress
    postrotate
        reload rsyslog >/dev/null 2>&1 || true
    endscript
}

proxy

proxy 設定是由 defaults, listen, frontend, backend 組成。

  • defaults name
    設定所有其他區塊的預設參數,如果設定檔中,後面又多了一塊 defaults 設定,後面的設定會覆蓋掉前面的設定。

  • frontend name
    描述接受 client 端 request 的 listening sockets。

  • backend name
    描述 proxy 會將 frontend 接收的 request 連線 forward 到後端的一組 servers。

  • listen name
    定義完整的 proxy,包含了 frontend 與 backend,這個部份的定義,通常用在設定一般的 TCP-only traffic。

proxy 定義的名稱 name ,必須要使用這些字元 upper and lower case letters, digits,
'-' (dash), '_' (underscore) , '.' (dot) and ':' (colon)。

HTTP traffic

HAProxy 針對 http traffic 提供了五種 mode

  • KAL: keep alive ("option http-keep-alive")
    這是預設值,所有處理中的 request, response 以及 connection 都會持續 idle 並保持連線。

  • TUN: tunnel ("option http-tunnel")
    只會處理第一個 request 與 response,其他封包都會直接 forward,不進行任何分析與處理,目前建議不使用這個模式,因為會對 logging 以及 HTTP processing 造成問題。

  • PCL: passive close ("option httpclose")
    就跟 tunnel mode 一樣,在處理完第一個封包後,會雙向加上 "conneciton: close",讓連線結束。

  • SCL: server close ("option http-server-close")
    在收到最後的 response 時,就會 close server-facing connection,但仍然持續保留 client-facing connection。

  • FCL: forced close ("option forceclose")
    在收到最後的 response 之後,就會關閉連線。

在一個 http connection 經過 frontend 與 backend 設定之後,可能會有兩段 proxy mode 的設定,最終這個 connection 的 mode 是由下表的規則決定,keep-alive 是最弱的設定值,而 force close 是最強的設定值。KAL < TUN < PCL < SCL < FCL

                          Backend mode

                | KAL | TUN | PCL | SCL | FCL
            ----+-----+-----+-----+-----+----
            KAL | KAL | TUN | PCL | SCL | FCL
            ----+-----+-----+-----+-----+----
            TUN | TUN | TUN | PCL | SCL | FCL
 Frontend   ----+-----+-----+-----+-----+----
   mode     PCL | PCL | PCL | PCL | FCL | FCL
            ----+-----+-----+-----+-----+----
            SCL | SCL | SCL | FCL | SCL | FCL
            ----+-----+-----+-----+-----+----
            FCL | FCL | FCL | FCL | FCL | FCL
defaults

這個區塊的參數,是設定檔後面所有參數的預設設定值,下面的設定值會蓋掉上面的。

範例

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

此區塊可使用的設定項目

  • balance
    選擇使用那一種 algorithm 來執行 load balance。
  • mode { tcp|http|health } (defaults 為 http)
  • log global
  • option httplog
    Enable logging of HTTP request, session state and timers
  • option http-server-close
    Enable or disable HTTP connection closing on the server side
  • option forwardfor except 127.0.0.0/8
    因為 X-Forwarded-For header 裡面會有 client 的 IP,HAProxy 會利用此 header,所以增加此設定,可讓 X-Forwarded-For header 都轉送到 後端的 server
  • option redispatch
    當 client 連到掛掉的機器時, 可重新再分配
  • retries 3
    失敗重試次數
  • timeout http-request 10s
    Set the maximum allowed time to wait for a complete HTTP request,等待 HTTP Request 完成的 timeout 時間
  • timeout queue 1m
    Set the maximum time to wait in the queue for a connection slot to be free,等待 queue 釋放空間的 timeout 時間
  • timeout connect 10s
    Set the maximum time to wait for a connection attempt to a server to succeed.
  • timeout client 1m
    Set the maximum inactivity time on the client side. 當有 long polling 時,這個參數在 frontend 要調長到 long polling 最大的等待時間
  • timeout server 1m
    Set the maximum inactivity time on the server side. 當有 long polling 時,這個參數在 backend 要調長到 long polling 最大的等待時間
  • timeout http-keep-alive 10s
    Set the maximum allowed time to wait for a new HTTP request to appear
  • timeout check 10s
    Set additional check timeout, but only after a connection has been already established.
  • maxconn 3000
    連線到某個 server 的最大連線數
backend balance mode

這是 balance 設定值可選填的 algorithm 項目。

  • roundrobin
    每個 server 依據權值 weight 依序選擇 servers,這是最快的 algorithm,server weight 會根據 instance slow start 的狀況而隨時變動。這個 algorithm 在每個 backend 最多可處理 4095 個 active server nodes。如果有個 server node 在 down 之後馬上又變成 active,可能會需要數百個 requests 之後,才會再 join 這個 server farm。

  • static-rr
    每個 server 根據 weight 依序選擇 server,類似 roundrobin,但權值 weight 是固定的。沒有 active server node 的處理數量上限。

  • leastconn
    目前處理最少連線數量的 server,作為下一個 connection 轉向的 server。建議在較長 tcp session 的服務中使用這個演算法,例如 LDAP, SQL, etc...,不適合用在類似 HTTP 這種短 sesion time 的 protocol。

  • first
    server nodes 之中,選擇第一個可使用的 server,當這個 server 連線處理數量達到 maxconn 時,就會選擇下一個 server,所以設定檔中一定要有 maxconn 設定值。

  • source
    source IP address 會 hashed 並除以 total weight of running serer,用這個結果來決定要選擇那一個 server,這可確保同一個 client IP 的 traffic 會連到同一個 server。如果 server nodes 數量改變了,就會因為重新計算 hash,造成 clients 連線到不同的 servers。

  • uri
    可用 uri 的 ? 前面的部份,或是整個 uri 進行 hash,可確保同一個 uri 會轉給同一個 server。這個設定通常用在 proxy caches 與 anti-virus proxyies,用以提高 cache hit rate。

    有兩個 optinal parameter: len 與 depth。len 就是使用 uri 裡面幾個字元,但因為 uri 通常是 / 開頭,所以不要將 len 設定為 1。

    depth 表示計算 hash 的 maxmimum directory depth,出現一個 / 代表多了一層 depth。

  • url_param
    使用 HTTP Get request URL 裡面的參數,如果用到了 "check_post",就會使用 POST request 裡面的參數。

  • hdr(name)
    會尋找名稱為 name 的 HTTP header 欄位。

    optonal 參數 "use_domain_only" 可用在指定 header 為 Host 的時候,如果 Host 為 "haproxy.1wt.eu" 就只會考慮 "1wt"

  • rdp-cookie, rdp-cookie(name)
    尋找設定的 cookie,通常就是 session ID,如果沒有 session ID 就會使用 roundrobin algorithm。

一些設定的參考範例

範例 1:雖然這個是 listen 區塊,但實際上是設定一個網頁 service,提供一個 HAProxy 網頁監控界面。

# HAProxy 網頁管理界面
listen stats *:2000
    mode http
    stats enable
    stats hide-version
    stats realm Haproxy\ Statistics
    stats uri /
    # 帳號密碼
    stats auth username:password
    stats refresh 10s

範例 2:

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
     errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

frontend localnodes
    bind *:80
    mode http
    default_backend nodes

backend nodes
    mode http
    balance roundrobin
    option forwardfor
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }
    option httpchk HEAD / HTTP/1.1\r\nHost:localhost
    server web01 127.0.0.1:9000 check
    server web02 127.0.0.1:9001 check
    server web03 127.0.0.1:9002 check

範例 3:

defaults
    log    global
    mode    http
    option    httplog
    option    dontlognull
    retries    3
    option redispatch
    option httpclose
    maxconn    32768
    contimeout    5000
    clitimeout    50000
    srvtimeout    50000

listen www-balancer
    bind 0.0.0.0:80
    balance         roundrobin
    server          w1 192.168.1.10:80 weight 3 check
    server          w2 192.168.1.20:80 weight 3 check
    option          httpchk GET /robots.txt
    option          forwardfor

範例 4: DB1 與 DB2 設定為 Master-Master Replication

defaults
    mode tcp
    option redispatch   #連前當前配置失敗時,允許重新配發到他台機器
    option abortonclose
    timeout connect 5000ms
    timeout client 30000ms
    timeout server 30000ms
    log 127.0.0.1 local0
    balance roundrobin   #使用輪替演算法,分散主機流量

listen proxy
    bind *:3306  #綁定192.168.11.180的3306 Port為運行主機端口
    mode tcp
    server db1 192.168.1.10:3306  weight 1 check  

    #後端主機.178,179,權重值配置

    server db2 192.168.1.20:3306  weight 2 check  

    #check 參數檢查改主機是否Live

範例 5:

defaults
    timeout server 86400000
    timeout connect 86400000
    timeout client 86400000
    timeout queue   1000s

# [HTTP Site Configuration]
listen  http_web 192.168.10.10:80
    mode http
    balance roundrobin  # Load Balancing algorithm
    option httpchk
    option forwardfor
    server server1 192.168.1.10:80 weight 1 maxconn 512 check
    server server2 192.168.1.20:80 weight 1 maxconn 512 check

# [HTTPS Site Configuration]
listen  https_web 192.168.10.10:443
    mode tcp
    balance source# Load Balancing algorithm
    reqadd X-Forwarded-Proto:\ http
    server server1 192.168.1.10:443 weight 1 maxconn 512 check
    server server2 192.168.1.20:443 weight 1 maxconn 512 check

References

富人用 L4 Switch,窮人用 Linux HAProxy!

使用 HAProxy 完成 網站分流, 平衡負載

[FreeBSD & Linux]網站分流:簡易架設 HAProxy 伺服器

使用 Haproxy 搭建高可用與負載平衡叢集

2015/3/8

駭客人生 - Kevin Mitnick, William L.Simon

駭客人生是 Kevin Mitnick 的駭客自傳,他是史上第一位受到美國聯邦調查局(FBI)通緝的大師級駭客,更是將社交工程(Social Engineering)技倆發揮到極致之人。曾入侵昇陽、威網(Novell)、諾基亞、摩托羅拉等跨國公司系統,造成數十億美元的損失。

Kevin Mitnick 追求的,是駭客行為後,帶來的成就感

雖說是數十億元的損失,但實際上他本人並不認為是如此,他認為都是FBI,在追捕他入獄之後,說服這些受害的公司把受害金額拉高而造成的。

雖然他有強烈的駭客欲望,著迷於一些大型公司內部的專案技術,例如諾基亞、摩托羅拉、昇陽以及 NEC 等公司,目的是取得這些公司的作業系統和手機的產品程式碼,找出和突破其漏洞,但實際上他從來沒有因為這些駭客行為,而去販售並取得大筆的獲利。

故事的內容從 Kevin Mitnick 小時候開始講起,他著迷於撥打免費的電話,他運用社交工程的方法取得內部的資訊,不需要花錢就可以打電話,後來漸漸地,他需要更多資訊,進入電話公司的交換機,然後進入手機的時代,他需要手機的原始碼,修改手機 ESN,然後變身為其他身份的手機撥打電話。

社交工程可以幫助他取得內部的資料,從撥接的號碼,擁有特殊權限的單位,一些關鍵的人名等等,有時候,在遇到無法直接取得的資訊時,還能夠透過社交工程,讓一些人直接把資料以傳真或是傳送檔案的方式把資料自動交給他。

米特尼克在2000年獲得假釋機會,但條件是三年內不得接觸任何網路設備,且七年內不得對外談論駭客技術。但他發現他有另一種方式,可以合法從事駭客行為,又可以賺錢,他提供駭客的服務,檢驗公司的資訊安全的等級,這也是他現在維生的方式。

Hacker/Cracker/Hacktivist White/Black/Grey Hat

原本的 Hacker 這個稱呼,到現在已經發展出其他的稱號,這篇文章: 認識駭客(Hacker),Cracker(破壞者),激進駭客(Hacktivist)網路犯罪份子(Cybercriminal),白帽(White Hat),黑帽(Black Hat),灰帽(Grey Hat) 有提供這幾種稱號的用法與解釋。

雖然稱號不大相同,但實際上,這些人的行為都是一樣的,利用管理者或使用者的疏忽,加上資訊系統的漏洞,取得一些特殊的權限,或是機密的資料。最大的差異應該就是取得資料後,這些天才們要怎麼使用資料,非請而來的攻擊行為,販售機密的資料,都會形成一種網路的犯罪行為。

是 駭客 還是 工程師

駭客其實也是個資訊工程師,在 EE人生:工程師可能成為駭客嗎? 這篇文章中,討論到工程師與駭客的差異。

在資訊安全的領域中,要知道怎麼防護系統之前,首先要先知道怎麼破壞它,換句話說,要知道怎麼防堵駭客的攻擊,首先要成為一個真正的駭客。

其實 Kevin Mitnick 的經驗也同樣驗證了這個想法,他被 FBI 追捕成功,也是因為一個駭客下村努的報復與協助。

一般資訊工程師,追求的是要如何成就一個完美的資訊系統,要以最精簡的方式,提供服務,而駭客的本質,就在破壞,突破現有系統設定的限制。

所以我們會常常聽到一種說法,一個成功的創業家,其實就是在以他自己的駭客方式,進行一種產業的革命,當他成功的時候,其實就是改變了這個世界的時候。

駭客 是天生的測試員

一般在撰寫程式的時候,總是會假設輸入的資料的內容,不會多做懷疑,但其實這也是發生資訊系統漏洞的時候。

一個稱職的測試工程師,除了不需要運用社交工程的部份以外,他跟駭客一樣,必須要想方設法破壞這個系統,找出系統的漏洞。

開發者與測試員其實有點像是光與影的兩面,也只有光與影互相完美地搭配,同時存在的時候,我們才能看到一個美侖美奐的美景。

面對系統開發,在最完美的組合下,我們可以試著在光明面進行系統開發,然後再轉向黑暗面補強系統,這樣才能建構出一個兼顧的資訊系統。但實際上,要能做到這樣的成果,我認為有幾項原則:

  1. 熟悉你使用的程式語言
  2. 熟悉你使用的函式庫或套件
  3. 在程式的界面上,不管是使用者界面或是程式與程式的界面上,要注意輸入與輸出資料的格式,並在程式中,檢查輸入資料的格式
  4. 寧願多寫檢查與保護資料格式的程式,也不能偷懶認為輸入的資料,一定會跟假設的一樣

References

駭客人生

Kevin Mitnick 的駭客人生

駭客人生,出獄三年內不能碰網路!

2015/3/2

redis HA sentinel

如果要建置 redis 的 HA 環境,有兩種選擇,第一種是基於 replication 的 sentinel,第二種則是還在 alpha 階段的 redis 3.0 提供的 cluster 功能。

redis cluster 目前還在 alpha 階段,他所提供最重要的功能,就是 auto sharding,

HA

考量到 HA 的議題,要思考兩點 (1) data availability (2) service availability,以目前 2.8 版的狀況,redis 支援 data replication,針對 service availabilty 也提供了sentinel做 redis daemon 的監控。

redis 與 sentinel 的關係可參考下圖,這兩個分別是獨立的 daemon,redis 服務的 port 為 tcp 6379,sentinel 是 tcp 26379。



當 master node 消失,sentinel 會自動將 slave node 改為 master,並繼續提供 redis 服務,如果 slave node 有多個,那麼 sentinel 只會自動將某一個 slave node 改為 master。



以下我們假設 master node ip 為 192.168.1.10,slave node 為 192.168.1.20

data replication

依照上圖,我們必須在 slave 的機器上,設定 replication,將資料由 master node 複製過來,這裡要注意的是,master node 不能填寫這個設定。

修改 192.168.1.20 機器上的 redis 設定檔

vi /etc/redis/6379.conf

slave of 192.168.1.10 6379

然後 restart redis

service redis restart

簡單的驗證方式如下,在 master node 寫入,在 slave node 讀取。

> redis-cli -h 192.168.1.10 -p 6379
192.168.1.10:6379> set hello world

> redis-cli -h 192.168.1.20 -p 6379
192.168.1.20:6379> get hello
"world"

Sentinel

根據 Sentinel 的文件,主要功能有四項

  1. Monitor
    sentinel 會持續檢查 master 與 slave redis server 的狀態
  2. Notification
    sentinel 會通知系統管理員或是透過 API 通知其他程式,監控的 redis server 發生故障的狀況。
  3. Automatic failover
    如果 master node 故障,sentinel 會自動找到一個 slave node 更改為 master,其他的 slave node 也會自動將 master 更改為新的 master node。
  4. Configuration provider
    sentinel 作為 client 端處理 service discovery 的功能,連接到 sentinel 的 clients 會詢問目前 redis 的 master node 的 ip,當 failover 發生時,會自動回報為新的 ip。

接下來要設定 sentinel,用來監控 redis server,設定檔內容如下

> vi /etc/redis/sentinel.conf
port 26379
dir /tmp

daemonize yes
pidfile /var/run/redis-sentinel.pid
loglevel verbose
logfile /var/log/redis-sentinel.log

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 192.168.1.10 6379 1
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

當 quorum 是 2,就表示需要有兩個 sentinels 回報 master node 失效時,才會進行 failover process。

vi /etc/init.d/redis-sentinel

#!/bin/sh
#
# sentinel - this script starts and stops the sentinel daemon
#
# chkconfig:   - 86 14
# description:  Redis Sentinel is monitor and aut-failover for redis.
# processname: redis-sentinel
# config:      /etc/redis/sentinel.conf
# config:      /etc/sysconfig/redis-sentinel
# pidfile:     /var/run/redis-sentinel.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0

redis="/usr/local/bin/redis-sentinel"
prog=$(basename $redis)

REDIS_CONF_FILE="/etc/redis/sentinel.conf"

[ -f /etc/sysconfig/redis-sentinel ] && . /etc/sysconfig/redis-sentinel

lockfile=/var/lock/subsys/redis-sentinel

start() {
    [ -x $redis ] || exit 5
    [ -f $REDIS_CONF_FILE ] || exit 6
    echo -n $"Starting $prog: "
    daemon $redis $REDIS_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}

stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -TERM
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

restart() {
    stop
    sleep 1
    start
}

rh_status() {
    status $prog
}

rh_status_q() {
    rh_status >/dev/null 2>&1
}

case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart)
        $1
        ;;
    status)
        rh_status
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart}"
        exit 2
esac
chkconfig redis-sentinel on
service redis-sentinel start

當我們把 master node 的 redis service 停掉,就會在 sentinel 的 log 中看到 fail-over 的過程。

[2880] 03 Feb 15:28:37.861 * +slave slave 192.168.1.20:6379 192.168.1.20 6379 @ mymaster 192.168.1.10 6379

[2880] 03 Feb 15:39:24.062 # +sdown master mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:24.063 # +odown master mymaster 192.168.1.10 6379 #quorum 1/1
[2880] 03 Feb 15:39:24.063 # +new-epoch 1
[2880] 03 Feb 15:39:24.063 # +try-failover master mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:24.113 # +vote-for-leader bf0e42815cc599c9dc2b931b08342f3575767a23 1
[2880] 03 Feb 15:39:24.113 # +elected-leader master mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:24.113 # +failover-state-select-slave master mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:24.181 # +selected-slave slave 192.168.1.20:6379 192.168.1.20 6379 @ mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:24.181 * +failover-state-send-slaveof-noone slave 192.168.1.20:6379 192.168.1.20 6379 @ mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:24.284 * +failover-state-wait-promotion slave 192.168.1.20:6379 192.168.1.20 6379 @ mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:25.158 - -role-change slave 192.168.1.20:6379 192.168.1.20 6379 @ mymaster 192.168.1.10 6379 new reported role is master
[2880] 03 Feb 15:39:25.282 # +promoted-slave slave 192.168.1.20:6379 192.168.1.20 6379 @ mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:25.282 # +failover-state-reconf-slaves master mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:25.284 # +failover-end master mymaster 192.168.1.10 6379
[2880] 03 Feb 15:39:25.285 # +switch-master mymaster 192.168.1.10 6379 192.168.1.20 6379
[2880] 03 Feb 15:39:25.286 * +slave slave 192.168.1.10:6379 192.168.1.10 6379 @ mymaster 192.168.1.20 6379
[2880] 03 Feb 15:39:55.364 # +sdown slave 192.168.1.10:6379 192.168.1.10 6379 @ mymaster 192.168.1.20 6379

sentinel client

使用 sentinel ,需要搭配 client 的實做,讓 client 能夠自動尋找 master node 並 fail-over 到 backup node。

如果是使用 java,通常是用 jedis library,JedisSentinelTest 提供利用 jedis 連結 sentinel 的範例。

另外 jedis-sentinel-pool 則是提供了 jedis connection pool 的實做。

docker for redis cluster

我們也可以搭配 docker 建置 redis cluster 環境,這篇文章:用Docker構建分佈式Redis集群 提供了利用 docker 建置 redis cluster 環境的過程範例。