こんにちは!クラウドインフラやSREの世界へようこそ。
今回は、インフラ構成管理やオーケストレーションの領域でも、知る人ぞ知る極上のツール「Docker Swarm」と、モダンなCI/CDの代名詞「GitHub Actions」を組み合わせた、「ゼロダウンタイムCI/CDパイプライン」の構築方法についてお話しします。
「Kubernetesはオーバースペックだけど、複数台のサーバーでコンテナを冗長化して、かつ絶対に落としたくない……!」
そんな現場の切実な願いを、驚くほどシンプルに、美しく解決してくれるのがDocker Swarmです。
これをマスターすれば、深夜のデプロイ作業で冷や汗をかくことも、ユーザーにエラー画面を見せることもなくなります。毎日のリリース作業が劇的に楽になりますよ。さあ、一緒にその深淵を覗いてみましょう!
—
1. なぜ「Docker Swarm」なのか? そしてゼロダウンタイムの思想
現代のコンテナオーケストレーションと言えばKubernetes(K8s)が王様ですが、セットアップの複雑さや学習コスト(いわゆるK8s steep learning curve)に心が折れそうになったことはありませんか?
Docker Swarmは、普段私たちが使っている`docker`コマンドの延長線上だけで動きます。追加の巨大なエコシステムを学ぶ必要はなく、Dockerさえインストールされていれば、数分でマルチホストのクラスタが組み上がります。
ゼロダウンタイム(無停止)更新のカラクリ
Swarmには、デフォルトで「ローリングアップデート(Rolling Update)」という強力な機能が備わっています。
例えば、3つのレプリカ(コンテナの複製)で動いているWebアプリをアップデートする場合、Swarmは以下のように振る舞います。
1. 古いバージョン(v1)のコンテナのうち、1つを優雅に停止(Drain / Stop)する。
2. その裏で、新しいバージョン(v2)のコンテナを1つ立ち上げる。
3. ヘルスチェックが成功し、トラフィックを受け付けられる状態になったことを確認する。
4. 残りの古いコンテナについても、1つずつ同様に置き換えていく。
この一連の流れにより、ユーザーからのリクエストを途切れさせることなく(=ゼロダウンタイム)、シームレスに新機能へ移行できるのです。
—
2. 基礎セットアップ:Swarmクラスタの初期化と準備
まずは、GitHub Actionsから指示を受け取る「Docker Swarmクラスタ」の土台を作ります。今回は概念をわかりやすくするため、手元にある1台のサーバー(またはローカル環境)でSwarmを初期化し、自分自身をワーカーとしても参加させる「シングルノード・Swarm」を例にします(本番では複数台のマネージャー/ワーカーノードで構成してください)。
ステップ1: Swarmモードの有効化
SSHでターゲットサーバーにログインし、以下のコマンドを叩くだけです。
docker swarm init –advertise-addr
たったこれだけで、このサーバーはDocker Swarmの「マネージャーノード」になりました。
ステップ2: ネットワークの準備(オーバーレイネットワーク)
コンテナ同士が安全に通信できるよう、Swarm専用のオーバーレイネットワークを作成します。
docker network create –driver overlay –attachable app-net
※ `–attachable` をつけておくと、後からデバッグ用などで単体のコンテナをこのネットワークに参加させやすくなるため、実運用では非常に重宝します。
—
3. 肝となるDocker Composeファイル(`docker-stack.yml`)
Docker Swarmでは、複数コンテナの定義に `docker-compose.yml` の拡張版である「Stackファイル」を使用します。ここで、先ほど解説した「ゼロダウンタイム」を実現するための極意(アップデート設定)を書き込みます。
プロジェクトのルートディレクトリに `docker-stack.yml` を作成しましょう。
version: ‘3.8’
services:
web:
# 実際にはGitHub PackagesやDocker Hubのイメージを指定します
image: ghcr.io/your-username/your-app:latest
# 稼働させるコンテナの数(冗長化)
deploy:
replicas: 3
# ★ここがゼロダウンタイムのキモ:アップデート戦略の設定
update_config:
parallelism: 1 # 一度に1つずつコンテナを更新
delay: 10s # コンテナ間の更新ウェイト(秒)
order: start-first # 古いものを消す前に、新しいものを先に起動する!
failure_action: rollback # 失敗したら自動ロールバック
# リソース制限の指定(SREの基本原則です)
resources:
limits:
cpus: ‘0.50’
memory: 512M
reservations:
cpus: ‘0.25’
memory: 256M
networks:
- app-net
ports:
- “80:80”
networks:
app-net:
external: true # 事前に作成したオーバーレイネットワークを利用
先行起動(`start-first`)の魔法
`order: start-first` を指定することで、Swarmは「新しいコンテナを完全に起動・ヘルスチェック完了させてから、古いコンテナを終了する」という挙動になります。これにより、リクエストの取りこぼしを理論上の極限までゼロに近づけることができます。
—
4. GitHub Actionsで極上のCI/CDパイプラインを構築する
いよいよ本丸です。コードをGitHubにプッシュ(またはmainブランチにマージ)した瞬間に、自動でビルドされ、SSH経由でSwarmクラスタが更新される魔法のパイプラインを作ります。
GitHubリポジトリの `Settings > Secrets and variables > Actions` に、以下の秘密情報を登録しておいてください。
- `SSH_HOST`: デプロイ先サーバーのIPまたはドメイン
- `SSH_USER`: SSHログインユーザー名(例: `ubuntu`)
- `SSH_PRIVATE_KEY`: サーバーにログインするための秘密鍵
- `CR_PAT`: GitHub Container Registry(GHCR)にログインするためのPersonal Access Token
ワークフローファイル (`.github/workflows/deploy.yml`)
name: Deploy to Docker Swarm
on:
push:
branches:
- main
同時実行制御:デプロイが渋滞しないようにする配慮
concurrency:
group: production
cancel-in-progress: false
jobs:
build-and-deploy:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
# 1. GitHub Container Registry (GHCR) へのログイン
- name: Log in to the Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.CR_PAT }}
# 2. Dockerイメージのビルドとプッシュ
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
# 3. サーバーへSSH接続し、Swarmスタックを更新する
- name: Deploy to Swarm via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
# 最新のイメージをプルする
docker pull ghcr.io/${{ github.repository }}:latest
# Stackを更新する(これが実行されると、Swarmが自動でローリングアップデートを開始します)
docker stack deploy –compose-file <(echo '${{ secrets.DOCKER_STACK_YML_CONTENT }}' ) --with-registry-auth app-stack
# 古くなった未使用イメージの削除(ディスク枯渇を防ぐSREの知恵)
docker image prune -f
(※補足: 上記スクリプト内では `docker-stack.yml` の内容をSecret等から渡すか、あらかじめサーバー側にファイルを配置しておき `docker stack deploy -c docker-stack.yml app-stack` と実行する形でも構いません)
サーバー側の基本コマンドは、たったこれだけです。
1. `docker pull` で最新イメージを落とす。
2. `docker stack deploy` でSwarmに差分を教える。
これだけで、Swarmのコントローラーが自律的に動きだし、クラスタ全体を無停止で新しいバージョンへ書き換えてくれます。
---
5. 動作確認とトラブルシューティングの極意
デプロイパイプラインが走ったら、サーバーにSSHで入り込んで、その美しい挙動を自分の目で確かめてみましょう。
サービスの稼働状況をリアルタイムで監視
docker service ls
各レプリカコンテナがどのように入れ替わっているかを確認
docker service ps app-stack_web
もしアップデート中にエラーや不具合が起きていても焦る必要はありません。先ほど設定した `failure_action: rollback` により、Swarmが自律的に健康なひとつ前のバージョンへ自動ロールバックしてくれます。
さらに、手動で緊急ロールバックしたい場合も、以下のコマンド一発で完了します。
docker service rollback app-stack_web
この「いざとなったら一瞬で巻き戻せる」という安心感こそが、エンジニアに心の平穏をもたらしてくれます。
—
おわりに
いかがでしたでしょうか?
Docker SwarmとGitHub Actionsを組み合わせることで、複雑なKubernetesの要塞を築くことなく、堅牢で美しいゼロダウンタイムCI/CDパイプラインが手に入りました。
「インフラはシンプルに、自動化は泥臭く確実に」。
この構成を一度あなたの環境に導入すれば、デプロイに対する恐怖心は綺麗さっぱり消え去り、新しいコードを世に送り出すことが純粋な楽しみに変わるはずです。
あなたの開発ライフが、この知見によってより豊かで快適なものになることを心から願っています。それではまた次の深淵でお会いしましょう!