Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

Docker でデプロイをする方法

いくつかあるのですが、「これだ!」みたいな方法が意外と見当たらなかったので書きます。

Docker を使ったデプロイにはいくつか方法があります。

  • リモートへログインして Docker Hub 経由でイメージを取得してデプロイ
  • ローカルからリモートの docker daemon へデータを送ってデプロイ

1. リモートへ SSH 接続してデプロイ

これが一番お手軽です。

  1. ローカルでイメージを作成
  2. 作成したイメージをリモートリポジトリ(Docker Hub など)にプッシュ
  3. リモートのマシンへログイン
  4. イメージをリポジトリから pull
  5. pull したイメージをもとにコンテナを作成

メリット

まず、とても簡単にできます。リモートへログインして直接コンテナを生成するので、迷う所がありません。

また、リモート側で docker-compose.yml を設定できます。このため、リモートマシン上にあるディレクトリを volumes へマウントできます。

デメリット

最大のデメリットは手動であることです。ローカルでイメージを更新したあと、いちいちリモートへログインして手動で pull する必要があります。Docker Hub と組み合わせて CI/CD をうまく構成できればやれなくはないとは思いますが、そもそも次に紹介する手法をとればもっと楽に実現できます。

また、この方法はリモートリポジトリを経由しなければなりません。そのため、プライベートリポジトリの数など制限があります。たとえば、Docker Hub の無料プランではプライベートリポジトリは 1 つしか作れません。もちろん、Docker Hub 以外にもリポジトリは色々あり、自前で立てることもできます。そのため、頑張ればプライベートリポジトリをいくつも作ることができます。しかし、そもそも次に紹介する方法ならリモートリポジトリを経由することなくデプロイできます。

2. Docker Context を使ってデプロイ

Docker には Context という機能があります。Docker Context を使うと、リモートの docker daemon にコマンドを実行することができます。

$ docker --context remote ps
# リモートマシン上のコンテナ情報が取得できる
$ docker --context remote run nginx
# リモートマシンで nginxイメージのコンテナを立ち上げる
$ docker --context remote build .
# リモートの docker daemon に ビルドコンテキストのファイルが送信されて、リモートでイメージが作成される

しかも、コンテキストは簡単に作成できます。

$ docker context create remote --docker host=ssh://example.com --default-stack-orchestrator swarm

host には ~/.ssh/config に書かれた設定も利用できます。

docker-compose の host オプションでも似たことができます。

$ docker-compose --context remote up
$ # あるいはこれでも
$ docker-compose --host ssh://example.com up

context で一括管理をするとホストの変更が容易です。また、名前も自由につけられるのでわかりやすいです。そのため、利用できるのであれば host オプションより context をおすすめします*1

version: "3"
services:
  web:
    image: nginx
    volumes:
      - /usr/share/nginx:/usr/share/nginx:ro

上の docker-compose.yml を context や host オプションを使って立ち上げます。すると、リモートマシン上にコンテナが立ち上がります。また、コンテナの/usr/share/nginxにリモートマシンの/usr/share/nginxがマウントされます。

複数コンテナを同時に立ち上げるとエラーが起こる

たとえば、以下のように複数コンテナをまとめて立ち上げようとすると、SSHコネクションエラーが発生します。

version: '3'
services:
  serviceA:
    image: nginx
  serviceB:
    image: nginx
  serviceC:
    image: nginx
  # ...
  serviceZ:
    image: nginx

どうやら、コンテナの分だけSSHコネクションが張られるようで、SSHコネクション数の制限に引っかかるみたいです。結局、僕は解決できず、独立したファイルに分けてデプロイすることにしました。

いい感じにデプロイしてみる

以下のような構成でリモートへデプロイしてみます。

./
├ service-a
│ ├ docker-compose.prod.yml
│ └ docker-compose.yml
├ service-b
│ ├ docker-compose.prod.yml
│ └ docker-compose.yml
├ service-c
│ ├ docker-compose.prod.yml
│ └ docker-compose.yml
├ reverse-proxy
│ ├ docker-compose.prod.yml
│ └ docker-compose.yml
└ deploy.sh
  • service-a、b、cはそれぞれ独立したサービス
  • reverse-proxyを介して各サービスへアクセスする
  • コンフィグは docker-compose.yml + docker-compose.prod.yml を使う

このとき、以下のようなスクリプトを書きます。

#!/bin/bash
SCRIPT_DIR=$(cd $(dirname $0); pwd)

for dir in $(ls -d */ | sed -e 's!/!!'); do
  cd $SCRIPT_DIR/$dir
  files=""
  if [ -f "$SCRIPT_DIR/$dir/docker-compose.yml" ]; then
    files+="-f $SCRIPT_DIR/$dir/docker-compose.yml "
  fi
  if [ -f "$SCRIPT_DIR/$dir/docker-compose.prod.yml" ]; then
    files+="-f $SCRIPT_DIR/$dir/docker-compose.prod.yml "
  fi
  if [ -n "$files" ]; then
    docker-compose --context remote $files build
    docker-compose --context remote $files down
    docker-compose --context remote $files up -d
  fi
done

各ディレクトリに入ってデプロイを行っています。context オプションを外せば、そのままローカルでテストができます(適宜docker-compose.dev.yml を追加するなど調整は必要です)。

まとめ

デプロイ時には Docker Context を使いましょう。

$ docker context create remote --docker host=ssh://example.com --default-stack-orchestrator swarm
$ docker-compose --context remote up -d

*1:context に対応したのが 2020 年 6 月リリースのバージョンなので、バージョンによっては host オプションしか使えない可能性があります