Trong mô hình Load Balancing, HAProxy đứng giữa client và các backend servers, vì vậy kết nối mã hóa SSL giữa client và server sẽ có thể được thực hiện theo các cách thức sau:

  • Thực hiện yêu cầu kết nối mã hóa SSL giữa Client và HAProxy, còn từ HAProxy thực hiện các kết nối không mã hóa với backend servers. Phương thức này gọi là SSL Termination

  • Thực hiện yêu cầu kết nối trực tiếp giữa Client và các backend servers. Tuy nhiên, khi đó chúng ta lại không thể thực hiện Add/Set phần Header. Phương thức này gọi là SSL Passthrough

  • Một cách thức khác là kết hợp cả 02 cách thức trên mà vừa đảm bảo phân tải xử lý của CPU, cũng như cho phép thực hiện chỉnh sửa thông tin Header.

Bài viết đi vào mô hình thực hiện ở cách thức kết nối SSL theo cách thức SSL Termination.

haproxy-ssl

Trong bài viết này, tôi sẽ hướng dẫn cấu hình HAProxy sử dụng SSL với  self-signed certificate và LetsEncrypt

1. Tự tạo self-signed certificate

Đây là certificate mà ta sẽ tự tạo ra trên chính server. Certificate này không được xác thực bởi các nhà cung cấp chứng chỉ số (CA). Mục đích tạo self-signed certificate là để kiểm tra hoặc sử dụng trong môi trường mạng local.

Các bước tạo self-signed certificate và cấu hình HAProxy sử dụng SSL như sau:

Step1: Tạo private key

Thực hiện tạo key cho local domain “example.local”.

  • Tạo thư mục chứa certificate
mkdir -p /etc/ssl/example.local
cd /etc/ssl/example.local
  • Tạo key với mã hóa RSA
openssl genrsa -des3 -out example.key 2048

root@vnsys:/etc/ssl/example.local# openssl genrsa -des3 -out example.key 2048
Generating RSA private key, 2048 bit long modulus
…………………………………………………+++
……………………………..+++
e is 65537 (0x10001)
Enter pass phrase for example.key:
Verifying – Enter pass phrase for example.key:

Quá trình tạo key ở trên yêu cầu nhập pass phrase. Để remove passphrase (mục đích là bỏ qua quá trình hỏi pass phrase) thực hiện như sau:

openssl rsa -in example.key -out example.key

root@vnsys:/etc/ssl/example.local# openssl rsa -in example.key -out example.key
Enter pass phrase for example.key:
writing RSA key

Step2: Đăng ký certificate (Certificate Signing Request -CSR)

Dựa vào key đã tạo ở Step1, chúng ta thực hiện đăng ký certificate

openssl req -new -key example.key -out example.csr

root@vnsys:/etc/ssl/example.local# openssl req -new -key example.key -out example.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.’, the field will be left blank.
Country Name (2 letter code) [AU]:VN
State or Province Name (full name) [Some-State]:HN
Locality Name (eg, city) []:CG
Organization Name (eg, company) [Internet Widgits Pty Ltd]:IT
Organizational Unit Name (eg, section) []:Example Inc
Common Name (e.g. server FQDN or YOUR name) []:example.local
Email Address []:

Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Step3: Tạo certificate – CRT

Dựa vào CSR ở trên, chúng ta tạo một self-signed certificate, với hiệu lực 365 ngày

openssl x509 -req -days 365 -in example.csr -signkey example.key -out example.crt

root@vnsys:/etc/ssl/example.local# openssl x509 -req -days 365 -in example.csr -signkey example.key -out example.crt
Signature ok
subject=/C=VN/ST=HN/L=CG/O=IT/OU=Example Inc/CN=example.local
Getting Private key

Step4: Mở rộng key và cert thành tệp pem

Tệp pem là tệp tin chứa định dạng có thể chỉ public certificate hoặc có thể gồm tập public key, private key và root certificate kết hợp với nhau.

Chúng ta thực hiện nối private key và certificate ở trên để tạo thành tệp pem

cat example.key example.crt >>example.pem

Step5: Cấu hình HAProxy với SSL cho website

Sau khi đã có tệp pem, chúng ta cấu hình HAProxy để cho phép xử lý luồng kết nối với SSL.

Phần frontend

Chúng ta gán listen port cho https với port default là 443, không sử dụng giao thức sslv3, và chỉ định vị trí tệp pem “example.pem”.

  • Sử dụng đồng thời cả 02 giao thức HTTP và HTTPS
#Frontend Section
frontend http-in
        bind *:80
    bind *:443 ssl no-sslv3 crt /etc/ssl/example.local/example.pem 
        acl example-acl hdr(host) -i example.local
        use_backend example if example-acl

Ở đây chúng ta sử dụng tham số “crt” (certificate) trong bind. Thiết lập này được sử dụng khi cấu hình SSL, chỉ định đường dẫn tệp pem. Chúng ta có thể khai báo nhiều “crt” với nhiều tệp pem được chỉ định cho nhiều site mà sử dụng SSL.

  • Chỉ sử dụng HTTPS

Khi đó chúng ta cần thêm tùy chọn redirect khi truy cập http đến https

Redirect toàn bộ các site trong cấu hình HAProxy, thì sử dụng:

redirect scheme https code 301 if !{ ssl_fc }

Redirect với site chỉ định

redirect scheme https if { hdr(Host) -i example.local } !{ ssl_fc }

Khi đó, cấu hình frontend như sau:

## Frontend section
frontend http-in
        bind *:80
        bind *:443 ssl no-sslv3 crt /etc/ssl/example.local/example.pem
        redirect scheme https if { hdr(Host) -i example.local } !{ ssl_fc } 
        acl example-acl hdr(host) -i example.local
        use_backend example if example-acl

Note: Thay vì sử dụng default port cho https là 443, chúng ta muốn redirect đến https với một cổng tùy chọn, chẳng hạn 8443. Khi đó cấu hình tùy chọn như sau:

## Frontend section
frontend http-in
        bind *:80
        bind *:8443 ssl no-sslv3 crt /etc/ssl/example.local/example.pem
        http-request replace-value Host (.*):80 \1
        http-request redirect location https://%[req.hdr(Host)]:8443%[capture.req.uri] if !{ ssl_fc }
        acl example-acl hdr(host) -i   example.local
        use_backend example if example-acl

