kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Docker Compose を使って HAProxy に入門した

今日から GW だし,今まで試したことの無かった HAProxy に入門してみた.今回は複数台ある nginx に対してリクエストを分散させるだけで,初歩的な動作確認になっている.

環境

今さら Vagrant 環境を構築するのも面倒で,今回は Docker を使おうと思ったのと,せっかくなら Alpine Linux をベースにしたコンテナイメージにしてみようと思って haproxy:1.6.4-alpinenginx:stable-alpine を選んだ.

kakakakakku.hatenablog.com

kakakakakku/docker-haproxy-nginx

今回試したコードを GitHub に push しておいたー!

github.com

ドキュメント

docker-compose.yml

Docker Compose で4コンテナを起動して,HAProxy コンテナは :8080 で叩けるようにしている.各 Dockerfile は GitHub 参照でー.

version: '2'

services:
  haproxy:
    build: ./haproxy/.
    ports:
     - '8080:8080'

  nginx1:
    build: ./nginx1/.

  nginx2:
    build: ./nginx2/.

  nginx3:
    build: ./nginx3/.

haproxy.cfg

特にチューニングなどはしてなく,シンプルな設定に留めている.Docker Compose で起動している nginx コンテナは nginx1 / nginx2 / nginx3 で参照できて,計3台に均等に負荷分散するようになっている.

global
  quiet

defaults
  mode http
  maxconn 5000

  timeout connect 5s
  timeout client  20s
  timeout server  20s

frontend public
  bind *:8080
  default_backend apps

backend apps
  server nginx1 nginx1:80 check
  server nginx2 nginx2:80 check
  server nginx3 nginx3:80 check

リクエストを投げてみる

20回リクエストを投げるだけのサンプルスクリプト requests.sh を用意してるので,実行してみると,ちゃんと負荷分散されていることがわかる.

$ sh ./requests.sh
From nginx1
From nginx2
From nginx3
From nginx1
From nginx2
From nginx3
From nginx1
From nginx2
From nginx3
From nginx1
From nginx2
From nginx3
From nginx1
From nginx2
From nginx3
From nginx1
From nginx2
From nginx3
From nginx1
From nginx2

nginx ごとに重みを変えてみる

weight オプションを付けることで負荷分散の重みを指定することができる.

backend apps
  server nginx1 nginx1:80 check weight 5
  server nginx2 nginx2:80 check weight 2
  server nginx3 nginx3:80 check weight 1

ただし,この weight は % ではなくあくまで相対的な回数の差と表現できる.ようするに上記の例だと,計8リクエストを受けると「5リクエスト / 2リクエスト / 1リクエスト」に分散される.

The "weight" parameter is used to adjust the server's weight relative to other servers. All servers will receive a load proportional to their weight relative to the sum of all weights, so the higher the weight, the higher the load. The default weight is 1, and the maximal value is 256. A value of 0 means the server will not participate in load-balancing but will still accept persistent connections. If this parameter is used to distribute the load according to server's capacity, it is recommended to start with values which can both grow and shrink, for instance between 10 and 100 to leave enough room above and below for later adjustments.

実際に試してみると確かにそうなっているし,さらに「1 / 1 / 2 / 1 / 1 / 3 / 1 / 2」という順番も維持されていることがわかる.

$ sh ./requests.sh
From nginx1
From nginx1
From nginx2
From nginx1
From nginx1
From nginx3
From nginx1
From nginx2
From nginx1
From nginx1
From nginx2
From nginx1
From nginx1
From nginx3
From nginx1
From nginx2
From nginx1
From nginx1
From nginx2
From nginx1

nginx を1台落としてみる

weight の設定を均等な負荷分散に戻した状態で,次は nginx3 を意図的に落としてからもう1度リクエストを投げてみる.

$ docker ps
$ docker kill 1fc13b25040a

ちゃんと nginx1 と nginx2 に負荷分散できていることがわかる.

$ sh ./requests.sh
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2
From nginx1
From nginx2

ちなみにこの check オプションは重要で,定期的にヘルスチェックをして,もしコンテナが落ちたら自動的に負荷分散の対象から外してくれる.checkを付けないとリクエストを伝搬するときにコンテナが落ちてることに気付くため,スループットが落ちてしまう.

backend apps
  server nginx1 nginx1:80 check
  server nginx2 nginx2:80 check
  server nginx3 nginx3:80 check

まとめ

HAProxy を使うと簡単に ELB のようなロードバランサが立てられて便利だった.次のステップとしては haproxy.cfg の設定を細かくチューニングしてみたり,MySQL Slave の負荷分散を試してみたり,HAProxy が SPOF にならないように冗長化してみたりを試していこうと思っている.

Alpine Linux を使ったメモ

nginx コンテナに直接 docker exec で入って,nginx の動作確認をするときに curl すら入ってないことに気付いて,apk でインストールした.

/ # curl http://localhost
/bin/sh: curl: not found

/ # apk --update add curl
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
(1/4) Installing openssl (1.0.2g-r0)
(2/4) Installing ca-certificates (20160104-r2)
(3/4) Installing libssh2 (1.6.0-r1)
(4/4) Installing curl (7.47.0-r0)
Executing busybox-1.24.1-r7.trigger
Executing ca-certificates-20160104-r2.trigger
OK: 20 MiB in 26 packages

/ # curl http://localhost
From nginx2

コンテナイメージのサイズも本当に小さくて流石 Alpine Linux だ!という感じだった.

$ docker images
REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
dockerhaproxynginx_haproxy   latest              eacfa54ad6eb        44 minutes ago      10.65 MB
dockerhaproxynginx_nginx1    latest              a66698b79bc8        44 minutes ago      15.47 MB
dockerhaproxynginx_nginx2    latest              d788e5bc105a        44 minutes ago      15.47 MB
dockerhaproxynginx_nginx3    latest              96872d46a7d8        44 minutes ago      15.47 MB