聚會時間公告: 因應COSCUP 2011, Kalug 8月份休會一次

四月 12, 2014

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» OSDC 2014 Talk: Introduction to Percona XtraDB Cluster and HAProxy

2014.05.07 OSDC 影片檔出爐

percona

很高興可以參加今年 2014 OSDC,投了一篇 Introduction to Percona XtraDB Cluster and HAProxy,主要介紹 Percona XtraDB Cluster 這套 open source 軟體搭配前端 HAProxy,底下是 Slides

OSDC 影片出爐,底下是錄影檔,大家可以參考

十月 9, 2013

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» HAProxy 搭配 CodeIgniter 取使用者真實 IP

前端 Load Balance 首選就是 HAProxy,後端架設 Nginx 搭配 CodeIgniter,紀錄使用者 IP 時,Nginx 總是只有抓到內部 IP 192.168.x.x,而無法抓到真實 Public IP,要抓到 Public IP 必須修改 HAProxy + Nginx 設定檔,HAProxy 只要在 Backend 地方加入 forward 選項,這樣 HAProxy 會送 X-Forwarded-For header 給後端 Nginx。

option forwardfor


接著設定 Nginx 部份,必須編譯 ngx_http_realip_module 模組,在 nginx.conf 加入

set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;

這樣就可以拿到真實 IP 了,最後在 CodeIgniter 找到 config.php 設定檔

/*
|--------------------------------------------------------------------------
| Reverse Proxy IPs
|--------------------------------------------------------------------------
|
| If your server is behind a reverse proxy, you must whitelist the proxy IP
| addresses from which CodeIgniter should trust the HTTP_X_FORWARDED_FOR
| header in order to properly identify the visitor's IP address.
| Comma-delimited, e.g. '
10.0.1.200,10.0.1.201'
|
*/
$config['
proxy_ips'] = '192.168.1.100,192.168.1.101,192.168.1.102';

將 proxy_ips 設定好即可

七月 17, 2013

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» HAproxy 搭配 Nginx port redirect issue

nginx-logo

HAproxy 是一套高效能分散式系統軟體,後端可搭配 Web 或 SQL 服務,這次在後端搭配 Nginx 出現 port redirect 問題,問題很簡單,在 Haproxy 設定 80 port 對應到內部三台 Nginx 機器,但是 Nginx port 設定 8080,這樣當我們在瀏覽網址如下:

http://aaa.bbb.ccc.ddd/test (請注意,最後沒有 slash 喔)

你會發現 Nginx 將網址轉成

http://aaa.bbb.ccc.ddd:8080/test/

為了避免 Nginx 自動將 port 加入到網址列,我們可以透過設定 port_in_redirect,Nginx 預設將此設定為 On,所以將此設定為 off,並且重新啟動 Nginx 即可

port_in_redirect off;

三月 31, 2013

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» Galera Cluster for MySQL Multi-master Replication

galera_mysql_replicationgalera_mysql_replication

最近公司買了幾台機架伺服器來處理 HTTP 跟 DB Load balancer,要做到 DB 的分散式架構,首先需要同步多台機器資料,也就是寫入或更動任意一台單筆資料,另外平行的機器也會同時進行更新。同步的好處可以用來做備援及分散處理連線,而要做到此功能,可子參考網路上評價不錯的 Galera Cluster for MySQL 方案。本篇會介紹在 UbuntuCentOS 6.x final 版本如何安裝 Galera 伺服器套件及設定。要架設 Galera Cluster Server,有兩種套件選擇,一個是 Percona XtraDB Cluster 另一個是 MariaDB Galera Cluster,這次作者會介紹後者的安裝。

Galera Cluster 介紹

為什麼要選擇 Galera Cluster Server,它有什麼優點及功能呢?MySQL/Galera 是一套可以同步多台 MySQL/InnoDB 機器的叢集系統,底下可以列出功能。

  • 同步複製資料
  • 可讀取和寫入叢集系統內任一節點
  • 自動偵測節點錯誤,如果有節點當機,則叢集系統自動移除該節點
  • 可任意擴充節點
  • 採用 row level 方式來平行複製資料

從上面功能看來,我們可以平行任意擴充節點,動態增加伺服器到叢集系統,要做到上面功能,就是利用 Galera library 來做到同步資料處理,同步的詳細細節,可以參考 Galera library 連結。這邊就不再多描述了。

安裝 Galera Cluster Server

本篇介紹的 MySQL Server 是使用 MariaDB 套件,而不是安裝原始的 MySQL。CentOS 和 Ubuntu 安裝方式雷同,前者是用 yum 後者則是 aptitude,在安裝前請先下載對應的 repository 設定檔

CentOS

$ yum -y update && yum -y upgrade
$ yum -y install MariaDB-Galera-server MariaDB-client galera

Ubuntu

$ aptitude -y update
$ aptitude -y install mariadb-galera-server-5.5 galera

啟動 MySQL

$ /etc/init.d/mysql start

設定 Galera Cluster Server

先講一下環境,目前總共兩台 Galera Server,IP 分別是:

Node_1: 192.168.1.100
Node_2: 192.168.1.101

建立 Node_1, Node_2 MySQL User,用來認證使用,先進入 MySQL Console.

$ mysql -u root -p
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 154
Server version: 10.0.1-MariaDB-mariadb1~precise-log mariadb.org binary distribution

Copyright (c) 2000, 2012, Oracle, Monty Program Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

建立 cluster 使用者,密碼為 1234,針對 % 跟 localhost 同時建立。

MariaDB [(none)]> GRANT USAGE ON *.* to cluster@'%' IDENTIFIED BY '1234';
MariaDB [(none)]> GRANT ALL PRIVILEGES on *.* to cluster@'%';
MariaDB [(none)]> GRANT USAGE ON *.* to cluster@'localhost' IDENTIFIED BY '1234';
MariaDB [(none)]> GRANT ALL PRIVILEGES on *.* to cluster@'localhost';

在 192.168.1.100 建立 Galera 設定檔 /etc/mysq/conf.d/wsrep.cnf

[MYSQLD]                                                                                                              
wsrep_provider=/usr/lib64/galera/libgalera_smm.so                                                                    
wsrep_cluster_address="gcomm://"                                              
wsrep_sst_auth=cluster:1234

重新啟動 mysqld 會看到多 listen 4567 port

tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      1118/mysqld
tcp        0      0 0.0.0.0:4567            0.0.0.0:*               LISTEN      1118/mysqld

在 192.168.1.101 建立 Galera 設定檔 /etc/mysq/conf.d/wsrep.cnf 並且將 cluster address 設定為 192.168.1.100

[MYSQLD]                                                                                                              
wsrep_provider=/usr/lib64/galera/libgalera_smm.so                                                                    
wsrep_cluster_address="gcomm://192.168.1.100:4567"                                              
wsrep_sst_auth=cluster:1234

設定完成後,重新啟動 mysql

$ /etc/init.d/mysql restart

要怎麼驗證設定成功呢?可以透過 mysql console 來確認

$ mysql -e "SHOW STATUS LIKE 'wsrep%';"

可以看到結果如下

+----------------------------+--------------------------------------------------------------+
| wsrep_local_state_uuid     | b4e876e0-8c1e-11e2-0800-8da732edfe2a                         |
| wsrep_protocol_version     | 4                                                            |
| wsrep_last_committed       | 516                                                          |
| wsrep_replicated           | 0                                                            |
| wsrep_replicated_bytes     | 0                                                            |
| wsrep_received             | 3                                                            |
| wsrep_received_bytes       | 282                                                          |
| wsrep_local_commits        | 0                                                            |
| wsrep_local_cert_failures  | 0                                                            |
| wsrep_local_bf_aborts      | 0                                                            |
| wsrep_local_replays        | 0                                                            |
| wsrep_local_send_queue     | 0                                                            |
| wsrep_local_send_queue_avg | 0.000000                                                     |
| wsrep_local_recv_queue     | 0                                                            |
| wsrep_local_recv_queue_avg | 0.000000                                                     |
| wsrep_flow_control_paused  | 0.000000                                                     |
| wsrep_flow_control_sent    | 0                                                            |
| wsrep_flow_control_recv    | 0                                                            |
| wsrep_cert_deps_distance   | 0.000000                                                     |
| wsrep_apply_oooe           | 0.000000                                                     |
| wsrep_apply_oool           | 0.000000                                                     |
| wsrep_apply_window         | 0.000000                                                     |
| wsrep_commit_oooe          | 0.000000                                                     |
| wsrep_commit_oool          | 0.000000                                                     |
| wsrep_commit_window        | 0.000000                                                     |
| wsrep_local_state          | 4                                                            |
| wsrep_local_state_comment  | Synced                                                       |
| wsrep_cert_index_size      | 0                                                            |
| wsrep_causal_reads         | 0                                                            |
| wsrep_incoming_addresses   | xxx.xxx.xxx.xxx:3306,xxx.xxx.xx.xxx:3306,xxx.xxx.xxx.xx:3306 |
| wsrep_cluster_conf_id      | 45                                                           |
| wsrep_cluster_size         | 3                                                            |
| wsrep_cluster_state_uuid   | b4e876e0-8c1e-11e2-0800-8da732edfe2a                         |
| wsrep_cluster_status       | Primary                                                      |
| wsrep_connected            | ON                                                           |
| wsrep_local_index          | 0                                                            |
| wsrep_provider_name        | Galera                                                       |
| wsrep_provider_vendor      | Codership Oy <info@codership.com>                            |
| wsrep_provider_version     | 23.2.4(r147)                                                 |
| wsrep_ready                | ON                                                           |
+----------------------------+--------------------------------------------------------------+

看到上述結果,有一個非常重要的數值,那就是 wsrep_ready,正確值是 ON,另外看看 wsrep_cluster_size 是否跟您設置 Node 的數量相同,這兩個如果都正確,那就表示設定成功了,由於上面 192.168.1.100 是主 Cluster Server,現在我們必須互相設定雙方 Address 也就是設定如下

node 01: gcomm://192.168.1.101
node 02: gcomm://192.168.1.100

設定如上述的好處就是,當 Node 01 關機時,資料還是在 Node 2 繼續運作,等到 Node 01 恢復上線後,資料又會從 Node 02 同步複製過來。

增加新 Node

我們可以任意新增多台 Node 到 Cluster 叢集系統裡,設置過程非常簡易

1. 安裝 MariaDB Server
2. 安裝 Galera Library
3. 設定 wsrep_cluster_address="gcomm://192.168.1.100:4567"

設定完成,就可以看到資料庫已經同步複製資料到新 Node 上面。如果遇到任何一台 Node 突然關機,不用緊張,系統會保持目前的資料,等到機器上線時,又會從 gcomm://another_node_ipaddress 同步後續的資料。

動態設定 gcomm://

如果新增一台新的 Node,我們就必須改動其它 Node 的 gcomm:// 設定,並且重新啟動 mysqld 服務,這樣會有些微時間影響,我們可以透過直接修改 GLOBAL wsrep_cluster_address 的值

$ mysql -e "SHOW VARIABLES LIKE 'wsrep_cluster_address';"                                                            
+-----------------------+-------------------------------------------------+                                          
| Variable_name         | Value                                           |                                          
+-----------------------+-------------------------------------------------+                                          
| wsrep_cluster_address | gcomm://xxx.xxx.xxx.xx:4567,xxx.xxx.xx.xxx:4567 |                                          
+-----------------------+-------------------------------------------------+

mysql> SET GLOBAL wsrep_cluster_address='gcomm://192.168.1.100:4567';
Query OK, 0 ROWS affected (3.51 sec)

mysql> SHOW VARIABLES LIKE 'wsrep_cluster_address';
+-----------------------+---------------------------+
| Variable_name         | VALUE                     |
+-----------------------+---------------------------+
| wsrep_cluster_address | gcomm://192.168.1.100:4567|
+-----------------------+---------------------------+

最後需要注意的地方是,由於我們每一台機器都互相設定,如果要關閉全部 Node 機器,請務必將第一台重新設定 gcomm:// 為空值,讓後續重新啟動的機器可以先連上此機器進行同步,

這次把 MySQL Replication 安裝設定完成,也就是完成架構圖的最下面部份

Server-load-balancersServer-load-balancers

之後會介紹如何透過 HAproxy 來處理 MySQL Load Balancer。

三月 20, 2010

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» [FreeBSD]high performance caching reverse proxy: Varnish (安裝架設篇)

varnish-logo-red-64
在上禮拜跟 DarkHero 兄聊到 How To Build a Scalable Web Site (3/6) 的上課講義,互相討論了 MySQL Load balance 以及 http reverse proxy 的方式,以前自己有用 HAProxy 當作 Web 平衡負載,順便紀錄了 HAProxy FreeBSD 安裝方式,這次要來介紹今天重點:Varnish Cache Server,在近幾年流行的 Caching 機制,大家會想到 Squid,只要您設定良好的 Squid 參數,它一定運作的非常穩定,然而它的核心依然是 forward proxy,要架設成 Reverse Proxy 還必需要設定一些參數才可以達到,是有一定的困難性,然而 Varnish Cache Server 底層就是高效能 caching reverse proxy,也因為 Squid 是 1980 年發展出來的,程式架構過於老舊,可以參考 ArchitectNotes 瞭解這部份詳情。也許您會問到 Varnish 可以架設成 forward proxy 嗎?答案是可以的,但是您也許不會這麼做,因為它需要 DNS 技術,以及需要一個非常大且複雜的 Varnish VCL(Varnish Configuration Language) file。

1. 今天要介紹如何在 FreeBSD 系統安裝,在介紹之前,系統必須先安裝好 apache,這樣才可以正確啟動,利用 ports 安裝:

cd /usr/ports/www/varnish/
make install clean

2. 修改 /etc/rc.conf

# varnishd
varnishd_enable="YES"
varnishd_listen=":80"
varnishd_admin=":81"
varnishd_backend=":5566"
varnishd_config="/usr/local/etc/varnish/default.vcl"

上面設定意思是說 Varnish listen on port 80,傳送 traffic 到後端 5566 port,管理連接埠是 81,也可以使用指令方式:

varnishd -a :80 -b localhost:8080 -T localhost:6082

Varnishd listen on port 80,and forwarding traffic to a web server listen on localhost port 8080. It also turns on the management interface on port 6082.

3. 修改 default.vcl (Varnish Configuration Language)
VCL 檔案告訴 Varnishd 正確的處理每個 request processing,包含在接受到 request 之前所處理的行為 vcl_recv(),另外還有 vcl_hit()、vcl_miss() 等…,都是用來處理 cache 如果存在或者是不存在時的情境 request。FreeBSD 預設放在 /usr/local/etc/varnish/default.vcl。打開此檔案,您會看到:

backend default {
   .host = "127.0.0.1";
   .port = "80";
}

您只要把 host = “127.0.0.1″ 改成你後端要連接的 ip 或者是 host,這樣 Varnish 會 forward traffic 到您的 web server。接下來只要啟動 apache 跟 Varnish 就算是初步架設完成。

/usr/local/etc/rc.d/apache22 restart
/usr/local/etc/rc.d/varnishd restart

Varnish_01
大家可以看到 61.*.*.* 連到本機 80 port,接下來 Varnish 在開啟隨機 57475 port 連接到 Web Server 5566 port。

Q:如何讓 apache 紀錄正確的 Client IP 到 log 檔案呢?
1. 打開 Vcl config 檔案,寫入
Varnish configuration:

sub vcl_recv {
  # Add a unique header containing the client address
  remove req.http.X-Forwarded-For;
  set    req.http.X-Forwarded-For = client.ip;
  # [...]
}

2. 開啟 apache httpd.conf 加入此行:

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" varnishcombined

Varnish_02

Q:如何讓 PHP 程式紀錄正確的 Client IP?
可以參考底下程式碼就可以完全抓到 Proxy 後面真正使用者IP,否則您的 Web 只會抓到 Reverse Proxy 的 IP Address。

function getIP() {
  if (validip($_SERVER["HTTP_CLIENT_IP"])) {
    return $_SERVER["HTTP_CLIENT_IP"];
  }
  foreach(explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $ip) {
    if (validip(trim($ip))) {return $ip;}
  }
  if (validip($_SERVER["HTTP_X_FORWARDED"]))
  {
    return $_SERVER["HTTP_X_FORWARDED"];
  }
  else if (validip($_SERVER["HTTP_FORWARDED_FOR"]))
  {
    return $_SERVER["HTTP_FORWARDED_FOR"];
  }
  else if (validip($_SERVER["HTTP_FORWARDED"]))
  {
    return $_SERVER["HTTP_FORWARDED"];
  }
  else if (validip($_SERVER["HTTP_X_FORWARDED"]))
  {
    return $_SERVER["HTTP_X_FORWARDED"];
  }
  else
  {
    return $_SERVER["REMOTE_ADDR"];
  }
}

function validip($ip) {
  if (!empty($ip) && ip2long($ip)!=-1) {
    $reserved_ips = array (
      array('10.0.0.0','10.255.255.255'),
      array('127.0.0.0','127.255.255.255'),
      array('169.254.0.0','169.254.255.255'),
      array('172.16.0.0','172.31.255.255'),
      array('192.168.0.0','192.168.255.255'),
    );
    foreach ($reserved_ips as $r) {
      $min = ip2long($r[0]);
      $max = ip2long($r[1]);
      if ((ip2long($ip) >= $min) && (ip2long($ip) <= $max)) return false;
    }
    return true;
  }
  else
  {
    return false;
  }
}

Q:rotate Varnish log file every day?
打開 /etc/newsyslog.conf,加入底下兩行

/var/log/varnish.log        640     7   *   @T00    JB  /var/run/varnishlog.pid
/var/log/varnishncsa.log    640     7   *   @T00    JB  /var/run/varnishncsa.pid

每天12點進行 log 備份,使用 gzip 壓縮 log 檔案。

Related View

support:

biggo.com.tw

biggo.sg

A Django site.