Help us understand the problem. What is going on with this article?

Docker MachineでMacからVPS上のDockerへアプリをデプロイしよう

More than 1 year has passed since last update.

はじめに

Docker Machineを使ったアプリのデプロイにて、VPSや自サバを対象とするケースの記事が少ない印象だったので本記事を書きました。

Docker Machineの機能を利用することで普段使いのローカルPCから直接(sshログイン無しに)VPSや自サバ上へDockerを構築、イメージとコンテナを管理、アプリをデプロイすることができます。

Docker Machineでのリモート化による嬉しい点には以下があります。

  • ローカルからできるので、対象のDockerホストサーバにログイン→ソースやイメージをPull、する必要がない。
  • サードパーティのデプロイツールに頼らずDockerの標準機能だけでアプリがデプロイできる。
  • 複数のDockerホストを扱うとき必要があるとき、すべてローカルから操作できる。

本記事では個人開発サービスやスタートアップサービスアプリなどのデプロイを前提とし、1つのConoha VPS上にDocker Composeでデプロイするまでを記載します。KubernetesやDocker Swarmを使った複数ホストが対象のクラスタリング利用は含まれていません。

前提・環境

Mac (ローカル側)

MacOS: High Sierra 10.13.6

Docker for Mac: Docker Engine, Docker Machineのバージョンはこのスクショ画像を参照
スクリーンショット 2018-11-25 16.43.42.png

Conoha VPS (サーバホスト側)

OS: Ubuntu 18.04 LTS (Bionic Beaver)

SSH公開鍵認証ログインできるユーザーを作成して設定

本記事ではrootユーザではVPSへ直接ログインできないことを前提とします。そのため、Docker Machineを経由して対象ホストへログインするためのSSH公開鍵認証のログインができるユーザーが必要です。

こちらの記事などを参照することでユーザ作成できます。

Generic DriverでVPS上にDockerホストを構築

以下のスクリプトを実行することでVPS上にDockerがインストールされ、1つのMachineとして構築されます。

create_docker_machine.sh
#!/bin/bash

IP=対象VPSのIPアドレス
USER=username
KEY=~/.ssh/id_rsa
NAME=conoha1 # Dockerホストサーバへの任意の名前

docker-machine create \
  --driver generic \
  --generic-ip-address=$IP \
  --generic-ssh-user $USER \
  --generic-ssh-key $KEY \
  $NAME

今回はDockerホストとなるサーバがVPSなので--driver genericを指定します。
ちなみに、公式ドキュメントを見ると、AWS、GCP、Azure、Degital Ocean、OpenStackとクラウドサービスごとにドライバーが用意されていることがわかります。

【注意】 visudoで設定を変更しないとエラーになる。

上記のdocker-machineコマンドを実行すると下記のようなエラーとなり失敗するかと思われます。

Running pre-create checks...
Creating machine...
(conoha1) Importing SSH key...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with ubuntu(systemd)...
Error creating machine: Error running provisioning: ssh command error:
command : sudo hostname conoha1 && echo "conoha1" | sudo tee /etc/hostname
err     : exit status 1
output  : sudo: no tty present and no askpass program specified

どうやらdocker-machineがVPSホストに入ってsudo実行しようとしてもパスワード入力が必要なので処理を続けられないようです。
ググってみると、こちらのissueで同様の話があがっており、最終的にこちらのgenericドライバーに関するドキュメントの更新に反映されています。

This user has to have password-less sudo privileges. If it's not the case, you need to edit the sudoers file and configure the user as a sudoer with NOPASSWD.

つまり、対象ユーザはsudo実行時にパスワード入力を必要としない設定にする必要があります。

私の場合、下記のように、sudoグループのユーザーはsudo実行時にパスワード入力不要の設定にしています。(この設定は当然、ユーザーを乗っ取られた場合、好き放題サーバを操作されてしまうので、設定した際にはユーザのログインしっぱなしなどを決してしないようにする必要があるでしょう。)

visudo
# Allow members of group sudo to execute any command
# %sudo ALL=(ALL:ALL) ALL # original setting
%sudo   ALL=(ALL) NOPASSWD:ALL

対象リモート先Dockerホストを操作できるかを確認

はじめにVPS上のDockerをMackから操作できるか確認しましょう。

コンテキスト指定