Phần Backend

Phần này, chúng ta sẽ khai báo thông tin thuật toán load balancing sẽ sử dụng, các thông tin web server , các tùy chọn về log và thiết lập tùy chọn header.

backend example
        balance roundrobin
        server server1 192.168.10.111:8080 weight 1 check
        server server2 192.168.10.112:8080 weight 1 check
        server server3 192.168.10.113:8080 weight 1 check
        option httplog
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request add-header X-Forwarded-Proto https if { ssl_fc }

Ở đây, chúng ta có một số tùy chọn:

option forwardfor : Được sử dụng để thêm header “X-Forwarded-For”, vì vậy backend có thể nhận được địa chỉ IP thực của client truy cập. Nếu không có tùy chọn này thì backend sẽ chỉ nhận được thông tin IP của HAProxy.

http-request set-header X-Forwarded-Port %[dst_port] : thiết lập một header “X-Forwarded-Port” vì vậy mà backend biết được port nào để sử dụng khi redirect URLs.

http-request add-header X-Forwarded-Proto https if { ssl_fc } : Thêm header X-Forwarded-Proto với thiết lập scheme là https nếu yêu cầu truy cập https. Cái này, cho phép backend xác định được scheme để sử dụng khi gửi URL lúc redirect

Khi đó, nội dung tệp cấu hình HAProxy đầy đủ cho site example.local sử dụng SSL như sau:

# Global settings
global
        log 127.0.0.1   local0
        pidfile     /var/run/haproxy.pid
        stats socket /var/lib/haproxy/stats
        maxconn 100000
        user haproxy
        group haproxy
        daemon
        quiet
# Proxies settings
## Defaults section
defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        retries 3
        option      redispatch
        maxconn     100000
        retries                 3
        timeout http-request    5s
        timeout queue           30s
        timeout connect         30s
        timeout client          30s
        timeout server          30s
        timeout http-keep-alive 30s
        timeout check           30s
## Frontend section
frontend http-in
        bind *:80
        bind *:443 ssl no-sslv3 crt /etc/ssl/example.local/example.pem
        redirect scheme https if { hdr(Host) -i example.local } !{ ssl_fc } 
        acl example-acl hdr(host) -i example.local
        use_backend example if example-acl
backend example
        balance roundrobin
        server server1 192.168.10.111:8080 weight 1 check
        server server2 192.168.10.112:8080 weight 1 chec
        server server3 192.168.10.113:8080 weight 1 check
        option httplog
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request add-header X-Forwarded-Proto https if { ssl_fc }
## Statistics settings
listen statistics
        bind *:1986
        stats enable
        stats admin if TRUE
        stats hide-version
        stats realm Haproxy\ Statistics
        stats uri /stats
        stats refresh 30s
        stats auth keepwalking86:ILoveVietnam$

2. Sử SSL sử dụng LetsEncrypt

Trong phần này, chúng ta sẽ sử dụng certificate được cung cấp bởi một nhà cung cấp certificate (certificate authority – CA). Sử dụng certificate này cho phép website được xác thực khi truy cập internet. Với điều kiện hạn chế, chúng ta có thể sử dụng certificate miễn phí của một số nhà cung cấp.

Trong phần cấu hình này, chúng ta sử dụng Let’s Encrypt để nhận certificate miễn phí với mỗi lần renew là 90 ngày.

Yêu cầu:

  • Website publish với tên miền internet.

Step1: Cài đặt Certbot

Để lấy certificate từ Let’s Encrypt chúng ta sử dụng công cụ Certbot

  • Cài đặt LetsEncrypt trên Ubuntu-16.04+
sudo add-apt-repository -y ppa:certbot/certbot
sudo apt-get update
sudo apt-get install -y certbot
  • Cài đặt LetsEncrypt trên CentOS 7
yum -y install epel-release
yum -y install certbot

Step2: Cấu hình HAProxy khi chưa https

Certbot có thể tự động lấy certificate, nhưng điều kiện cần là nó phải xác minh được domain hợp lệ. Tôi cấu hình haproxy cho website với tên miền mymusic.vn (thay mymusic.vn với thông tin website thực cần cấu hình) như sau:

# Global settings
global
        pidfile     /var/run/haproxy.pid
        maxconn 100000
        user haproxy
        group haproxy
        daemon
        quiet
        stats socket /var/lib/haproxy/stats
        log 127.0.0.1   local0

# Proxies settings
## Defaults section
defaults
        log     global
        mode    http
        option  httplog
        option  forwardfor
        option  dontlognull
        retries 3
        option      redispatch
        maxconn     100000
        retries                 3
        timeout http-request    5s
        timeout queue           10s
        timeout connect         10s
        timeout client          10s
        timeout server          10s
        timeout http-keep-alive 10s
        timeout check           10s

## Frontend section
frontend http-in
        bind *:80
        acl mymusic-acl hdr(host) -i mymusic.vn
        use_backend mymusic if mymusic-acl
## Backend section
backend mymusic
        balance roundrobin
        server server1 192.168.10.111:8080 weight 1 check
        server server2 192.168.10.112:8080 weight 1 check
        server server3 192.168.10.113:8080 weight 1 check
## Statistics settings
listen statistics
        bind *:1986
        stats enable
        stats admin if TRUE
        stats hide-version
        stats realm Haproxy\ Statistics
        stats uri /stats
        stats refresh 30s
        stats auth keepwalking86:ILoveVietnam$

Step3: Tạo New letsencrypt certificate bằng certbot

Nếu muốn tạo certificate và cấu hình haproxy, chúng ta cần cấp phép cho LetsEncrypt. Khi đó cần thiết lập một site stand-alone để listen khi ủy quyền cho LetsEncrypt.

Khi yêu cầu một certificate từ LetsEncrypt, nó sẽ yêu cầu được truy cập vào tệp tin được ủy quyền theo dạng đường dẫn “http://your-domain/well-known/acme-challenge/” và đường dẫn này bind đến cổng 80 cho yêu cầu http và 443 cho https. Khi đó chúng ta thực hiện yêu cầu certificate với một trong hai cách sau:

Cách 1: Stop HAProxy

Chẳng hạn haproxy đang listen ở port 80, khi đó stop haproxy và thực hiện chạy dòng lệnh sau để yêu cầu certificate từ LetsEncrypt

systemctl stop haproxy
certbot certonly --standalone -d mymusic.vn --non-interactive --agree-tos --email keepwalking86@mymusic.vn

root@vnsys:/home/keepwalking# certbot certonly –standalone -d mymusic.vn –non-interactive –agree-tos –email keepwalking86@mymusic.vn
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for mymusic.vn
Waiting for verification…
Cleaning up challenges

IMPORTANT NOTES:
– Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/mymusic.vn/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/mymusic.vn/privkey.pem
Your cert will expire on 2019-04-29. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew all of your certificates, run
“certbot renew”
– Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.
– If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

Sau khi kết thúc quá trình nhận certificate cho site mymusic.vn từ LetsEncrypt thành công, khi đó thư mục chứa các tệp certificate, key nằm ở /etc/letsencrypt/live/mymusic.vn/

Cách 2: Haproxy vẫn hoạt động trên port 80

Để tránh xung đột port 80 với HAProxy, chúng ta sẽ cấu hình haproxy để bảo cho LetsEncrypt listen với một port khác, chẳng hạn 8080. Thực hiện cấu hình haproxy như sau:

  • Thêm tùy chọn acl trong phần frontend

Sử dụng cổng 80 cùng với site mymusic.vn nhưng khi đó, cần tạo acl để truy cập URL mà chứa chuỗi “/.well-known/acme-challenge/”

acl letsencrypt-acl path_beg /.well-known/acme-challenge/
use_backend letsencrypt if letsencrypt-acl
  • Cấu hình phần backend

Khai báo cổng listen sẽ sử dụng

backend letsencrypt
    server letsencrypt 127.0.0.1:8080

Khi đó reload haproxy và thực hiện tạo certificate như sau:

certbot certonly --standalone -d mymusic.vn --non-interactive --agree-tos --email keepwalking86@mymusic.vn --http-01-port=8080

root@vnsys:/home/keepwalking# certbot certonly –standalone -d mymusic.vn –non-interactive –agree-tos –email keepwalking86@mymusic.vn –http-01-port=8080
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for mymusic.vn
Waiting for verification…
Cleaning up challenges

IMPORTANT NOTES:
– Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/mymusic.vn/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/mymusic.vn/privkey.pem
Your cert will expire on 2019-04-29. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew all of your certificates, run
“certbot renew”
– If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

Trong đó, thuộc tính các tham số truyền vào như sau:

–standalone: Sử dụng chế độ standalone để đạt một certificate khi chúng ta không muốn sử dụng hoặc không có web server đang tồn tại. Sử dụng standalone, chúng ta kết hợp gồm lệnh “certonly”

-d mymusic.vn: chỉ định public domain hợp lệ “mymusic.vn” mà trỏ bản ghi “A” vào địa chỉ IP Public của haproxy. Tùy chọn “-d” có thể được sử dụng cho nhiều domain và yêu cầu cùng một certificate.

–non-interactive –agree-tos –email keepwalking86@mymusic.vn: Cho phép xác nhận các điều khoản, cũng như vào thông tin email tự động khi LetsEncrypt yêu cầu

–http-01-port=8080: Cái này bảo cho stand-alone server sẽ listen trên port 8080 của một request HTTP.

Step4: Mở rộng key và cert thành tệp pem

Tệp pem là tệp tin chứa định dạng có thể chỉ public certificate hoặc có thể gồm tập public key, private key và root certificate kết hợp với nhau.

Chúng ta thực hiện nối private key và certificate ở trên để tạo thành tệp pem.

cd /etc/letsencrypt/live/mymusic.vn/
cat fullchain.pem privkey.pem | tee mymusic.vn.pem

Step5: Cấu hình HAProxy sử dụng LetsEncrypt SSL

Sau khi đã có tệp pem, chúng ta cấu hình HAProxy để cho phép xử lý luồng kết nối với SSL.

Phần frontend

Chúng ta gán listen port cho https với port default là 443, không sử dụng giao thức sslv3, và chỉ định vị trí tệp pem “mymusic.vn.pem”.

  • Nếu HAProxy sử dụng đồng thời cả HTTP và HTTPS, khi đó cấu hình như sau:
## Frontend section
frontend http-in
        bind *:80
          bind *:443 ssl no-sslv3 crt /etc/letsencrypt/live/mymusic.vn/mymusic.vn.pem
        acl mymusic-acl hdr(host) -i mymusic.vn
        use_backend mymusic if mymusic-acl
  • HAProxy chỉ sử dụng HTTPS

Khi đó chúng ta cần thêm tùy chọn redirect khi truy cập http đến https

Redirect với site mymusic.vn

redirect scheme https if { hdr(Host) -i mymusic.vn } !{ ssl_fc }

Khi đó, cấu hình frontend như sau:

## Frontend section
frontend http-in
        bind *:80
        bind *:443 ssl no-sslv3 crt /etc/letsencrypt/live/mymusic.vn/mymusic.vn.pem
        redirect scheme https if { hdr(Host) -i mymusic.vn } !{ ssl_fc } 
        acl mymusic-acl hdr(host) -i mymusic.vn
        use_backend mymusic if mymusic-acl

Phần Backend

Chúng ta sẽ khai báo thông tin thuật toán load balancing sẽ sử dụng, các thông tin web server , các tùy chọn về log và thiết lập tùy chọn header.

backend mymusic
        balance roundrobin
        server server1 192.168.10.111:8080 weight 1 check
        server server2 192.168.10.112:8080 weight 1 chec
        server server3 192.168.10.113:8080 weight 1 check
        option httplog
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request add-header X-Forwarded-Proto https if { ssl_fc }

Ở đây, chúng ta có một số tùy chọn:

option forwardfor : Được sử dụng để thêm header “X-Forwarded-For”, vì vậy backend có thể nhận được địa chỉ IP thực của client truy cập. Nếu không có tùy chọn này thì backend sẽ chỉ nhận được thông tin IP của HAProxy.

http-request set-header X-Forwarded-Port %[dst_port] : thiết lập một header “X-Forwarded-Port” vì vậy mà backend biết được port nào để sử dụng khi redirect URLs.

http-request add-header X-Forwarded-Proto https if { ssl_fc } : Thêm header X-Forwarded-Proto với thiết lập scheme là https nếu yêu cầu truy cập https. Cái này, cho phép backend xác định được scheme để sử dụng khi gửi URL lúc redirect

Khi đó, nội dung tệp cấu hình HAProxy đầy đủ cho site mymusic.vn sử dụng SSL như sau:

# Global settings
global
        log 127.0.0.1   local0
        pidfile     /var/run/haproxy.pid
        stats socket /var/lib/haproxy/stats
        maxconn 100000
        user haproxy
        group haproxy
        daemon
        quiet
# Proxies settings
## Defaults section
defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        retries 3
        option      redispatch
        maxconn     100000
        retries                 3
        timeout http-request    5s
        timeout queue           30s
        timeout connect         30s
        timeout client          30s
        timeout server          30s
        timeout http-keep-alive 30s
        timeout check           30s
## Frontend section
frontend http-in
        bind *:80
        bind *:443 ssl no-sslv3 crt /etc/letsencrypt/live/mymusic.vn/mymusic.vn.pem
        redirect scheme https if { hdr(Host) -i mymusic.vn } !{ ssl_fc } 
        acl mymusic-acl hdr(host) -i mymusic.vn
        use_backend mymusic if mymusic-acl
backend mymusic
        balance roundrobin
        server server1 192.168.10.111:8080 weight 1 check
        server server2 192.168.10.112:8080 weight 1 chec
        server server3 192.168.10.113:8080 weight 1 check
        option httplog
        option forwardfor
        http-request set-header X-Forwarded-Port %[dst_port]
        http-request add-header X-Forwarded-Proto https if { ssl_fc }
## Statistics settings
listen statistics
        bind *:1986
        stats enable
        stats admin if TRUE
        stats hide-version
        stats realm Haproxy\ Statistics
        stats uri /stats
        stats refresh 30s
        stats auth keepwalking86:ILoveVietnam$

Tham khảo thêm về LetsEncrypt

 

Advertisements

HA redis sentinel sử dụng HAProxy

Posted: Tháng Một 16, 2019 in HAProxy, Redis
Thẻ:, , ,

Trong phần trước “Redis cluster với sentinel“, chúng ta đã cài đặt và cấu hình redis cluster mà sử dụng sentinel. Trong bài viết này chúng ta sẽ sử dụng HAProxy để cấu hình chạy HA cho redis sentinel.

redis ha with haproxy

Mô hình thực hiện gồm:

01 server HAProxy có địa chỉ: 192.168.10.186

03 redis server cấu hình cluster sử dụng sentinel, với địa chỉ IP là:

Redis master: 192.168.10.111

Redis slaves: 192.168.10.112 và 192.168.10.113

Thừa nhận đã cài đặt và cấu hình redis sentinel cho 03 redis server. Có thể tham khảo bài viết cài đặt  “Redis cluster với sentinel”  hoặc sử dụng bash script sau: “Script for Install and configure Redis cluster + Sentinel

Cấu hình HAProxy

Nếu HAProxy chỉ dành riêng cho phục vụ cho redis (ở đây chỉ dùng mode tcp, không sử dụng mode http), chúng ta có thêm các phần sau trong phần proxy của tệp cấu hình haproxy.cfg

defaults redis
    mode tcp
    timeout connect 10s
    timeout server 30s
    timeout client 30s
frontend redis-in
    bind *:6380
    default_backend redis-backend
backend redis-backend
    option tcp-check
    tcp-check connect
    tcp-check send PING\r\n
    tcp-check expect string +PONG
    tcp-check send info\ replication\r\n
    tcp-check expect string role:master
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server redis1 192.168.10.111:6379 check inter 1s
    server redis2 192.168.10.112:6379 check inter 1s
    server redis3 192.168.10.113:6379 check inter 1s

Ở đây, chúng ta sẽ cấu hình phần proxy của HAProxy thành 3 phần: defaults, frontend và backend

– phần defaults: chúng ta sử dụng để thiết lập các thông số chung cho các phần còn lại. Trong đó, khai báo mode mà haproxy sẽ thực hiện là “tcp”. Các giá trị timeout ở đây là thiết lập time giữa nội bộ app, haproxy và redis, vì vậy tùy thuộc vào hiệu năng của server mà ta thiết lập timeout cho phù hợp.

– phần frontend: thiết lập thông tin IP và port 3680 sẽ gán cho HAProxy mà chấp nhận yêu cầu kết nối từ app. Cấu hình cho HAProxy sẽ pass mặc định đến phần backend redis

– phần backend: thiết lập nhóm redis server, sử dụng tcp-check (thực hiện tuần tự  tcp send/expect) để kiểm tra hoạt động và role của redis. Chỉ có redis master có quyền write, vì vậy chúng ta cấu hình chỉ thực hiện route đến redis master khi app yêu cầu kết nối. Tất nhiên chúng ta có thể cấu hình check hệ thống redis mà bỏ qua tcp-check giá trị master, khi đó nếu không có giá trị redis master nó vẫn có thể read được từ redis slave (nhưng sẽ xảy ra lỗi khi có yêu cầu write). Ví dụ:

backend redis-backend
    option tcp-check
    tcp-check connect
    tcp-check send PING\r\n
    tcp-check expect string +PONG
    server redis1 192.168.10.111:6379 check inter 1s
    server redis2 192.168.10.112:6379 check inter 1s
    server redis3 192.168.10.113:6379 check inter 1s

Chúng ta có thể thiết lập cấu hình 3 phần trên (default, frontend, backend) vào một phần “listen” trong HAProxy. Thiết lập cấu hình như sau:

listen redis
    bind *:6380
    mode tcp
    timeout connect  10s
    timeout server  30s
    timeout client  30s
    option tcp-check
    tcp-check send PING\r\n
    tcp-check expect string +PONG
    tcp-check send info\ replication\r\n
    tcp-check expect string role:master
    tcp-check send QUIT\r\n
    tcp-check expect string +OK
    server redis1 192.168.10.111:6379 check inter 1s
    server redis2 192.168.10.112:6379 check inter 1s
    server redis3 192.168.10.113:6379 check inter 1s

Khi đó chúng ta có tệp /etc/haproxy/haproxy.cfg đầy đủ như sau:

# Global settings
global
        pidfile     /var/run/haproxy.pid
        maxconn 10000
        user haproxy
        group haproxy
        daemon
        quite
        stats socket /var/lib/haproxy/stats
        log 127.0.0.1   local0
## Statistics settings
listen statistics
        bind *:1986
        stats enable
        stats hide-version
        stats realm Haproxy\ Statistics
        stats uri /stats
        stats refresh 30s
        stats auth keepwalking86:ILoveVietnam$
## Redis HA
listen redis
        bind *:6380
        mode tcp
        timeout connect  10s
        timeout server  30s
        timeout client  30s
        option tcp-check
        tcp-check send PING\r\n
        tcp-check expect string +PONG
        tcp-check send info\ replication\r\n
        tcp-check expect string role:master
        tcp-check send QUIT\r\n
        tcp-check expect string +OK
        server redis1 192.168.10.111:6379
        server redis2 192.168.10.112:6379
        server redis3 192.168.10.113:6379

Thực hiện start haproxy và tạo vòng lặp kiểm tra thông tin replication khi sử dụng HAProxy

for i in {1..3}; do redis-cli -p 6380 info replication; done

check redis ha with haproxy

Ta thấy mỗi lần truy vấn đến 192.168.10.186 với port 3680, giá trị trả về cho role hiện tại là master và có 02 slave được connect.

Redis cluster với sentinel

Posted: Tháng Một 11, 2019 in Redis
Thẻ:, ,

Có nhiều công cụ hỗ trợ cho tạo redis cluster như: Sentinel, Dynomite, Reborn, … Trong bài viết này, chúng ta sẽ sử dụng sentinel (một tính năng có sẵn trong redis) để thực hiện giám sát trạng thái hoạt động của redis cluster và thực hiện failover tự động. Đảm bảo rằng hệ thống redis sẽ vẫn hoạt động khi có một trong số các redis server trong cụm cluster down.redis cluster with sentinel

Mô hình thực hiện gồm 03 redis server. Trong đó:

  • 01 redis master: 192.168.10.111

  • 02 redis slave: 192.168.10.112 và 192.168.10.186

Một số công việc cần thực hiện: cài đặt redis, cấu hình redis sentinel.

1. Cài đặt và cấu hình Redis từ source

Step1: Installing redis

cd /opt
curl -O http://download.redis.io/redis-stable.tar.gz
tar xzvf redis-stable.tar.gz cd redis-stable
make
cp src/redis-cli /usr/bin/redis-cli
cp src/redis-server /usr/bin/redis-server

Step2: Configure redis

  • Tạo thư mục chứa cấu hình redis

sudo mkdir /etc/redis

  • Copy tệp mẫu redis.conf

sudo cp /opt/redis-stable/redis.conf /etc/redis

  • Sửa đổi tệp /etc/redis/redis.conf với thay đổi sau

Sử dụng supervised với hệ thống systemd. Chỉ định cố định thư mục chứa dump redis

sed -i 's/supervised no/supervised systemd/' /etc/redis/redis.conf
sed -i 's/dir .\//dir \/var\/lib\/redis/' /etc/redis/redis.conf

Sử dụng thông tin cho bash:

mkdir /etc/redis
cp /opt/redis-stable/redis.conf /etc/redis
sed -i 's/supervised no/supervised systemd/' /etc/redis/redis.conf
sed -i 's/dir .\//dir \/var\/lib\/redis/' /etc/redis/redis.conf
sed -i '/bind 127.0.0.1/c\bind 0.0.0.0' /etc/redis/redis.conf

Step3: Thiết lập redis chạy với systemd

Tạo tệp redis.service với thông tin sau để chạy với hệ thống systemd

cat >/etc/systemd/system/redis.service <<EOF
[Unit]
Description=Redis In-Memory Data Store
After=network.target

[Service]
User=redis
Group=redis
ExecStart=/usr/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/bin/redis-cli shutdown
Restart=always

[Install]
WantedBy=multi-user.target
EOF

Tạo redis user và group để chạy redis

adduser --system --no-create-home redis
mkdir /var/lib/redis
chown redis:redis /var/lib/redis
chmod 770 /var/lib/redis

Start và enable redis service

systemctl start redis.service
systemctl enable redis.service

2. Redis cluster with redis-sentinel

Yêu cầu: Cài đặt redis trên 03 server. Mở firewall cho các port 6379 và 26389

Step1: Cấu hình replication

Chúng ta thiết lập cấu hình nhân bản redis trên các máy redis slave

Thực hiện thêm dòng sau vào tệp cấu hình redis.conf trên các máy redis slave:

echo 'slaveof 192.168.10.111 6379' >>/etc/redis/redis.conf
systemctl restart redis.service

Ở đây “slaveof” được sử dụng để thông báo cho redis cluster là server nào làm master và server nào được sử dụng làm slave. Khai báo thông tin master server là 192.168.10.111

Check thông tin replication trên một trong các redis server

redis-cli info replication

redis-cli info replication

Chúng ta thấy vai trò hiện tại của server02 là slave và địa chỉ IP của master hiện tại là 192.168.10.111.

  • Tạo cập key để kiểm tra
redis-cli
127.0.0.1:6379>set key value
127.0.0.1:6379> SET key1 "Hello world!"
OK
127.0.0.1:6379> SET key "Value"
OK

Trên các máy slave thực hiện get key

[root@server02 redis-stable]# redis-cli 
127.0.0.1:6379> GET *
(nil)
127.0.0.1:6379> KEYS *
1) "key"
2) "key1"
127.0.0.1:6379> get key1
"Hello world!"
127.0.0.1:6379>

Step2: Configure redis sentinel

Trên 03 redis server, tạo tệp sentinel.conf trong thư mục /etc/redis/ với nội dung sau:

cat >/etc/redis/sentinel.conf<<EOF
daemonize yes
pidfile '/var/run/redis/redis-sentinel.pid'
logfile '/var/log/redis/redis-sentinel.log'
bind 0.0.0.0
port 26379
sentinel monitor mymaster 192.168.10.111 6379 2
sentinel down-after-milliseconds mymaster 2000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
EOF

Ở đây, chúng ta cấu hình redis-sentinel trên mỗi redis server với cùng các thông tin sau:

  • Thiết lập redis cluster với tên “mymaster”. Chỉ định server 192.168.10.111 làm master hiện tại.
  • down-after-milliseconds: thiết lập khoảng thời gian mili giây mà master server không phản hồi, khi đó nó được xác định là down.
  • failover-timeout: khoảng thời gian mà failover lần tiếp theo sau khi thực hiện failover trước đó bị lỗi.
  • parallel-syncs: thiết lập số slave mà có thể được cấu hình lại để sử dụng master mới sau khi failover tại một thời điểm. Ở đây, chúng ta thiết lập tại một thời điểm chỉ một slave được sử dụng.

Step3: Start redis-sentinel

Chúng ta có thể tạo systemd cho redis-sentinel. Thực hiện tạo tệp như sau:

cat >/etc/systemd/system/redis-sentinel.service<<EOF
[Unit]
Description=Redis Sentinel
After=network.target
[Service]
ExecStart=/usr/bin/redis-server /etc/redis/sentinel.conf --sentinel --daemonize no
Restart=always
[Install]
WantedBy=multi-user.target
EOF

Thực hiện start và enable redis-sentinel với systemd

systemctl start redis-sentinel
systemctl enable redis-sentinel

Step4: Check redis-sentinel

  • Kiểm tra log redis-sentinel

redis log

Khi đó chúng ta thấy redis master hiện tại là 192.168.10.111 và các slave là 192.168.10.112 và 192.168.10.186. Nếu kiểm tra tệp cấu hình sentinel.conf lúc này, trên mỗi redis server tệp sentinel.conf sẽ tự động cập nhật thông tin cấu hình cuối và nội dung sẽ gồm thông tin các redis server còn lại mà sentinel sẽ giám sát và current-epoch 0.

  • Kiểm tra redis nào hiện tại đang là master

Chúng ta có thể thực hiện kiểm tra với dòng lệnh sau trên mất kỳ redis server.

localhost:26379> sentinel get-master-addr-by-name mymaster
1) "192.168.10.111"
2) "6379"
  • Stop redis master và check log trên redis slave
systemctl stop redis.service
tail -f /var/log/redis/redis-sentinel.log

Chúng ta xem log trên từng redis slave sau:

Trên Server 192.168.10.112

redis sentinel log slave01

Trên Server 192.168.10.186

redis sentinel log slave02

Quan sát log, chúng ta thấy tuần tự:

sentinel giám sát thấy master down được đánh dấu lần đầu là +sdown (subjectively down state). Sau khi các redis server, ở đây sử dụng sentinel để giám sát xác nhận redis master đã down thật, khi đó trạng thái được đánh dấu là +odown ( objectively down state). Tiếp theo, sentinel vote (+vote) một sentinel khác mà thực hiện cố gắng failover lần đầu. Chúng ta thấy có quá trình chuyển đổi failover giữa các redis server. Và slave 192.168.10.186 được chọn làm master.

Sử dụng lệnh để check redis server nào hiện tại đang có vai trò là master:

[root@server02 redis-stable]# redis-cli -p 26379
127.0.0.1:26379> SENTINEL get-master-addr-by-name mymaster
1) "192.168.10.186"
2) "6379"

Tham khảo thêm về redis sentinel tại: https://redis.io/topics/sentinel.

Cân bằng tải trong HAProxy

Posted: Tháng Một 11, 2019 in HAProxy, Load Balancing
Thẻ:,

Trong bài viết này, chúng ta sẽ giới thiệu về một số thuật toán cân bằng tải phổ biến trong HAProxy, và cách thức cố định session với sticky session

1. Các thuật toán cân bằng tải

HAProxy hỗ trợ các thuật toán sau: roundrobin, static-rr, leastconn, first, source, uri, url_parm, hdr, rdp-cookie. Ở đây, chúng ta sẽ giới thiệu một số thuật toán thường được sử dụng.

1.1 Roundrobin

Là thuật toán luân chuẩn theo vòng. Các server sẽ được sử dụng lần lượt theo vòng, phụ thuộc vào giá trị trọng số của nó. roundrobin là thuật toán được sử dụng mặc định load balancing khi không có thuật toán nào được chỉ định.

backend web-backend
    option httplog
    option forwardfor
    server web1 192.168.1.110:8080 check
    server web2 192.168.1.111:8080 check
    server web3 192.168.1.112:8080 check

Dựa vào khả năng xử lý của từng server, chúng ta sẽ thay đổi giá trị trọng số của từng server để phân phối tải đến các server khác nhau. Sử dụng tham số “weight” để thay đổi trọng số. Tỷ lệ tải của các server sẽ tỷ lệ thuận trọng số của chúng so với tổng trọng số của tất cả server. Vì vậy mà server nào có trọng số càng cao, thì yêu cầu tải lên nó cũng sẽ cao. Ví dụ cân bằng tải khi thiết lập weight

backend web-backend
    balance  roundrobin
    option httplog
    option forwardfor
    server web1 192.168.1.110:8080 check weight 2
    server web2 192.168.1.111:8080 check weight 2
    server web3 192.168.1.112:8080 check weight 1

Khi đó mỗi 05 request, 2 request đầu tiên sẽ được chuyển tiếp lần lượt đến server web1 và web2, 3 request sau sẽ thực hiện chuyển tiếp lần lượt đến server web1, web2 và web3.

Mặc định weight có giá trị là 1, giá trị tối đa của weight là 256. Nếu server giá trị weight là 0, khi đó nó sẽ không tham gia vào cụm server trong load balancing.

1.2 leastconn

Đây là thuật toán dựa trên tính toán số lượng kết nối để thực hiện cân bằng tải cho server, nó sẽ tự động lựa chọn server với số lượng kết nối đang hoạt động là nhỏ nhất, để lượng connection giữa các server là tương đương nhau.

