こんにちは、開発部の澤井です。
前回は1つのサーバに複数のコンテナを配置して、ブリッジネットワークにおけるコンテナ間通信についてまとめました。
ブリッジネットワークは、同一ブリッジネットワークに配置したコンテナ同士でのみ通信できます。
一般に複数のサーバに配置したコンテナ間で通信する環境をマルチホストと呼びます。 また複数コンテナを管理するツールをコンテナオーケストレーションツールと呼びます。
代表的なコンテナオーケストレーションツールに以下があります。
- Amazon ECS
- Kubernetes
- Docker Swarm
今回は簡単なマルチホストのサンプルをDocker Swarmで動かしたいと思います。
Docker Swarmでマルチホストを実現する技術
Docker Swarm
Docker Swarmは、Docker社が提供するオーケストレーションツールで複数のホストを集約して管理します。
Docker Swarmは、manager
ノードとworker
ノードで構成されます。
overlayネットワーク
overlay
ネットワークは複数のDockerホスト間に分散ネットワークを作成します。
具体的にはVXLAN技術を使用してOSI参照モデルのレイヤ3に論理的なレイヤ2を構築して同一セグメントのように扱います。
ゴール
Docker SwarmでAWSに以下のアプリケーションを構築します。
- 可用性を高めるために異なるサブネットにwebコンテナを配置する
- dbコンテナはwebコンテナとは独立したサブネットに配置する
準備
本記事で使用するAmazon EC2はUbuntu 22.04.2 LTSを使用します。
Docker Engineをインストール
UbuntuにDocker Engineをインストールする手順はInstall Docker Engine on Ubuntuに記載されています。
本記事は各EC2インスタンスにDocker Engineがインストール済みであることを前提としています。
ホスト用EC2インスタンス&ネットワークを構築
VPC10.0.0.0/16
に4つのサブネットを作成して、各1つのEC2インスタンスを配置します。
ノード | サブネット | プライベートIPアドレス | ホスト名(hostname) | パブリックIPアドレス |
---|---|---|---|---|
manager | 10.0.0.0/24(パブリックサブネット) | 10.0.0.10 | ip-10-0-0-10 | 203.0.113.10 |
worker | 10.0.1.0/24(プライベートサブネット) | 10.0.1.10 | ip-10-0-1-10 | なし |
worker | 10.0.2.0/24(プライベートサブネット) | 10.0.2.10 | ip-10-0-2-10 | なし |
worker | 10.0.3.0/24(プライベートサブネット) | 10.0.3.10 | ip-10-0-3-10 | なし |
プライベートサブネットはNATゲートウェイを使用して外部とアクセス可能にします。
EC2 セキュリティグループ
Swarmモードを使用するために以下ポートを開きます。
- TCP port 2377 は、クラスタ管理通信のため
- TCP と UDP の port 7946 は、ノード間の通信のため
- UDP port 4789 はオーバレイ・ネットワーク・トラフィックのため
ref. swarm モード導入ガイド
workerのセキュリティグループ
managerのセキュリティグループ
manager
は上記に加えて2377
を追加します。
また動作確認のために8000
も追加します(こちらは本記事のサンプルアプリケーションの仕様でありDocker Swarmの仕様ではありません)。
サンプルコード
簡単なサンプルなので本記事で使用するすべてのコードを掲載します。
web用PHP
<?php
// index.php
$redis = new Redis();
// ホストにサービス名を指定
$redis->connect('db', 6379);
$redis->incr('count');
?>
<html>
<head>
<title>Swarm sample</title>
</head>
<body>
<p>
<?= sprintf('表示回数:%d', $redis->get('count')); ?>
</p>
</body>
</html
web用Dockerfile
# Dockerfile
FROM php:latest
WORKDIR /usr/src/myapp
RUN pecl install -o -f redis \
&& rm -rf /tmp/pear \
&& docker-php-ext-enable redis
COPY . /usr/src/myapp
CMD ["php","-S", "0.0.0.0:8000"]
Docker Compose
# sample-stack.yml
version: '3'
services:
web:
image: web:latest
ports:
- "8000:8000"
deploy:
mode: replicated
replicas: 2
endpoint_mode: vip
placement:
constraints: [node.role!=manager, node.hostname!=ip-10-0-3-10]
networks:
- sample
db:
image: redis:alpine
networks:
- sample
deploy:
placement:
constraints: [node.hostname==ip-10-0-3-10]
networks:
sample:
external: true
swarmを構築
managerを構築
manager
を10.0.0.10
に構築します。
manager-ip-10-0-0-10$ sudo docker swarm init --advertise-addr=10.0.0.10
ノードを確認します。
manager-ip-10-0-0-10$ sudo docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
xxxxxxxxxxx * ip-10-0-0-10 Ready Active Leader 23.0.1
manager
が作成されています。
後述するworker
を追加するコマンドは、以下コマンドで取得できます。
manager-ip-10-0-0-10$ sudo docker swarm join-token worker
To add a worker to this swarm, run the following command:
docker swarm join --token {{トークン}} 10.0.0.10:2377
workerを構築
worker
を構築します。
10.0.1.10
のDocker Engineをswarmに参加させます。
10.0.1.10
でコマンドを発行します。
worker-ip-10-0-1-10$ docker swarm join --token {{トークン}} 10.0.0.10:2377
manager
でノードを確認します。
ノードが追加されています。
manager-ip-10-0-0-10$ sudo docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
xxxxxxxxxxx * ip-10-0-0-10 Ready Active Leader 23.0.1
xxxxxxxxxxx ip-10-0-1-10 Ready Active 23.0.1
同様に10.0.2.10
および10.0.3.10
のDocker Engineもswarmに参加させます。
10.0.2.10
のDocker Engineをswarmに参加させます。
worker2-ip-10-0-2-10$ docker swarm join --token {{トークン}} 10.0.0.10:2377
10.0.3.10
のDocker Engineをswarmに参加させます。
worker3-ip-10-0-3-10$ docker swarm join --token {{トークン}} 10.0.0.10:2377
manager
でノードを確認します。
manager-ip-10-0-0-10$ sudo docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
xxxxxxxxxxx * ip-10-0-0-10 Ready Active Leader 23.0.1
xxxxxxxxxxx ip-10-1-0-10 Ready Active 23.0.1
xxxxxxxxxxx ip-10-2-0-10 Ready Active 23.0.1
xxxxxxxxxxx ip-10-3-0-10 Ready Active 23.0.1
overlay ネットワークを作成
manager
でoverlay
ネットワークを作成します。
172.20.0.0/24
のoverlay
ネットワークをsampleという名前で作成します。
manager-ip-10-0-0-10$ sudo docker network create -d overlay --subnet 172.20.0.0/24 --attachable sample
web用イメージをビルド&各ノードに配布
manager-ip-10-0-0-10$ sudo docker build -f -t web:latest .
manager-ip-10-0-0-10$ sudo docker save web:latest | gzip > web-latest.tgz
SCPなどで各ノードにweb-latest.tgz
をコピーします。
10.0.1.10
、10.0.2.10
にコピー済み仮定します。
コピー先でロードします。
例として10.0.1.10
でロードします。
worker1-ip-10-0-1-10$ sudo docker load -i web-latest.tgz
以上で準備が整ったのでスタックをデプロイします。
デプロイ
デプロイの指示をDocker Composeで定義します。
sample-stack.ymlを再度記載します。
# sample-stack.yml
version: '3'
services:
web:
image: web:latest
ports:
- "8000:8000"
deploy:
mode: replicated
replicas: 2 # ---(1)
endpoint_mode: vip
placement:
constraints: [node.role!=manager, node.hostname!=ip-10-0-3-10] # ---(2)
networks:
- sample
db:
image: redis:alpine
networks:
- sample
deploy:
placement:
constraints: [node.hostname==ip-10-0-3-10] # ---(3)
networks:
sample:
external: true
以下のようにデプロイします。
(1) webサービスを2つ稼働します
(2) webサービスをmanager
ノード、ip-10-0-3-10
ノード以外に配置します
(3) dbサービスをip-10-0-3-10
ノードに配置します
manager
でスタックのデプロイコマンドを発行します。
manager-ip-10-0-0-10$ sudo docker stack deploy --compose-file sample-stack.yml sample
デプロイを確認します。
manager-ip-10-0-0-10$ sudo docker stack ps sample
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
xxxxxxxxxxx sample_web.1 web:latest ip-10-0-1-10 Running Running 12 seconds ago
xxxxxxxxxxx sample_web.2 web:latest ip-10-0-2-10 Running Running 12 seconds ago
xxxxxxxxxxx sample_db.1 redis:alpine ip-10-0-3-10 Running Running 14 seconds ago
webサービスが10.0.1.10
、10.0.2.0
、dbサービスが10.0.3.10
にデプロイされました。
動作確認
http://203.0.113.10:8000 にアクセスします。
以下のようにアクセス数が表示されます。
表示回数:1
アクセスするたびにカウンターがアップするのでwebサービスとdbサービスが連携していることを確認できます。
メリット
Docker Swarmでデプロイしました。
とても簡単なサンプルですがマルチホストを使用することのメリットについてまとめます。
可用性
可用性を確認するために10.0.1.10
のコンテナを強制的に停止してみます。
worker1-ip-10-0-1-10$ sudo docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
xxxxxxxxxxx web:latest "docker-php-entrypoi…" 4 hours ago Up 4 hours sample_web.1.xxxxxxxxxxx
worker1-ip-10-0-1-10$ sudo docker stop xxxxxxxxxxx
予想通りswarmが自動で新たなコンテナを起動しました。
worker1-ip-10-0-1-10$ sudo docker ps --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
xxxxxxxxxxx web:latest "docker-php-entrypoi…" 15 seconds ago Up 12 seconds sample_web.1.xxxxxxxxxxx
xxxxxxxxxxx web:latest "docker-php-entrypoi…" 4 hours ago Exited (137) 18 seconds ago sample_web.1.xxxxxxxxxxx
セキュリティ
外部に公開するサービスと公開の必要のないサービスを別サブネットに配置できました。
外部に公開する必要のないサービスをプライベートサブネットに配置することでセキュリティが高まりました。
まとめ
今回はマルチホストのコンテナ運用について理解を深めたくて、Docker Swarmを取り上げました。マルチホストの可用性などをコンテナレベルで確認することができてました。今後もオーケストレーションツールの理解を深めたいと思います。