以下のコマンドでdockerコマンドがVPS上のDockerを指すように指定します。

$ NAME=conoha1 # docker-machine createで指定したホスト名
$ eval $(docker-machine env $NAME)
$ env | grep DOCKER # 指定した環境変数を確認

docker-machine env $NAMEは実行するとわかりますが、環境変数のexportをしているのでevalに食わせることで適用されます。
これでリモート先と繋がります。docker image lsなどを実行するとMac上のDockerではなくVPS上に配置されたDockerであることが確認できると思います。

コンテキストを解除

操作し終わったら以下のコマンドでコンテキストを解除しましょう。解除するとローカル上のDockerが対象に戻ります。

$ eval $(docker-machine env -u)

MacからDocker Composeでアプリをデプロイ

Docker ComposeでVPS上のDockerにアプリをデプロイしてみましょう。

本記事ではMySQLを利用するGo言語のAPIサーバアプリケーションを例とします。

サンプルアプリについて

ファイル構成はこのような感じです。

アプリ構成
.
├── Dockerfile
├── db
│   └── init
│       └── init.sql
├── deploy.sh
├── docker-compose.prod.yml
├── docker-compose.yml
├── go.mod
├── go.sum
├── main.go
└── scripts
    └── create_docker_machines.sh

Macのローカル上でdocker-compose build=>docker-compose upをするとDockerでアプリケーションが立ち上がる前提とします。

なので本記事ではDockerfilemain.goのコードの詳細は割愛します。main.goは環境変数を参照してMySQLに接続しHTTPサーバを立ち上げるアプリだとみなしてください。

docker-compose.prod.ymlで本番環境用上書き設定

普段のローカル開発ではdocker-compose.ymlのみを利用しますが、VPS環境(本番環境とします)にDocker Composeでデプロイする際はdocker-compose.prod.ymlという別ファイルを用意しdocker-compose.ymlの内容を上書きすることで環境の差異を補いましょう。

上記のサンプルアプリの例ではそれぞれ以下のようになります。

docker-compose.yml
version: '2'
services:
  api:
    build: .
    ports:
      - "5000:5000"
    depends_on:
      - db
    links:
      - db
  db:
    image: mysql:5.7.22
    ports:
      - "3306:3306"
    volumes:
      - "./db/init:/docker-entrypoint-initdb.d"
    environment:
      MYSQL_ROOT_PASSWORD: localmysqlpass
      MYSQL_DATABASE: superappdb
docker-compose.prod.yml
version: '2'
services:
  db:
    volumes:
      - mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: prodsecurepass
volumes:
  mysql_data:
    driver: local

mysqlイメージの使い方に関しては公式を参照。

prodの方のファイルでdbサービス(MySQL)のvolumeと環境変数を上書き変更しています。

注意点としてvolumeはDocker Machineでの操作であっても、ローカル(Mac)側ではなくリモート先(VPS)のホスト上のリソースがvolumeとなりコンテナにマウントされます。
上記のdocker-compose.prod.ymlの場合、Docker Composeのトップとしてdata volumeを明示的に定義しているので、実行するとリモート先ホスト内にvolumeが作成されデータが永続化されます。

ビルド&デプロイ用スクリプトを用意してデプロイ

デプロイ用のスクリプトを用意しましょう。上記のアプリ構成にあるdeploy.shがそれにあたります。
内容は以下になります。

deploy.sh
#!/bin/bash

set -eu

export DEPLOY_DOCKER_MACHINE="conoha1" # 設定したDocker Machineの名前
export DEPLOY_SERVICE="api" # ビルド対象のDocker Composeサービス

# Change docker context to production environment
eval $(docker-machine env $DEPLOY_DOCKER_MACHINE)

# Build, and then Deploy
docker-compose build --no-cache $DEPLOY_SERVICE
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up --no-deps -d $DEPLOY_SERVICE

# Reset docker context
eval $(docker-machine env -u)

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up --no-deps -d のように-fオプションを指定することでprod設定で上書きされ、アプリがリモート先ホストで立ち上がります。

./deploy.shでMacからデプロイ完了です!

参考

momotaro98
Gopher ʕ ◔ϖ◔ʔなWebエンジニアです。
https://momotaro98.github.io
rarejob
明治神宮にあるオンライン英会話サービスを提供するベンチャー
https://www.rarejob.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした