Dockerエクスペリエンス

序文の代わりに













今日、私は夢を見ました。まるで数匹の大きさに刺されたかのように

キロバイト、何らかの種類のソケットで立ち往生し、コンテナーで起動します。

オーバーレイネットワークに割り当てられ、許可されたトランスポート

他のコンテナでのサービスのテスト...

まだdocker rmを実行していません




少し前まで、私は非常にクールなチームのメンバーになれたことは幸運でした

Centos-admin.ruで 、私のような人々と出会いました。新しいテクノロジー、愛好家、そしてただ素晴らしい人たちに情熱を持っている志を同じくする人々。 そのため、すでに2日目に同僚と私は「ドッキング可能なものをすべてドッキングする」必要がある1つのプロジェクトに取り組まれました。サービスの高可用性を確保することは非常に重要でした。



それまでは、私は普通のLinuxベースのルーム管理者でした。すぐに、稼働時間、apt-get-installedパッケージ、構成ルール、再起動サービス、タイルログを測定しました。 一般的に、彼は特に優れた実用的なスキルを持っていませんでした;彼はペット対の概念について全く何も知りませんでした。 牛は 、Dockerに実質的になじみがなく、一般的に、彼がどのような機会を隠しているのかについてほとんど知りませんでした。 そして自動化ツールから、サーバーとさまざまなbashスクリプトを構成するためにansibleのみを使用しました。







このプロジェクトでの作業中に得た経験に基づいて、少し共有したいと思います。







Docker化されたクラスターが解決する必要があるタスク:



-動的インフラストラクチャ。

-変更の迅速な実装。

-アプリケーションの展開の簡素化。



使用されたツール:



-ドッカー

-Docker swarm(エージェント+管理)

-領事

-登録者

-領事テンプレート

-Docker作成

-手



ツールの説明:





Docker













Habrを含むDockerに関する記事はすでにたくさんありました。 それが何であるかを詳細に説明すべきではないと思います。

すべての人の生活を簡素化するツール。 開発者、テスター、システム管理者、アーキテクトに。

Dockerを使用すると、ほぼすべてのアプリケーションをほぼすべてのプラットフォームで作成、実行、展開できます。

Dockerはgitと比較できますが、コードを操作するコンテキストではなく、アプリケーション全体を操作するコンテキストで比較できます。



ここでは、この素晴らしい製品の喜びについて多くを話すことができます。



Docker swarm













Swarmは、すべてのホスト(ノード)を1つのクラスターに論理的に結合する機能を提供します。

これは、どのノードでこのコンテナまたはそのコンテナを実行するかを考える必要がないように機能します。 Swarmは私たちのためにそれを行います。 アプリケーションを「どこか」で起動したいだけです。

Swarmの操作-コンテナーのプールを操作します。 SwarmはDocker APIを使用してコンテナを操作します。



通常、コマンドラインで作業する場合、変数を指定すると便利です

export DOCKER_HOST=tcp://<my_swarm_ip>:3375
      
      





そして、通常どおりdockerコマンドを使用しますが、すでにローカルノードではなく、クラスター全体で機能しています。



--labelオプションに注意してください。 これを使用して、ノードのノートを指定できます。 たとえば、SSDディスクを搭載したマシンがあり、PosrgreSQLを使用してコンテナを起動する必要がある場合、クラスター内の「どこか」ではなく、高速ディスクがインストールされているノード上にあります。



ノードデーモンにラベルを割り当てます。

 docker daemon --label com.example.storage="ssd"
      
      





指定されたラベルのフィルターでPostgreSQLを開始します。

 docker run -d -e constraint:com.example.storage="ssd" postgres
      
      







フィルターの詳細



Swarmクラスタのstartegyなどのパラメータも検討する価値があります。 このパラメーターを使用すると、クラスターのノード間で負荷をより効率的に分散できます。

3つの戦略パラメーターをノードに割り当てることができます



-スプレッド

他の戦略パラメーターが指定されていない場合、デフォルトで使用されます。 この場合、このノードで実行されているコンテナが他のノードよりも少ない場合、swarmは新しいコンテナを起動します。 このパラメーターは、コンテナーの状態を考慮しません。 すべて停止することもできますが、このノードは新しいコンテナを開始するために選択されません。



-ビンパック

反対に、このパラメーターを使用すると、swarmは、眼球へのコンテナーで各ノードを詰まろうとします。 停止したコンテナもここで考慮されます。



-ランダム

名前はそれ自体を物語っています。



領事













領事は、ミッチェル橋本のギャング、 ハシコープの別の素晴らしい製品であり、Vagrantや他の多くのような素晴らしいツールで私たちを喜ばせます。

Consulは、登録者によって最新の状態に維持される分散型の一貫した構成リポジトリとして機能します。

エージェントとサーバーで構成されます(N / 2 + 1サーバーのクォーラム)。 エージェントはクラスターのノードで起動され、サービスの登録、検証スクリプトの実行、およびConsulサーバーの結果の報告に関与します。

Consulをキー値ストアとして使用して、コンテナの関係をより柔軟に構成することもできます。

また、Consulは、登録者もサポートしているチェックのリストに従ってヘルスチェッカーとして機能します。

サービス、チェック、ノード、そしてもちろんREST APIのステータスを表示できるWeb UIがあります。



チェックについて少し:



スクリプト



スクリプトチェック。 スクリプトはステータスコードを返す必要があります。



-終了コード0-合格のステータスをチェックインします(つまり、サービスはすべて正常です)

-終了コード1-警告ステータスのチェックイン

-その他のコード-失敗ステータスを確認する



例:

 #!/usr/bin/with-contenv sh RESULT=`redis-cli ping` if [ "$RESULT" = "PONG" ]; then exit 0 fi exit 2
      
      







ドキュメントには、nagiosプラグインに似たものの使用例も記載されています。

 { "check": { "id": "mem-util", "name": "Memory utilization", "script": "/usr/local/bin/check_mem.py", "interval": "10s" } }
      
      







gist.github.com/mtchavez/e367db8b69aeba363d21



TCP



指定されたホスト名/ IPアドレスのソケットをノックします。 例:

 { "id": "ssh", "name": "SSH TCP on port 22", "tcp": "127.0.0.1:22", "interval": "10s", "timeout": "1s" }
      
      







HTTP



標準HTTPチェックの例:



REST API Consulを介してチェックを登録することに加えて、-l( label )引数を使用して、コンテナーの開始時にチェックをハングさせることができます。