Thuật toán này khắc phục được tình trạng một số server có lượng connection rất lớn (do duy trì trạng thái connection), trong khi một số server khác thì lượng tải hay connection thấp.

backend web-backend
      leastconn
    option httplog
    option forwardfor
    server web1 192.168.1.110:8080 check
    server web2 192.168.1.111:8080 check
    server web3 192.168.1.112:8080 check

Thuật toán này hoạt động tốt khi mà hiệu suất và khả năng tải của các server là tương đương nhau.

2. Sticky session

Trong môi trường web, nhiều khi chúng ta cần cố định session của user, như để duy trì trạng thái login. Khi đó, chúng ta cần cố định session trên một server. HAProxy hỗ trợ một số thuật toán Load Balancing duy trì trạng thái kết nối mà cho phép cố định session như hdr, rdp-cookie, source, uri hoặc url_param. Chẳng hạn như:

backend cms
    balance source
    hash-type consistent
    server web1 192.168.10.110:8080 check
    server web2 192.168.10.111:8080 check
    server web3 192.168.10.112:8080 check

Nếu chúng ta muốn cố định session mà vẫn sử dụng các thuật toán load balancing như roundrobin, leastconn, hoặc static-rr, khi đó chúng ta sử dụng “Sticky Session”. Sticky session cho phép cố định session của users mà sử dụng cookie, và HAProxy sẽ điều phối để luôn request từ một user đến cùng một server.

Để sử dụng sticky session trong HAProxy, chúng ta thêm tùy chọn “cookie cookie_name insert/prefix” vào trong phần backend.

2.1 Session cookie được thiết lập bởi HAProxy

Khi đó sử dụng ‘cookie cookie_name insert <options>’. “cookie_name” là giá trị mà HAProxy sẽ chèn vào (insert). Khi client quay lại (tức là cũng là client này và request tiếp theo), HAProxy sẽ biết được server nào để chọn cho client này. Ví dụ:

    cookie  WEB insert
    server web1 192.168.1.110:8080 cookie web1 check
    server web2 192.168.1.111:8080 cookie web2 check
    server web3 192.168.1.112:8080 cookie web3 check

Chúng ta check thử xem HAProxy sẽ response giá trị cookie như thế nào khi sử dụng insert

session cookie setup by haproxy

Khi đó chúng ta thấy giá trị cookie mà HAProxy phản hồi cho client là “WEB=web1”

2.2 Sử dụng session cookie của ứng dụng

Khi đó sử dụng “cookie SESSION_ID prefix <option>”. “SESSION_ID” là tên cookie của application như PHPSESSID, JSESSID, laravel_session, … Khi đó, HAProxy sẽ sử dụng session id cookie mà được tạo bởi application để duy trì kết nối giữa một client và một server backend. Cách thức hoạt động, đó là HAProxy sẽ mở rộng cookie với một SESSION ID cookie hoặc cookie đang tồn tại, mà có đặt trước nó là giá trị cookie của server và dấu ~.

    cookie laravel_session prefix
    server web1 192.168.1.110:8080 cookie web1 check
    server web2 192.168.1.111:8080 cookie web2 check
    server web2 192.168.1.112:8080 cookie web3 check

Chúng ta check thử xem header của haproxy server và response của nó. Sử dụng: curl -I http://haproxy-ip-address:80/

session cookie setup by app

Khi đó chúng ta thấy HAProxy server phản hồi với header như hình, với giá trị cookie được thay đổi là: “laravel_session=web1~eyJpdiI6InJuOUN…1Lc0E9PSIsInZhbH” với giá trị prefix là web1~ trước giá trị cookie của application mà HAProxy đã thêm vào.

Hạn chế của sticky session: Với sticky session, việc các request từ một user sẽ chỉ cố định vào một server. Vì vậy mà sẽ không đảm bảo được tính điều phối nhiều request từ một users đến nhiều server. Để khắc phục điểm hạn chế này, thì hiện nay có một số phần mềm như redis, memcached, … cho phép lưu session của user, còn việc điều phối các request của user thì vẫn thực hiện bình thường đến các server.

Vì vậy, trong bài viết tiếp theo chúng ta đi vào phần cấu hình và sử dụng redis.

Tham khảo thêm các bài viết về HAProxy tại: https://www.haproxy.com/blog/

Triển khai stack đến Docker Swarm

Posted: Tháng Mười Hai 8, 2018 in Container, Docker
Thẻ:,

Ở bài trước chúng ta giới thiệu tổng quan về docker swarm mà nói về các tính năng, cũng như chạy một service đơn giản trong docker swarm. Trong phần này chúng ta đi vào phần Deploy một stack (gồm các service/container liên kết với nhau) trong docker swarm mà được gọi là “Docker Stack

Reference: Deploy a stack to a swarm

1. Về Docker Stack

  • Docker Swarm đơn thuần điều phối các container chạy trên nhiều các docker host. Nhưng giữa các container đấy không có sự phụ thuộc lẫn nhau. Chính vì vậy, để liên kết các container ấy thành một ứng dụng chúng ta cần phải thao tác thủ công cho phép kết nối giữa chúng.
  • Docker Compose là công cụ mà cho phép liên kết các container với nhau để chạy ứng dụng. Nhưng các container đấy lại chạy trên cùng một docker host. Vì vậy, nó sẽ có mặt hạn chế khi muốn high availability và horizontal scaling.
  • Với Docker Stack, nó cho phép chúng ta kết nối 2 công nghệ trên với nhau. Nó sử dụng tệp tin docker compose (mặc định docker-compose.yml) để định nghĩa, liên kết các container với nhau và chạy trong một cluster docker host (docker swarm). Hình dưới minh họa về Docker Stack

docker stack - docker swarm

2. Triển khai một stack

Chúng ta thực hiện deploy “LEMP” stack đến docker swarm với Docker Stack.

LEMP là stack gồm: Nginx, MySQL/MariaDB và PHP trên Linux

Thực hiện một số công việc sau:

  • Thiết lập docker swarm
  • Định nghĩa các services cho stack
  • Triển khai docker stack

2.1 Thiết lập docker swarm

Chúng ta tham khảo bài viết trước để thiết lập docker swarm

Xác nhận docker swarm đã tạo xong, với một số node

show docker nodes

2.2 Định nghĩa các services cho stack

Chúng ta thực hiện tạo tệp tin docker-compose.yml để định nghĩa các services cho stack, để cho phép triển khai trên docker swarm.

Tệp docker-compose.yml trong docker swarm không hỗ trợ  tùy chọn “build”, vì vậy mà chúng ta sẽ khai báo các image name trực tiếp ở mỗi service. Để sử dụng image name, chúng ta cần thực hiện build image trước với Dockerfile và push lên registry (private hoặc public).

Step1: Dựng private docker registry

Trong bài viết này tôi sử dụng private registry chứa các image, vì vậy chúng ta cần dựng private docker registry. Tham khảo qua bài viết dựng private docker registry

Nếu sử dụng một số public registry (như hub.docker.com), chúng ta có thể bỏ qua bước này.

Step2: Build các docker image

– Build php7-fpm image

Tạo tệp tin Dockerfile để build image cho php7-fpm với nội dung sau

FROM php:7.2-fpm

RUN apt-get update
RUN apt-get install libxml2-dev unzip libmcrypt-dev zlib1g-dev -y
#Using docker-php-ext-install to install php modules
RUN docker-php-ext-install pdo_mysql mysqli mbstring xml opcache zip

EXPOSE 9000
CMD ["php-fpm", "-F"]

Chúng ta sử dụng image gốc là php:7.2-fpm và thực hiện cài một số thư viện và một số module cho php

Thực hiện build image với tên hub.example.local/php7-fpm

 docker build -t hub.example.local/php7-fpm .

build image php7-fpm

– Build nginx image

Nội dung tệp tệp Dockerfile có nội dung như sau:

FROM nginx:latest

MAINTAINER KeepWalking86

ADD ./default.conf /etc/nginx/conf.d/
RUN mkdir -p /var/www/example/
RUN chown -R nginx:nginx /var/www/example

EXPOSE 80

Trong đó, chúng ta build lại từ image gốc nginx:latest, và thực hiện một số tùy như:

Add tệp ‘default.conf’ để thay nội dung tệp cấu hình chính mặc định của nginx. Nội dung tệp như sau:

server {
        listen 80;
        listen [::]:80 ipv6only=on;

    # Log files for Debugging
        access_log /var/log/nginx/laravel-access.log;
        error_log /var/log/nginx/laravel-error.log;

    # Webroot Directory for Laravel project
        root /var/www/example/public;
        index index.php index.html index.htm;

        # Your Domain Name
        server_name example.local;
        location ~* \.(jpg|jpeg|gif|css|png|js|ico|txt|srt|swf|zip|rar|html|htm|pdf)$ {
                access_log        off;
                log_not_found     off;
                expires 30d; # caching, expire after 30 days
        }

        location / {
                try_files $uri $uri/ /index.php?$query_string;
        }

    # PHP-FPM Configuration Nginx
        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                #fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
                fastcgi_pass web-phpfpm:9000;
        }
}

Ở đây, tôi khai báo một host với tên site “example.local”; vị trí thư mục root của website là “/var/www/example/public”. Website chạy php và sử dụng bộ dịch là FastCGI. Trong đó sử dụng chỉ thị “fastcgi_pass” để thiết lập kết nối đến server FastCGI, mà trong trường  hợp này là kết nối đến server web-phpfpm:9000. Ở đây web-phpfpm là tên service mà ta sẽ định nghĩa ở trong tệp docker-compose.yml trong Step tiếp theo.

Thực hiện build image với tên hub.example.local/nginx

docker build -t hub.example.local/nginx .

build nginx nginx

Step3: Tạo tệp tin docker-compose.yml

Chúng ta định nghĩa 03 service: db, web-phpfpm và web-nginx.

Một số tùy chọn cho mỗi service ở tệp docker compose như sau:

– image: xác định tên image để tạo container

– deploy: tùy chọn cấu hình cho việc triển khai

+ replicas: Số lượng bản sao container

+ constraints: [node.role == worker/manager] → Xác định container sẽ được triển khai ở host nào trong docker swarm. Ở đây, chúng ta xác định vị trí docker host theo vai trò manager/worker trong docker swarm.

– volumes: để mount source code giữa docker host và container. Mục đích của tôi là muốn mount source code tại thư mục ./src trên node manager vào web-phpfpm và web-nginx.

– Ở service “db”, tôi khai báo một số biến ENV để tạo thông tin database như: user, password, … Sau khi tạo và chạy stack, chúng ta có thể kiểm tra kết nối web và db

Tham khảo thêm về cấu hình tệp docker-compose cho docker stack từ:

https://docs.docker.com/compose/compose-file/#deploy

Nội dung tệp docker-compose.yml như sau:

version: "3"

services:
    db:
      image: mariadb:latest
      deploy:
        replicas: 2
        placement:
          constraints: [node.role == worker]
      networks:
        - mynet
      environment:
        MYSQL_ROOT_PASSWORD: P@ssw0rd
        MYSQL_DATABASE: laravel
        MYSQL_USER: keepwalking
        MYSQL_PASSWORD: P@ssw0rd

    web-phpfpm:
      image: hub.example.local/php7-fpm
      deploy:
        replicas: 2
        placement:
          constraints: [node.role == manager]
      volumes:
        - ./src:/var/www/example
      networks:
        - mynet

    web-nginx:
      image: hub.example.local/nginx
      deploy:
        replicas: 2
        placement:
          constraints: [node.role == manager]
      depends_on:
        - db
        - web-phpfpm
      ports:
        - 8080:80
      networks:
        - mynet
      volumes:
        - ./src:/var/www/example

networks:
  mynet:

2.3 Thực hiện deploy docker stack

Chúng ta thực hiện lệnh sau để triển khai stack với tên “lemp”

​docker stack deploy --compose-file docker-compose.yml lemp

Thực hiện một số lệnh sau để kiểm tra stack

Check docker stack lemp

Như vậy, chúng ta đã tạo lemp stack với 03 services. Mỗi service đều nhân bản với 02 replicas. Trong đó service: web-nginx và web-phpfpm được phân bố ở node vnsys (là node manager); service db được phân bố vào node02 và node03 là các node worker.