基礎知識
非常に今更ですが、そもそもコンテナ自体が初めてなので、基礎知識を確認。
Docker
Linuxカーネルのもつコンテナ機能を利用して、アプリケーションのデプロイを自動化するためのソフトウェア。コンテナ仮想化の技術(コンテナエンジン)においてはデファクトスタンダード。
Comunity Edition(CE)とEnterprise Edition(EE)があり、EEの中でもBasic, Standard, Advancedがある。フリーでずっと使用できるのはCE。
Dockerそのものでは、自身のインストールされたホスト上のコンテナしか管理できないため、複数ホスト(クラスタ)上でコンテナを運用するには別途オーケストレーションツールが必要。オーケストレーションツールに関しては、どれがよいかはまだ議論がある。
参考1
参考2
参考3
→比較という意味ではこれが一番わかりやすい。
Docker Swarm
Docker1.12より、Docker自体にバンドルされたオーケストレーションツール。オートスケーリングやリバランスの機能がないなど、他に比べると機能が劣るということだが…。Dockerだけで完結できるので、手軽さという意味ではよさそう。
Kubernetes(k8s)
Googleが開発して、今はCloud Native Computing Foundationが管理。複数のコンテナをPodという単位にまとめ、Pod単位で管理を行う。またPodにServiceというエンドポイントを持たせることができるため、コンテナがスケールアウトしたり、別のホストに移動しても同一のURLでアクセスが継続できる。
KVS(Key Value Store)としてetcdを使用。
Mesos
Apacheが運営。Mesosphere社はディストリビューションを提供。
他2つとは異なり、コンピューティングレイヤーでの抽象化を行う。別の言い方では、複数のホストを1つの巨大なホストのように見立てて、そのうえでコンテナを動かすという考え方。
またそもそもコンテナ管理ツールではないため、コンテナを動かすには別のツールとの連携が必要(Marathon,Chronos)。
KVS(Key Value Store)としてZooKeeperを使用。
インストール
今回はDocker + Swarmで試してみる。
インストール要件
環境
CentOS 7(minimal)
Install
公式通りで問題なし。
追加で、
https://docs.docker.com/engine/installation/linux/linux-postinstall/#configure-docker-to-start-on-boot
の自動起動だけはやっておく。
Tutrial
Image作成
以下3つのファイルを作成して、空きディレクトリに格納。
- Dockerfile
- requirements.txt
- app.py
# Use an official Python runtime as a base image
FROM python:2.7-slim
# Set the working directory to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
ADD . /app
# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt
# Make port 80 available to the world outside this container
EXPOSE 80
# Define environment variable
ENV NAME World
# Run app.py when the container launches
CMD ["python", "app.py"]
Flask
Redis
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
そのディレクトリにて、イメージを作成。
[root@localhost testapp]# ls
app.py Dockerfile requirements.txt
[root@localhost testapp]#
[root@localhost testapp]# docker build -t friendlyhello .
Sending build context to Docker daemon 4.608 kB
Step 1/7 : FROM python:2.7-slim
[root@localhost testapp]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest d058024f6d16 38 seconds ago 195 MB
python 2.7-slim 1c7128a655f6 13 days ago 183 MB
hello-world latest 48b5124b2768 4 months ago 1.84 kB
起動
[root@localhost testapp]# docker run -p 4000:80 friendlyhello
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
コンテナの視点ではポート80で起動しているが、ホストから見ると、ポート4000にマッピングして外部にエクスポートしている。そのため、外からブラウザでアクセスする際は4000に接続すればよい。
上記ではフォアグラウンドで起動してしまうが、バックグラウンドで起動するには、
docker run -d -p 4000:80 friendlyhello
と-dをつける(Detach mode)。
停止は下記のように、docker psでプロセスIDを特定して、docker stopすればよい。
[root@localhost testapp]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9a8a215706d0 friendlyhello "python app.py" 6 seconds ago Up 5 seconds 0.0.0.0:4000->80/tcp festive_snyder
[root@localhost testapp]#
[root@localhost testapp]#
[root@localhost testapp]# docker stop 9a8a215706d0
9a8a215706d0
イメージのシェア
Docker Cloudというサイトに登録すると、フリーでレポジトリを使用することができ、そこにイメージのアップロード、および公開が可能。
https://cloud.docker.com
初回アクセス時はSign Upして、適当なレポジトリを作成する。Docker IDがユーザ名に当たるが、大文字は使用不可らしい。
上記完了したら、Dockerホストにて以下を実行すると、タグ付けしたイメージがアップロードされる。
# docker tag friendlyhello keisuke1208/test:test
→friendlyhelloというイメージに、keisuke1208というアカウントの、testというレポジトリの、testというタグを割り当てている。
[root@localhost testapp]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: keisuke1208
Password:
Login Succeeded
[root@localhost testapp]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
friendlyhello latest d058024f6d16 37 minutes ago 195 MB
python 2.7-slim 1c7128a655f6 13 days ago 183 MB
hello-world latest 48b5124b2768 4 months ago 1.84 kB
[root@localhost testapp]#
[root@localhost testapp]# docker tag friendlyhello keisuke1208/test:test
[root@localhost testapp]# docker push keisuke1208/test:test
The push refers to a repository [docker.io/keisuke1208/test]
afe75cea1bdd: Pushed
a95f70df3cfb: Pushed
3f925f4b7c74: Pushed
7b7f69d6236f: Mounted from library/python
667e68ed0db3: Mounted from library/python
5eac2de68a97: Mounted from library/python
8d4d1ab5ff74: Mounted from library/python
test: digest: sha256:a1e7034a88bc3f272ea0ec78845a0c13824ce4635dc3b88e3b5a990c8adfd788 size: 1787
Swarm
1.12以上であれば、何も意識しなくてもインストールされている。
Serviceの設定
複数のコンテナにてある1つの「サービス」を提供するときに、これをserviceと定義する。Serviceの定義はdocker-compose.ymlというymlにて行う。以下は1例。
webという名前のserviceを、2台のコンテナで起動する。それぞれのコンテナはホストの0.1(10%)のCPUと、50MBのメモリを使用できる。ポートはコンテナ上の80をホストの4000にマップしている。
最初のversionはこのファイルの書式のバージョン。
書式の詳細はこちら。
version: "3"
services:
web:
image: keisuke1208/test:test
deploy:
replicas: 2
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
networks:
webnet:
ファイルを作成したら、swarmを起動したうえでservice(正確にはStack)を起動させる。
以下の例では、getstartedlabというstackを起動しているが、webというserviceはこのstackの一部。
stackはserviceをまとめたもので、1つのStackが複数のサービスを持つことができる。
[root@localhost testapp]# docker swarm init
Swarm initialized: current node (z2ng430u16l3ztxb7xpxneedl) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-262rqbrvz7gy0sylfdpxnizsd2u6q0qf5n5mg207khkhj0ll8r-4tsvhobo7tq0j4t74k7xhv6g1 \
10.32.2.55:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
[root@localhost testapp]#
[root@localhost testapp]# docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web
[root@localhost testapp]#
[root@localhost testapp]# docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
rk2tqj9jqb5l getstartedlab_web.1 keisuke1208/test:test localhost.localdomain Running Running 13 seconds ago
r1n4klevxsn4 getstartedlab_web.2 keisuke1208/test:test localhost.localdomain Running Running 12 seconds ago
[root@localhost testapp]#
docker stack psコマンドで、2つのコンテナが起動していることが確認できる。また4000番にアクセスすると、サービスも起動していることが確認できる。
スケールアウトさせるには、ymlのreplicaの指定を変更して、再度stack deployすればよい。いったんダウンさせる必要はない。
[root@localhost testapp]# cat docker-compose.yml
version: "3"
services:
web:
image: keisuke1208/test:test
deploy:
replicas: 3
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
networks:
webnet:
[root@localhost testapp]#
[root@localhost testapp]#
[root@localhost testapp]#
[root@localhost testapp]# docker stack deploy -c docker-compose.yml getstartedlab
Updating service getstartedlab_web (id: mq21pewumaemvlw0ckkpkqsx2)
[root@localhost testapp]#
[root@localhost testapp]# docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
rk2tqj9jqb5l getstartedlab_web.1 keisuke1208/test:test localhost.localdomain Running Running 26 minutes ago
r1n4klevxsn4 getstartedlab_web.2 keisuke1208/test:test localhost.localdomain Running Running 26 minutes ago
u336wzrj40we getstartedlab_web.3 keisuke1208/test:test localhost.localdomain Running Running 6 seconds ago
本当に大丈夫なのか、別のホストからncでウォッチしてみたところ、一瞬だけ落ちているようだが、基本的には問題なさそう。
[root@tsslin01 ~]# while true; do echo -en "GET / HTTP/1.1\n\n" | nc 10.32.2.55 4000 | head -n 1 >> temp3; date >> temp3;sleep 1;done
^C
[root@tsslin01 ~]#
[root@tsslin01 ~]# cat temp3
HTTP/1.0 200 OK
Thu May 25 16:19:30 JST 2017
HTTP/1.0 200 OK
Thu May 25 16:19:31 JST 2017
HTTP/1.0 200 OK
Thu May 25 16:19:32 JST 2017
Thu May 25 16:19:33 JST 2017
HTTP/1.0 200 OK
Thu May 25 16:19:34 JST 2017
SwarmでCluster化
参考
もう一台同じようにDockerをインストールしたホスト(rainy)を用意。InstallまででTutrialの内容は未実施。
まずはManagerノード(元々swarm initしたホスト,sunny)にて状態確認。
[root@sunny ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
z2ng430u16l3ztxb7xpxneedl * sunny.weather.local Ready Active Leader
Join用のトークンを発行。今回はWorkerとして追加。
[root@sunny ~]# docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-262rqbrvz7gy0sylfdpxnizsd2u6q0qf5n5mg207khkhj0ll8r-4tsvhobo7tq0j4t74k7xhv6g1 \
10.32.2.55:2377
表示されたコマンドをrainyにて実行。
[root@rainy ~]# docker swarm join --token SWMTKN-1-262rqbrvz7gy0sylfdpxnizsd2u6q0qf5n5mg207khkhj0ll8r-4tsvhobo7tq0j4t74k7xhv6g1 10.32.2.55:2377
This node joined a swarm as a worker.
これでSwarm Clusterが形成される。
[root@sunny ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
kyjd450c6ixluglc6ixu8arwo rainy.weather.local Ready Active
z2ng430u16l3ztxb7xpxneedl * sunny.weather.local Ready Active Leader
この状態でStackをデプロイすると、3台のコンテナ中2台がrainy,1台がsunnyで起動していることがわかる。
[root@sunny testapp]# docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web
[root@sunny testapp]#
[root@sunny testapp]# docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
jmzusd2ta971 getstartedlab_web.1 keisuke1208/test:test rainy.weather.local Running Preparing 36 seconds ago
4j91vu2pn91q getstartedlab_web.2 keisuke1208/test:test rainy.weather.local Running Preparing 36 seconds ago
qreob82mno3p getstartedlab_web.3 keisuke1208/test:test sunny.weather.local Running Running 35 seconds ago
この状態でアクセスすると、ラウンドロビンで3台にアクセスされていることがわかる。
ここで表示されるホスト名は、各コンテナに設定されているらしい。
[root@sunny testapp]# ^ls^cat
cat /var/lib/docker/containers/f71ab01c50019138939cc897906c690f518c8325086f9b6c274a486f5a78ee15/hostname
f71ab01c5001
障害が発生すると??
Worker(rainy)のdockerサービスを落として疑似障害を発生させる。
[root@sunny testapp]# docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
i572m3d8ngpa getstartedlab_web.1 keisuke1208/test:test rainy.weather.local Running Running 7 seconds ago
w2yd90vqk3e5 getstartedlab_web.2 keisuke1208/test:test sunny.weather.local Running Running 7 seconds ago
nqdoy8a7ymy4 getstartedlab_web.3 keisuke1208/test:test sunny.weather.local Running Running 7 seconds ago
[root@sunny testapp]#
(Worker(rainy)のDockerサービスをStop)
[root@sunny testapp]#
[root@sunny testapp]# docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
x66fmwazzoxy getstartedlab_web.1 keisuke1208/test:test sunny.weather.local Running Running less than a second ago
i572m3d8ngpa \_ getstartedlab_web.1 keisuke1208/test:test rainy.weather.local Shutdown Running 20 seconds ago
w2yd90vqk3e5 getstartedlab_web.2 keisuke1208/test:test sunny.weather.local Running Running 39 seconds ago
nqdoy8a7ymy4 getstartedlab_web.3 keisuke1208/test:test sunny.weather.local Running Running 39 seconds ago
[root@sunny testapp]#
(Worker(rainy)のDockerサービスをStart)
[root@sunny testapp]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
kyjd450c6ixluglc6ixu8arwo rainy.weather.local Ready Active
z2ng430u16l3ztxb7xpxneedl * sunny.weather.local Ready Active Leader
[root@sunny testapp]#
[root@sunny testapp]# docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
x66fmwazzoxy getstartedlab_web.1 keisuke1208/test:test sunny.weather.local Running Running about a minute ago
i572m3d8ngpa \_ getstartedlab_web.1 keisuke1208/test:test rainy.weather.local Shutdown Shutdown 12 seconds ago
w2yd90vqk3e5 getstartedlab_web.2 keisuke1208/test:test sunny.weather.local Running Running 2 minutes ago
nqdoy8a7ymy4 getstartedlab_web.3 keisuke1208/test:test sunny.weather.local Running Running 2 minutes ago
このshutdownとなっている古いプロセスの情報は、任意には消せないらしい。
docker service update <service name> --force
でリバランスは可能だが、生きているやつもすべて落としてリバランスしている。
[root@sunny testapp]# docker service update getstartedlab_web --force
getstartedlab_web
[root@sunny testapp]#
[root@sunny testapp]# docker service ps getstartedlab_web
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
qnkadpxcvu9f getstartedlab_web.1 keisuke1208/test:test rainy.weather.local Ready Ready 2 seconds ago
x66fmwazzoxy \_ getstartedlab_web.1 keisuke1208/test:test sunny.weather.local Shutdown Running 2 seconds ago
i572m3d8ngpa \_ getstartedlab_web.1 keisuke1208/test:test rainy.weather.local Shutdown Shutdown 4 minutes ago
y24fw1ggg3lc getstartedlab_web.2 keisuke1208/test:test rainy.weather.local Ready Ready 2 seconds ago
w2yd90vqk3e5 \_ getstartedlab_web.2 keisuke1208/test:test sunny.weather.local Shutdown Running 2 seconds ago
vsftpfdx56y3 getstartedlab_web.3 keisuke1208/test:test sunny.weather.local Ready Ready 2 seconds ago
nqdoy8a7ymy4 \_ getstartedlab_web.3 keisuke1208/test:test sunny.weather.local Shutdown Running 2 seconds ago
感想
他を使ったことがないのであまり参考にならないが、Docker + Swarmは確かに簡単そう。複数台ホストに分散したシステムも簡単に作れた。ドキュメントの精度も高く、ほとんどつっかかるポイントがなかったのも助かる。
リバランスが乱暴なのが気になるが、もう少し使ってみないと何ともいえない。
その他参考
KVS比較
https://www.consul.io/intro/vs/zookeeper.html
Docker CE,EE
https://www.slideshare.net/Docker/docker-online-meetup-announcing-docker-ce-ee
Docker Doc
https://docs.docker.com