例として、django + uwsgiを使用してコンテナを起動します。

 docker run -p 8088:3000 -d --name uwsgi-worker --link consul:consul -l "SERVICE_NAME=uwsgi-worker" -l "SERVICE_TAGS=django" \ -l "SERVICE_3000_CHECK_HTTP=/" -l "SERVICE_3000_CHECK_INTERVAL=15s" -l "SERVICE_3000_CHECK_TIMEOUT=1s" uwsgi-worker
      
      







Consul UIには、標準のdjangoページのヘッダーが表示されます。 チェックステータスが合格であることがわかります。これは、すべてがサービスで正常であることを意味します。







または、http経由でREST APIにリクエストを行うことができます。

 curl http://<consul_ip>:8500/v1/health/service/uwsgi-worker | jq .
      
      





 [ { "Node": { "Node": "docker0", "Address": "127.0.0.1", "CreateIndex": 370, "ModifyIndex": 159636 }, "Service": { "ID": "docker0:uwsgi-worker:3000", "Service": "uwsgi-worker", "Tags": [ "django" ], "Address": "127.0.0.1", "Port": 8088, "EnableTagOverride": false, "CreateIndex": 159631, "ModifyIndex": 159636 }, "Checks": [ { "Node": "docker0", "CheckID": "serfHealth", "Name": "Serf Health Status", "Status": "passing", "Notes": "", "Output": "Agent alive and reachable", "ServiceID": "", "ServiceName": "", "CreateIndex": 370, "ModifyIndex": 370 }, { "Node": "docker0", "CheckID": "service:docker1:uwsgi-worker:3000", "Name": "Service 'uwsgi-worker' check", "Status": "passing", "Notes": "", "Output": "", "ServiceID": "docker0:uwsgi-worker:3000", "ServiceName": "uwsgi-worker", "CreateIndex": 159631, "ModifyIndex": 159636 } ] } ]
      
      







HTTPサービスが2xx応答ステータスを返す限り、Consulはそれが生きていると見なします。 応答コードが429(要求が多すぎる)の場合-検証は警告状態になり、他のすべてのコードは失敗としてマークされ、Consulはこのサービスを失敗としてマークします。

デフォルトでは、httpチェック間隔は10秒です。 タイムアウトパラメータを定義することにより、異なる間隔を指定できます。

Consul Templateは、検証の結果に基づいて、バランサーに構成ファイルを生成します。バランサーはN番目の「正常な」ワーカーを使用し、バランサーはワーカーにリクエストを送信します。



領事への新しい小切手の登録:

 curl -XPUT -d @_ssh_check.json http://<consul_ip>:8500/v1/agent/check/register
      
      







ssh_check.jsonファイルの検証パラメーターは次のとおりです。

 { "id": "ssh", "name": "SSH TCP on port 22", "tcp": "<your_ip>:22", "interval": "10s", "timeout": "1s" }
      
      







チェックを無効にします。

 curl http://<consul_ip>:8500/v1/agent/check/deregister/ssh_check
      
      







Consulの可能性は非常に大きく、残念ながら1つの記事でそれらすべてを網羅することは問題です。

希望する人は、多くの例があり、すべてについて十分に書かれている公式ドキュメントを参照できます。



登録者





登録者は、実行中のDockerコンテナーの変更に関する情報提供者として機能します。 コンテナのリストを監視し、コンテナを起動または停止する場合にConsulに適切な変更を加えます。 登録者は、新しいコンテナの作成を含めて、すぐにConsulのサービスのリストに反映します。

また、コンテナメタデータに基づいてヘルスチェックエントリをConsulに追加します。

たとえば、次のコマンドでコンテナを起動する場合:

 docker run --restart=unless-stopped -v /root/html:/usr/share/nginx/html:ro --links consul:consul -l "SERVICE_NAME=nginx" -l "SERVICE_TAGS=web" -l "SERVICE_CHECK_HTTP=/" -l "SERVICE_CHECK_INTERVAL=15s" -l "SERVICE_CHECK_TIMEOUT=1s" -p 8080:80 -d nginx
      
      







登録者はnignxサービスをConsulに追加し、このサービスのHTTPチェックを作成します。



詳細



領事テンプレート





Hashicorpから来たもう1人のすばらしいツール。 Consulに変わり、その中にあるパラメーター/値の状態に応じて、たとえばコンテナー内などのテンプレートに従ってファイルのコンテンツを生成できます。 Consul Templateは、Consulでデータを更新するときにさまざまなコマンドを実行することもできます。

例:



NGINX:



server.conf.ctmplファイルを作成します

 upstream fpm { least_conn; {{range service "php"}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1; {{else}}server 127.0.0.1:65535{{end}} } server { listen 80; root /var/www/html; index index.php index.html index.htm; server_name your.domain.com; sendfile off; location / { } location ~ \.php$ { fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass fpm; fastcgi_index index.php; include fastcgi_params; } }
      
      





Consulテンプレートを実行します。

 consul-template -consul <your_consul_ip>:8500 -template server.conf.ctmpl -once -dry
      
      





-dryパラメーターは結果の構成をstdoutに表示し、-onceパラメーターはconsul-templateを1回実行します。

 upstream fpm { least_conn; server 127.0.0.1:9000 max_fails=3 fail_timeout=60 weight=1; } server { listen 80; root /var/www/html; index index.php index.html index.htm; server_name your.domain.com; sendfile off; location / { } location ~ \.php$ { fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass fpm; fastcgi_index index.php; include fastcgi_params; } }
      
      





ご覧のとおり、ConsulにphpというIPアドレスとサービスポートを要求し、テンプレートから構成ファイルを表示します。

nginx構成ファイルを最新の状態に保つことができます。

 consul-template -consul <your_consul_ip>:8500 -template server.conf.ctmpl:/etc/nginx/conf.d/server.conf:service nginx reload
      
      







したがって、Consulテンプレートはサービスを監視し、それらをnginx構成に転送します。 サービスが突然クラッシュした場合、またはポートが変更された場合、Consulテンプレートは構成ファイルを更新し、nginxをリロードします。



バランサー(nginx、haproxy)にConsulテンプレートを使用すると非常に便利です。

しかし、これはこのすばらしいツールを使用できるユーザー事例の1つにすぎません。



Consul Templateの詳細



練習する













したがって、ローカルホストに4つの仮想マシンがあり、Debian 8 Jessieがインストールされており、カーネルバージョンが3.16であり、このテクノロジースタックに慣れて、クラスターで何らかのWebアプリケーションを実行しようとする時間と要望があります。



簡単なワードプレスブログを作成しましょう。



* ここでは、SwarmノードとConsulノード間でTLSを設定する瞬間を省略します。



ノードの環境を設定します。





各仮想マシン(以下、ノードと呼びます)にリポジトリを追加します

 echo "deb http://apt.dockerproject.org/repo debian-jessie main" > /etc/apt/sources.list.d/docker.list
      
      





そして、環境に必要なパッケージをインストールします。

 apt-get update apt-get install ca-certificates apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D apt-get update apt-get install docker-engine aufs-tools
      
      





プライマリノードでのバインディングの開始:

 docker run --restart=unless-stopped -d -h `hostname` --name consul -v /mnt:/data \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8300:8300 \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8301:8301 \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8301:8301/udp \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8302:8302 \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8302:8302/udp \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8400:8400 \ -p 8500:8500 \ -p 172.17.0.1:53:53/udp \ gliderlabs/consul-server -server -rejoin -advertise `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'` -bootstrap
      
      





--restart = without-stoppedオプションは、 docker -daemonが手動で停止されていない場合、docker-daemonが再起動してもコンテナを実行し続けます



Consulの起動後、systemdのdocker-daemon起動パラメーターを修正する必要があります

/etc/systemd/system/multi-user.target.wants/docker.serviceファイルで、ExecStart行を次のように変換する必要があります。

 ExecStart=/usr/bin/docker daemon -H fd:// -H tcp://<your_ip>:2375 --storage-driver=aufs --cluster-store=consul://<your_ip>:8500 --cluster-advertise <your_ip>:2375
      
      





そして、その後デーモンを再起動します

 systemctl daemon-reload service docker restart
      
      





Consulが稼働していることを確認します。

 docker ps
      
      





次に、プライマリノードでswarm-managerを実行します。

 docker run --restart=unless-stopped -d \ -p 3375:2375 \ swarm manage \ --replication \ --advertise `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:3375 \ consul://`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8500/
      
      





manageコマンドは、ノードでSwarmマネージャーを起動します。

--replicationオプションは、クラスターのプライマリノードとセカンダリノード間のレプリケーションを有効にします。

 docker run --restart=unless-stopped -d \ swarm join \ --advertise=`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:2375 \ consul://`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8500/
      
      





joinコマンドは、コンテナ内のアプリケーションを実行するノードをswarmクラスターに追加します。

Consulアドレスを渡すことにより、サービス検出機能を追加します。



そして、もちろん、登録者:

 docker run --restart=unless-stopped -d \ --name=registrator \ --net=host \ --volume=/var/run/docker.sock:/tmp/docker.sock \ gliderlabs/registrator:latest \ consul://`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8500
      
      





次に、残りのノードに進みましょう。



Consulの起動:

 docker run --restart=unless-stopped -d -h `hostname` --name consul -v /mnt:/data \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8300:8300 \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8301:8301 \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8301:8301/udp \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8302:8302 \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8302:8302/udp \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8400:8400 \ -p `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8500:8500 \ -p 172.17.0.1:53:53/udp \ gliderlabs/consul-server -server -rejoin -advertise `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'` -join <primary_node_ip>
      
      





ここで、 -joinパラメーターでは、上記で構成したプライマリノードのアドレスを指定する必要があります。



Swarmマネージャー:

 docker run --restart=unless-stopped -d \ -p 3375:2375 \ swarm manage \ --advertise `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:3375 \ consul://`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8500/
      
      





ノードをクラスターに接続します。

 docker run --restart=unless-stopped -d \ swarm join \ --advertise=`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:2375 \ consul://`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8500/
      
      





Consulでサービスを登録するための登録者。

 docker run --restart=unless-stopped -d \ --name=registrator \ --net=host \ --volume=/var/run/docker.sock:/tmp/docker.sock \ gliderlabs/registrator:latest \ -ip `ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'` \ consul://`ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`:8500
      
      







「クイックコマンド」について少し



すべてのコンテナを再起動します

 docker stop $(docker ps -aq);docker start $(docker ps -aq)
      
      





すべてのコンテナを削除

 docker stop $(docker ps -aq);docker rm $(docker ps -aq)
      
      





非アクティブなコンテナをすべて削除する:

 docker stop $(docker ps -a | grep 'Exited' | awk '{print $1}') && docker rm $(docker ps -a | grep 'Exited' | awk '{print $1}')
      
      





すべてのボリュームを削除する(ビジー期限切れにならない)

 docker volume rm $(docker volume ls -q);
      
      





すべての画像の削除(ビジー期限切れなし)

 docker rmi $(docker images -q);
      
      







フロントエンド





したがって、クラスターは作業と防御の準備ができています。 プライマリノードに戻って、フロントエンドバランサーを実行しましょう。

上で述べたように、コマンドラインで作業するとき、変数を指定すると便利です

 export DOCKER_HOST=tcp://<my_swarm_ip>:3375
      
      





そして、通常どおりdockerコマンドを使用しますが、すでにローカルノードではなく、クラスター全体で機能しています。



phusion-baseimageイメージを使用し、プロセスで少し変更します。 Consulテンプレートを追加して、nginx構成ファイルを最新の状態に保ち、その中に稼働中および作業中のワーカーのリストを保持する必要があります。 nginx-lbフォルダーを作成し、その中に次の内容のDockerfileを作成します。



非表示のテキスト
 FROM phusion/baseimage:0.9.18 ENV NGINX_VERSION 1.8.1-1~trusty ENV DEBIAN_FRONTEND=noninteractive # Avoid ERROR: invoke-rc.d: policy-rc.d denied execution of start. RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d RUN curl -sS http://nginx.org/keys/nginx_signing.key | sudo apt-key add - && \ echo 'deb http://nginx.org/packages/ubuntu/ trusty nginx' >> /etc/apt/sources.list && \ echo 'deb-src http://nginx.org/packages/ubuntu/ trusty nginx' >> /etc/apt/sources.list && \ apt-get update -qq && apt-get install -y unzip ca-certificates nginx=${NGINX_VERSION} && \ rm -rf /var/lib/apt/lists/* && \ ln -sf /dev/stdout /var/log/nginx/access.log && \ ln -sf /dev/stderr /var/log/nginx/error.log EXPOSE 80 #      Consul Template ADD https://releases.hashicorp.com/consul-template/0.12.2/consul-template_0.12.2_linux_amd64.zip /usr/bin/ RUN unzip /usr/bin/consul-template_0.12.2_linux_amd64.zip -d /usr/local/bin ADD nginx.service /etc/service/nginx/run RUN chmod a+x /etc/service/nginx/run ADD consul-template.service /etc/service/consul-template/run RUN chmod a+x /etc/service/consul-template/run RUN rm -v /etc/nginx/conf.d/*.conf ADD app.conf.ctmpl /etc/consul-templates/app.conf.ctmpl CMD ["/sbin/my_init"]
      
      









次に、nignx起動スクリプトを作成する必要があります。 nginx.serviceファイルを作成します。

 #!/bin/sh /usr/sbin/nginx -c /etc/nginx/nginx.conf -t && \ exec /usr/sbin/nginx -c /etc/nginx/nginx.conf -g "daemon off;"
      
      





Consul Template起動スクリプト:

 #!/bin/sh exec /usr/local/bin/consul-template \ -consul consul:8500 \ -template "/etc/consul-templates/app.conf.ctmpl:/etc/nginx/conf.d/app.conf:sv hup nginx || true"
      
      





素晴らしい。 次に、Consulテンプレート用のnginx構成ファイルテンプレートが必要です。 app.confを作成します。



非表示のテキスト
 upstream fpm { least_conn; {{range service "fpm"}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1; {{else}}server 127.0.0.1:65535{{end}} } server { listen 80; root /var/www/html; index index.php index.html index.htm; server_name domain.example.com; sendfile off; location / { try_files $uri $uri/ /index.php?q=$uri&$args; } location /doc/ { alias /usr/share/doc/; autoindex on; allow 127.0.0.1; allow ::1; deny all; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/www; } location ~ \.php$ { try_files $uri =404; fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass fpm; fastcgi_index index.php; include fastcgi_params; } location ~ /\.ht { deny all; } }
      
      









次に、変更されたイメージを作成する必要があります。

 docker build -t nginx-lb .
      
      





クラスターの各ノードでこの画像を手動で収集するか、無料のDocker Hubクラウドにアップロードして、いつでもどこからでも不要なジェスチャーなしで撮影できる2つのオプションがあります。 または、個人のDockerレジストリ内。

Docker Hubの操作については、 ドキュメントで 詳しく説明してい ます



今が何が起こったのかを見る時です。 コンテナを起動します。

 docker run -p 80:80 -v /mnt/storage/www:/var/www/html -d --name balancer --link consul:consul -l "SERVICE_NAME=balancer" -l "SERVICE_TAGS=balancer" \ -l "SERVICE_CHECK_HTTP=/" -l "SERVICE_CHECK_INTERVAL=15s" -l "SERVICE_CHECK_TIMEOUT=1s" nginx-lb
      
      





ブラウザをつついて確認してください。 はい、彼は悪いゲートウェイを与えるでしょう、なぜなら スタティックもバックエンドもありません。



バックエンド





さて、フロントエンドを見つけました。 誰かがPHPコードを処理する必要があります。 これは、FPMを使用したWordPressイメージに役立ちます

ここでも、画像をわずかに修正する必要があります。 つまり、Consulテンプレートを追加して、MySQLサーバーを検出します。 しかし、データベースサーバーが実行されているノードを毎回調べて、イメージの起動時にそのアドレスを手動で指定する必要はありませんか? これにはそれほど時間はかかりませんが、私たちは怠け者であり、「怠lazは進歩のエンジン」です(c)。



Dockerfile
 FROM php:5.6-fpm # install the PHP extensions we need RUN apt-get update && apt-get install -y unzip libpng12-dev libjpeg-dev && rm -rf /var/lib/apt/lists/* \ && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ && docker-php-ext-install gd mysqli opcache # set recommended PHP.ini settings # see https://secure.php.net/manual/en/opcache.installation.php RUN { \ echo 'opcache.memory_consumption=128'; \ echo 'opcache.interned_strings_buffer=8'; \ echo 'opcache.max_accelerated_files=4000'; \ echo 'opcache.revalidate_freq=60'; \ echo 'opcache.fast_shutdown=1'; \ echo 'opcache.enable_cli=1'; \ } > /usr/local/etc/php/conf.d/opcache-recommended.ini VOLUME /var/www/html ENV WORDPRESS_VERSION 4.4.2 ENV WORDPRESS_SHA1 7444099fec298b599eb026e83227462bcdf312a6 # upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress RUN curl -o wordpress.tar.gz -SL https://wordpress.org/wordpress-${WORDPRESS_VERSION}.tar.gz \ && echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c - \ && tar -xzf wordpress.tar.gz -C /usr/src/ \ && rm wordpress.tar.gz \ && chown -R www-data:www-data /usr/src/wordpress ADD https://releases.hashicorp.com/consul-template/0.12.2/consul-template_0.12.2_linux_amd64.zip /usr/bin/ RUN unzip /usr/bin/consul-template_0.12.2_linux_amd64.zip -d /usr/local/bin #    . ADD db.conf.php.ctmpl /db.conf.php.ctmpl #    consul-template ADD consul-template.sh /usr/local/bin/consul-template.sh #    MySQL      WP ADD mysql.ctmpl /tmp/mysql.ctmpl COPY docker-entrypoint.sh /entrypoint.sh # grr, ENTRYPOINT resets CMD now ENTRYPOINT ["/entrypoint.sh"] CMD ["php-fpm"]
      
      









MySQL設定テンプレートdb.conf.php.ctmplを作成します。

 <?php {{range service "mysql"}} define('DB_HOST', '{{.Address}}'); {{else}} define('DB_HOST', 'mysql'); {{end}} ?>
      
      





そして、consul-template.sh起動スクリプト:

 #!/bin/sh echo "Starting Consul Template" exec /usr/local/bin/consul-template \ -consul consul:8500 \ -template "/db.conf.php.ctmpl:/var/www/html/db.conf.php"
      
      







MySQLサーバー検出テンプレートmysql.ctmpl:

 {{range service "mysql"}}{{.Address}} {{.Port}} {{end}}
      
      





docker-entrypoint.shスクリプトは、いくつかの修正が必要です。 つまり、Consulテンプレートを接続してMySQLサーバーを検出し、デフォルトで127.0.0.1のみをリッスンするため、fpmを0.0.0.0に切り替えます。



非表示のテキスト
 #!/bin/bash set -e #    WORDPRESS_DB_HOST="$(/usr/local/bin/consul-template --template=/tmp/mysql-master.ctmpl --consul=consul:8500 --dry -once | awk '{print $1}' | tail -1)" #    WORDPRESS_DB_PORT="$(/usr/local/bin/consul-template --template=/tmp/mysql-master.ctmpl --consul=consul:8500 --dry -once | awk '{print $2}' | tail -1)" if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then if [ -n "$MYSQL_PORT_3306_TCP" ]; then if [ -z "$WORDPRESS_DB_HOST" ]; then WORDPRESS_DB_HOST='mysql' else echo >&2 'warning: both WORDPRESS_DB_HOST and MYSQL_PORT_3306_TCP found' echo >&2 " Connecting to WORDPRESS_DB_HOST ($WORDPRESS_DB_HOST)" echo >&2 ' instead of the linked mysql container' fi fi if [ -z "$WORDPRESS_DB_HOST" ]; then echo >&2 'error: missing WORDPRESS_DB_HOST and MYSQL_PORT_3306_TCP environment variables' echo >&2 ' Did you forget to --link some_mysql_container:mysql or set an external db' echo >&2 ' with -e WORDPRESS_DB_HOST=hostname:port?' exit 1 fi # if we're linked to MySQL and thus have credentials already, let's use them : ${WORDPRESS_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}} if [ "$WORDPRESS_DB_USER" = 'root' ]; then : ${WORDPRESS_DB_PASSWORD:=$MYSQL_ENV_MYSQL_ROOT_PASSWORD} fi : ${WORDPRESS_DB_PASSWORD:=$MYSQL_ENV_MYSQL_PASSWORD} : ${WORDPRESS_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-wordpress}} if [ -z "$WORDPRESS_DB_PASSWORD" ]; then echo >&2 'error: missing required WORDPRESS_DB_PASSWORD environment variable' echo >&2 ' Did you forget to -e WORDPRESS_DB_PASSWORD=... ?' echo >&2 echo >&2 ' (Also of interest might be WORDPRESS_DB_USER and WORDPRESS_DB_NAME.)' exit 1 fi if ! [ -e index.php -a -e wp-includes/version.php ]; then echo >&2 "WordPress not found in $(pwd) - copying now..." if [ "$(ls -A)" ]; then echo >&2 "WARNING: $(pwd) is not empty - press Ctrl+C now if this is an error!" ( set -x; ls -A; sleep 10 ) fi tar cf - --one-file-system -C /usr/src/wordpress . | tar xf - echo >&2 "Complete! WordPress has been successfully copied to $(pwd)" if [ ! -e .htaccess ]; then # NOTE: The "Indexes" option is disabled in the php:apache base image cat > .htaccess <<-'EOF' # BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> # END WordPress EOF chown www-data:www-data .htaccess fi fi # TODO handle WordPress upgrades magically in the same way, but only if wp-includes/version.php's $wp_version is less than /usr/src/wordpress/wp-includes/version.php's $wp_version # version 4.4.1 decided to switch to windows line endings, that breaks our seds and awks # https://github.com/docker-library/wordpress/issues/116 # https://github.com/WordPress/WordPress/commit/1acedc542fba2482bab88ec70d4bea4b997a92e4 sed -ri 's/\r\n|\r/\n/g' wp-config* # FPM   0.0.0.0 sed -i 's/listen = 127.0.0.1:9000/listen = 0.0.0.0:9000/g' /usr/local/etc/php-fpm.d/www.conf if [ ! -e wp-config.php ]; then awk '/^\/\*.*stop editing.*\*\/$/ && c == 0 { c = 1; system("cat") } { print }' wp-config-sample.php > wp-config.php <<'EOPHP' // If we're behind a proxy server and using HTTPS, we need to alert Wordpress of that fact // see also http://codex.wordpress.org/Administration_Over_SSL#Using_a_Reverse_Proxy if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { $_SERVER['HTTPS'] = 'on'; } EOPHP #   Consul Template    MySQL DB_HOST_PRE=$(grep 'DB_HOST' wp-config.php) sed -i "s/$DB_HOST_PRE/include 'db.conf.php';/g" wp-config.php chown www-data:www-data wp-config.php fi # see http://stackoverflow.com/a/2705678/433558 sed_escape_lhs() { echo "$@" | sed 's/[]\/$*.^|[]/\\&/g' } sed_escape_rhs() { echo "$@" | sed 's/[\/&]/\\&/g' } php_escape() { php -r 'var_export(('$2') $argv[1]);' "$1" } set_config() { key="$1" value="$2" var_type="${3:-string}" start="(['\"])$(sed_escape_lhs "$key")\2\s*," end="\);" if [ "${key:0:1}" = '$' ]; then start="^(\s*)$(sed_escape_lhs "$key")\s*=" end=";" fi sed -ri "s/($start\s*).*($end)$/\1$(sed_escape_rhs "$(php_escape "$value" "$var_type")")\3/" wp-config.php } set_config 'DB_HOST' "$WORDPRESS_DB_HOST" set_config 'DB_USER' "$WORDPRESS_DB_USER" set_config 'DB_PASSWORD' "$WORDPRESS_DB_PASSWORD" set_config 'DB_NAME' "$WORDPRESS_DB_NAME" # allow any of these "Authentication Unique Keys and Salts." to be specified via # environment variables with a "WORDPRESS_" prefix (ie, "WORDPRESS_AUTH_KEY") UNIQUES=( AUTH_KEY SECURE_AUTH_KEY LOGGED_IN_KEY NONCE_KEY AUTH_SALT SECURE_AUTH_SALT LOGGED_IN_SALT NONCE_SALT ) for unique in "${UNIQUES[@]}"; do eval unique_value=\$WORDPRESS_$unique if [ "$unique_value" ]; then set_config "$unique" "$unique_value" else # if not specified, let's generate a random value current_set="$(sed -rn "s/define\((([\'\"])$unique\2\s*,\s*)(['\"])(.*)\3\);/\4/p" wp-config.php)" if [ "$current_set" = 'put your unique phrase here' ]; then set_config "$unique" "$(head -c1M /dev/urandom | sha1sum | cut -d' ' -f1)" fi fi done if [ "$WORDPRESS_TABLE_PREFIX" ]; then set_config '$table_prefix' "$WORDPRESS_TABLE_PREFIX" fi if [ "$WORDPRESS_DEBUG" ]; then set_config 'WP_DEBUG' 1 boolean fi TERM=dumb php -- "$WORDPRESS_DB_HOST" "$WORDPRESS_DB_USER" "$WORDPRESS_DB_PASSWORD" "$WORDPRESS_DB_NAME" <<'EOPHP' <?php // database might not exist, so let's try creating it (just to be safe) $stderr = fopen('php://stderr', 'w'); list($host, $port) = explode(':', $argv[1], 2); $maxTries = 10; do { $mysql = new mysqli($host, $argv[2], $argv[3], '', (int)$port); if ($mysql->connect_error) { fwrite($stderr, "\n" . 'MySQL Connection Error: (' . $mysql->connect_errno . ') ' . $mysql->connect_error . "\n"); --$maxTries; if ($maxTries <= 0) { exit(1); } sleep(3); } } while ($mysql->connect_error); if (!$mysql->query('CREATE DATABASE IF NOT EXISTS `' . $mysql->real_escape_string($argv[4]) . '`')) { fwrite($stderr, "\n" . 'MySQL "CREATE DATABASE" Error: ' . $mysql->error . "\n"); $mysql->close(); exit(1); } $mysql->close(); EOPHP fi #  consul-template exec /usr/local/sbin/php-fpm & exec /usr/local/bin/consul-template.sh exec "$@"
      
      









さて、画像を組み立てましょう:

 docker build -t fpm .
      
      





Wordpressの全機能を実行するデータベースサーバーがないため、まだ起動する必要はありません。

 docker run --name fpm.0 -d -v /mnt/storage/www:/var/www/html \ -e WORDPRESS_DB_NAME=wordpressp -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpress \ --link consul:consul -l "SERVICE_NAME=php-fpm" -l "SERVICE_PORT=9000" -p 9000:9000 fpm
      
      







データベース:





マスター





MySQL 5.7イメージをデータベースとして使用します



また、少し修正する必要があります。 すなわち:2つの画像を作成します。 1つはマスター用、2つ目はスレーブ用です。

Master.



Dockerfile
 FROM debian:jessie # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mysql && useradd -r -g mysql mysql RUN mkdir /docker-entrypoint-initdb.d # FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db: # File::Basename # File::Copy # Sys::Hostname # Data::Dumper RUN apt-get update && apt-get install -y perl pwgen --no-install-recommends && rm -rf /var/lib/apt/lists/* # gpg: key 5072E1F5: public key "MySQL Release Engineering <mysql-build@oss.oracle.com>" imported RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys A4A9406876FCBD3C456770C88C718D3B5072E1F5 ENV MYSQL_MAJOR 5.7 ENV MYSQL_VERSION 5.7.11-1debian8 RUN echo "deb http://repo.mysql.com/apt/debian/ jessie mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list # the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql) # also, we set debconf keys to make APT a little quieter RUN { \ echo mysql-community-server mysql-community-server/data-dir select ''; \ echo mysql-community-server mysql-community-server/root-pass password ''; \ echo mysql-community-server mysql-community-server/re-root-pass password ''; \ echo mysql-community-server mysql-community-server/remove-test-db select false; \ } | debconf-set-selections \ && apt-get update && apt-get install -y mysql-server="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql # comment out a few problematic configuration values # don't reverse lookup hostnames, they are usually another container RUN sed -Ei 's/^(bind-address|log)/#&/' /etc/mysql/my.cnf \ && echo 'skip-host-cache\nskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf \ && mv /tmp/my.cnf /etc/mysql/my.cnf VOLUME /var/lib/mysql COPY docker-entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] EXPOSE 3306 CMD ["mysqld"]
      
      









MySQL:



docker-entrypoint.sh
 #!/bin/bash set -eo pipefail # if command starts with an option, prepend mysqld if [ "${1:0:1}" = '-' ]; then set -- mysqld "$@" fi if [ "$1" = 'mysqld' ]; then # Get config DATADIR="$("$@" --verbose --help 2>/dev/null | awk '$1 == "datadir" { print $2; exit }')" if [ ! -d "$DATADIR/mysql" ]; then if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then echo >&2 'error: database is uninitialized and password option is not specified ' echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' exit 1 fi mkdir -p "$DATADIR" chown -R mysql:mysql "$DATADIR" echo 'Initializing database' "$@" --initialize-insecure echo 'Database initialized' "$@" --skip-networking & pid="$!" mysql=( mysql --protocol=socket -uroot ) for i in {30..0}; do if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then break fi echo 'MySQL init process in progress...' sleep 1 done if [ "$i" = 0 ]; then echo >&2 'MySQL init process failed.' exit 1 fi if [ -n "${REPLICATION_MASTER}" ]; then echo "=> Configuring MySQL replication as master (1/2) ..." if [ ! -f /replication_set.1 ]; then echo "=> Writting configuration file /etc/mysql/my.cnf with server-id=1" echo 'server-id = 1' >> /etc/mysql/my.cnf echo 'log-bin = mysql-bin' >> /etc/mysql/my.cnf touch /replication_set.1 else echo "=> MySQL replication master already configured, skip" fi fi # Set MySQL REPLICATION - SLAVE if [ -n "${REPLICATION_SLAVE}" ]; then echo "=> Configuring MySQL replication as slave (1/2) ..." if [ -n "${MYSQL_PORT_3306_TCP_ADDR}" ] && [ -n "${MYSQL_PORT_3306_TCP_PORT}" ]; then if [ ! -f /replication_set.1 ]; then echo "=> Writting configuration file /etc/mysql/my.cnf with server-id=2" echo 'server-id = 2' >> /etc/mysql/my.cnf echo 'log-bin = mysql-bin' >> /etc/mysql/my.cnf echo 'log-bin=slave-bin' >> /etc/mysql/my.cnf touch /replication_set.1 else echo "=> MySQL replication slave already configured, skip" fi else echo "=> Cannot configure slave, please link it to another MySQL container with alias as 'mysql'" exit 1 fi fi # Set MySQL REPLICATION - SLAVE if [ -n "${REPLICATION_SLAVE}" ]; then echo "=> Configuring MySQL replication as slave (2/2) ..." if [ -n "${MYSQL_PORT_3306_TCP_ADDR}" ] && [ -n "${MYSQL_PORT_3306_TCP_PORT}" ]; then if [ ! -f /replication_set.2 ]; then echo "=> Setting master connection info on slave" echo "!!! DEBUG: ${REPLICATION_USER}, ${REPLICATION_PASS}." "${mysql[@]}" <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; CHANGE MASTER TO MASTER_HOST='${MYSQL_PORT_3306_TCP_ADDR}',MASTER_USER='${REPLICATION_USER}',MASTER_PASSWORD='${REPLICATION_PASS}',MASTER_PORT=${MYSQL_PORT_3306_TCP_PORT}, MASTER_CONNECT_RETRY=30; START SLAVE ; EOSQL echo "=> Done!" touch /replication_set.2 else echo "=> MySQL replication slave already configured, skip" fi else echo "=> Cannot configure slave, please link it to another MySQL container with alias as 'mysql'" exit 1 fi fi if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql fi if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi "${mysql[@]}" <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; DELETE FROM mysql.user ; CREATE USER 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION ; DROP DATABASE IF EXISTS test ; FLUSH PRIVILEGES ; EOSQL if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then mysql+=( -p"${MYSQL_ROOT_PASSWORD}" ) fi # Set MySQL REPLICATION - MASTER if [ -n "${REPLICATION_MASTER}" ]; then echo "=> Configuring MySQL replication as master (2/2) ..." if [ ! -f /replication_set.2 ]; then echo "=> Creating a log user ${REPLICATION_USER}:${REPLICATION_PASS}" "${mysql[@]}" <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; CREATE USER '${REPLICATION_USER}'@'%' IDENTIFIED BY '${REPLICATION_PASS}'; GRANT REPLICATION SLAVE ON *.* TO '${REPLICATION_USER}'@'%' ; FLUSH PRIVILEGES ; RESET MASTER ; EOSQL echo "=> Done!" touch /replication_set.2 else echo "=> MySQL replication master already configured, skip" fi fi if [ "$MYSQL_DATABASE" ]; then echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" mysql+=( "$MYSQL_DATABASE" ) fi if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" if [ "$MYSQL_DATABASE" ]; then echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}" fi echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}" fi echo for f in /docker-entrypoint-initdb.d/*; do case "$f" in *.sh) echo "$0: running $f"; . "$f" ;; *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; *) echo "$0: ignoring $f" ;; esac echo done if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then "${mysql[@]}" <<-EOSQL ALTER USER 'root'@'%' PASSWORD EXPIRE; EOSQL fi if ! kill -s TERM "$pid" || ! wait "$pid"; then echo >&2 'MySQL init process failed.' exit 1 fi echo echo 'MySQL init process done. Ready for start up.' echo fi chown -R mysql:mysql "$DATADIR" fi exec "$@"
      
      









:

 docker build -t mysql-master .
      
      





 docker run --name mysql-master.0 -v /mnt/volumes/master:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootpass -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=wordpress -e MYSQL_DB=wordpress -e REPLICATION_MASTER=true -e REPLICATION_USER=replica -e REPLICATION_PASS=replica --link consul:consul -l "SERVICE_NAME=master" -l "SERVICE_PORT=3306" -p 3306:3306 -d mysql-master
      
      







, MySQL (REPLICATION_USER, REPLICATION_PASS, REPLICATION_MASTER, REPLICATION_SLAVE).



Slave





Slave , MySQL Master- . Consul Template:



Dockerfile
 FROM debian:jessie # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd -r mysql && useradd -r -g mysql mysql RUN mkdir /docker-entrypoint-initdb.d # FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db: # File::Basename # File::Copy # Sys::Hostname # Data::Dumper RUN apt-get update && apt-get install -y perl pwgen --no-install-recommends && rm -rf /var/lib/apt/lists/* # gpg: key 5072E1F5: public key "MySQL Release Engineering <mysql-build@oss.oracle.com>" imported RUN apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys A4A9406876FCBD3C456770C88C718D3B5072E1F5 ENV MYSQL_MAJOR 5.7 ENV MYSQL_VERSION 5.7.11-1debian8 RUN echo "deb http://repo.mysql.com/apt/debian/ jessie mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list # the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql) # also, we set debconf keys to make APT a little quieter RUN { \ echo mysql-community-server mysql-community-server/data-dir select ''; \ echo mysql-community-server mysql-community-server/root-pass password ''; \ echo mysql-community-server mysql-community-server/re-root-pass password ''; \ echo mysql-community-server mysql-community-server/remove-test-db select false; \ } | debconf-set-selections \ && apt-get update && apt-get install -y mysql-server="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql # comment out a few problematic configuration values # don't reverse lookup hostnames, they are usually another container RUN sed -Ei 's/^(bind-address|log)/#&/' /etc/mysql/my.cnf \ && echo 'skip-host-cache\nskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf \ && mv /tmp/my.cnf /etc/mysql/my.cnf ADD https://releases.hashicorp.com/consul-template/0.12.2/consul-template_0.12.2_linux_amd64.zip /usr/bin/ RUN unzip /usr/bin/consul-template_0.12.2_linux_amd64.zip -d /usr/local/bin ADD mysql-master.ctmpl /tmp/mysql-master.ctmpl VOLUME /var/lib/mysql COPY docker-entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] EXPOSE 3306 CMD ["mysqld"]
      
      









docker-entrypoint.sh
 #!/bin/bash set -eo pipefail #   Consul,     master MYSQL_PORT_3306_TCP_ADDR="$(/usr/bin/consul-template --template=/tmp/mysql-master.ctmpl --consul=consul:8500 --dry -once | awk '{print $1}' | tail -1)" MYSQL_PORT_3306_TCP_PORT="$(/usr/bin/consul-template --template=/tmp/mysql-master.ctmpl --consul=consul:8500 --dry -once | awk '{print $2}' | tail -1)" if [ "${1:0:1}" = '-' ]; then set -- mysqld "$@" fi if [ "$1" = 'mysqld' ]; then # Get config DATADIR="$("$@" --verbose --help 2>/dev/null | awk '$1 == "datadir" { print $2; exit }')" if [ ! -d "$DATADIR/mysql" ]; then if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then echo >&2 'error: database is uninitialized and password option is not specified ' echo >&2 ' You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD' exit 1 fi mkdir -p "$DATADIR" chown -R mysql:mysql "$DATADIR" echo 'Initializing database' "$@" --initialize-insecure echo 'Database initialized' "$@" --skip-networking & pid="$!" mysql=( mysql --protocol=socket -uroot ) for i in {30..0}; do if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then break fi echo 'MySQL init process in progress...' sleep 1 done if [ "$i" = 0 ]; then echo >&2 'MySQL init process failed.' exit 1 fi if [ -n "${REPLICATION_MASTER}" ]; then echo "=> Configuring MySQL replication as master (1/2) ..." if [ ! -f /replication_set.1 ]; then echo "=> Writting configuration file /etc/mysql/my.cnf with server-id=1" echo 'server-id = 1' >> /etc/mysql/my.cnf echo 'log-bin = mysql-bin' >> /etc/mysql/my.cnf touch /replication_set.1 else echo "=> MySQL replication master already configured, skip" fi fi # Set MySQL REPLICATION - SLAVE if [ -n "${REPLICATION_SLAVE}" ]; then echo "=> Configuring MySQL replication as slave (1/2) ..." if [ -n "${MYSQL_PORT_3306_TCP_ADDR}" ] && [ -n "${MYSQL_PORT_3306_TCP_PORT}" ]; then if [ ! -f /replication_set.1 ]; then echo "=> Writting configuration file /etc/mysql/my.cnf with server-id=2" echo 'server-id = 2' >> /etc/mysql/my.cnf echo 'log-bin = mysql-bin' >> /etc/mysql/my.cnf echo 'log-bin=slave-bin' >> /etc/mysql/my.cnf touch /replication_set.1 else echo "=> MySQL replication slave already configured, skip" fi else echo "=> Cannot configure slave, please link it to another MySQL container with alias as 'mysql'" exit 1 fi fi # Set MySQL REPLICATION - SLAVE if [ -n "${REPLICATION_SLAVE}" ]; then echo "=> Configuring MySQL replication as slave (2/2) ..." if [ -n "${MYSQL_PORT_3306_TCP_ADDR}" ] && [ -n "${MYSQL_PORT_3306_TCP_PORT}" ]; then if [ ! -f /replication_set.2 ]; then echo "=> Setting master connection info on slave" "${mysql[@]}" <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; CHANGE MASTER TO MASTER_HOST='${MYSQL_PORT_3306_TCP_ADDR}',MASTER_USER='${REPLICATION_USER}',MASTER_PASSWORD='${REPLICATION_PASS}',MASTER_PORT=${MYSQL_PORT_3306_TCP_PORT}, MASTER_CONNECT_RETRY=30; START SLAVE ; EOSQL echo "=> Done!" touch /replication_set.2 else echo "=> MySQL replication slave already configured, skip" fi else echo "=> Cannot configure slave, please link it to another MySQL container with alias as 'mysql'" exit 1 fi fi if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then # sed is for https://bugs.mysql.com/bug.php?id=20545 mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql fi if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then MYSQL_ROOT_PASSWORD="$(pwgen -1 32)" echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD" fi "${mysql[@]}" <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; DELETE FROM mysql.user ; CREATE USER 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION ; DROP DATABASE IF EXISTS test ; FLUSH PRIVILEGES ; EOSQL if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then mysql+=( -p"${MYSQL_ROOT_PASSWORD}" ) fi # Set MySQL REPLICATION - MASTER if [ -n "${REPLICATION_MASTER}" ]; then echo "=> Configuring MySQL replication as master (2/2) ..." if [ ! -f /replication_set.2 ]; then echo "=> Creating a log user ${REPLICATION_USER}:${REPLICATION_PASS}" "${mysql[@]}" <<-EOSQL -- What's done in this file shouldn't be replicated -- or products like mysql-fabric won't work SET @@SESSION.SQL_LOG_BIN=0; CREATE USER '${REPLICATION_USER}'@'%' IDENTIFIED BY '${REPLICATION_PASS}'; GRANT REPLICATION SLAVE ON *.* TO '${REPLICATION_USER}'@'%' ; FLUSH PRIVILEGES ; RESET MASTER ; EOSQL echo "=> Done!" touch /replication_set.2 else echo "=> MySQL replication master already configured, skip" fi fi if [ "$MYSQL_DATABASE" ]; then echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}" mysql+=( "$MYSQL_DATABASE" ) fi if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}" if [ "$MYSQL_DATABASE" ]; then echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}" fi echo 'FLUSH PRIVILEGES ;' | "${mysql[@]}" fi echo for f in /docker-entrypoint-initdb.d/*; do case "$f" in *.sh) echo "$0: running $f"; . "$f" ;; *.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;; *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;; *) echo "$0: ignoring $f" ;; esac echo done if [ ! -z "$MYSQL_ONETIME_PASSWORD" ]; then "${mysql[@]}" <<-EOSQL ALTER USER 'root'@'%' PASSWORD EXPIRE; EOSQL fi if ! kill -s TERM "$pid" || ! wait "$pid"; then echo >&2 'MySQL init process failed.' exit 1 fi echo echo 'MySQL init process done. Ready for start up.' echo fi chown -R mysql:mysql "$DATADIR" fi exec "$@"
      
      









Consul Template, mysql-master.ctmpl:

 {{range service "master"}}{{.Address}} {{.Port}} {{end}}
      
      





収集するもの:

 docker build -t mysql-slave .
      
      





以下を開始します。

 docker run --name mysql-slave.0 -v /mnt/volumes/slave:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=rootpass -e REPLICATION_SLAVE=true -e REPLICATION_USER=replica -e REPLICATION_PASS=replica --link=consul:consul -l "SERVICE_NAME=slave" -l "SERVICE_PORT=3307" -p 3307:3306 -d mysql-slave
      
      





, .

 docker run --name fpm.0 -d -v /mnt/storage/www:/var/www/html \ -e WORDPRESS_DB_NAME=wordpressp -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=wordpress \ --link consul:consul -l "SERVICE_NAME=php-fpm" -l "SERVICE_PORT=9000" -l "SERVICE_TAGS=worker" -p 9000:9000 fpm
      
      





, , , Wordress .



 docker logs <container_name>
      
      







Docker-compose.





, , , … , , ? — docker-compose .

. Docker-compose YAML, . .



, - docker-compose.yml.



非表示のテキスト
 mysql-master: image: mysql-master ports: - "3306:3306" environment: - "MYSQL_DATABASE=wp" - "MYSQL_USER=wordpress" - "MYSQL_PASSWORD=wordpress" - "REPLICATION_MASTER=true" - "REPLICATION_USER=replica" - "REPLICATION_PASS=replica" external_links: - consul:consul labels: - "SERVICE_NAME=mysql-master" - "SERVICE_PORT=3306" - "SERVICE_TAGS=db" volumes: - '/mnt/storage/master:/var/lib/mysql' mysql-slave: image: mysql-slave ports: - "3307:3306" environment: - "REPLICATION_SLAVE=true" - "REPLICATION_USER=replica" - "REPLICATION_PASS=replica" external_links: - consul:consul labels: - "SERVICE_NAME=mysql-slave" - "SERVICE_PORT=3307" - "SERVICE_TAGS=db" volumes: - '/mnt/storage/slave:/var/lib/mysql' wordpress: image: fpm ports: - "9000:9000" environment: - "WORDPRESS_DB_NAME=wp" - "WORDPRESS_DB_USER=wordpress" - "WORDPRESS_DB_PASSWORD=wordpress" external_links: - consul:consul labels: - "SERVICE_NAME=php-fpm" - "SERVICE_PORT=9000" - "SERVICE_TAGS=worker" volumes: - '/mnt/storage/www:/var/www/html'
      
      









«» , .

 docker-compose up
      
      







おわりに









— .

Swarm . , . « ».



— .

, — . — . .



— .

, .



— .

docker-compose .







.

, Docker stateful-. flocker, , «» .

glusterfs, lsyncd. Glusterfs, , , .



, — .











PS

how-to, .

/ , , .



All Articles