Docker コンテキストとは?
Docker コンテキストは、ひとことで言うと、Docker CLI で入力したコマンド (docker
) の操作対象ホストを切り替えるための仕組みです。
例えば、Conoha
などの VPS サーバー や、Azure (ACI)、AWS (ECS) といったクラウドサービス上の Docker エンジンに対して、ローカルホストから
docker
コマンドを実行できるようになります。
カレントコンテキストは、docker compose use
コマンドで簡単に切り替えることができるので、ローカルでのコンテナのテストが終わったら、コンテキストを Azure (ACI) に切り替えてクラウド環境上で docker compose up
するといったことが簡単にできます。
当然、接続先のコンテナサービスごとに接続プロトコルは異なるのですが、Docker は標準で Azure や AWS をターゲットとしたコンテキストの作成に対応しており、実際に docker
コマンドを実行するときは接続プロトコルを意識しないで済むようになっています。
ここでは、より汎用的な SSH 接続を用いるコンテキストを作成し、リモートホスト上の Docker エンジンに対して Docker コマンドを実行してみます。
前提条件:
- リモートホストに Docker がインストールされていること
- リモートホストに SSH キーで SSH 接続できるようなっていること(参考: SSH の使い方)
- 接続先のユーザーが
sudo
なしでdocker
コマンドを実行できるようなっていること(参考: docker グループへの追加)
デフォルト・コンテキスト
Docker Desktop をインストールすると、デフォルトのコンテキストとして、default
という名前のコンテキストが作成されます。
Docker コンテキストの一覧を表示するには、docker context ls
コマンドを使用します。
$ docker context ls
NAME TYPE DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default * moby Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
default
コンテキストのエンドポイントは unix:///var/run/docker.sock
となっており、これはローカルホスト上の Docker デーモンに対して Docker コマンドを実行することを示しています。
リモートホストへの SSH 接続設定
SSH 接続に使用するユーザー情報などは、~/.ssh/config
ファイルにまとめて記述しておくと便利です。
というか、これを使わないと、細かい接続設定を行うのが困難なので作成しておきましょう。
Host conoha
Hostname example.com
Port 22
User maku
IdentityFile ~/.ssh/id_ed25519
ここでは、Conoha VPS
を使う想定で、設定名を
conoha
にしましたが、このあたりは自由に決めてください。
SSH キー(秘密鍵)にパスフレーズが設定されている場合は、SSH agent に秘密鍵とそのパスフレーズを設定しておく必要があります。
$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /Users/maku/.ssh/id_ed25519: (秘密鍵のパスフレーズを入力)
Identity added: /Users/maku/.ssh/id_ed25519 (maku@macbook.local)
SSH 接続用の Docker コンテキストを作成する
新しい Docker コンテキストを作成するには、docker context create
コマンドを使用します。
--docker
オプションを次のように指定すると、SSH 接続用のコンテキストを作成できます。
$ docker context create --docker "host=ssh://conoha" my-context
ssh://conoha
という接続 URI では、前述の ~/.ssh/config
で設定した名称を使用していることに注意してください。
ssh://maku@example.com:22
のように、明示的な URI を指定することもできます。
次のように実行すると、新しいコンテキスト my-context
が作成されていることを確認できます。
$ docker context ls
NAME TYPE DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default * moby Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
my-context moby ssh://conoha
この段階では、まだカレントコンテキストは default
のままです。
コンテキストを切り替えて Docker コマンドを実行する
使用する Docker コンテキストを切り替えるには、docker context use
コマンドを使用します。
# コンテキストを切り替え
$ docker context use my-context
my-context
# カレントコンテキストを確認(アクティブなコンテキストに * 印が付いている)
$ docker context ls
NAME ...(省略)...
my-context * ...(省略)...
default ...(省略)...
# 次のようにカレントコンテキスト名だけ表示することも可能
$ docker context show
my-context
あとは、いつも通り docker
コマンドを実行すると、SSH 接続先の Docker ホストで処理されるようになります。
$ docker container run --rm hello-world
例えば、リモートホスト側で次のような感じで Web サーバーを起動し、ローカルホスト側からアクセスできるようになります(もちろんポートが適切に開放されている必要があります)。
# リモートホスト上の Web サーバー(nginx コンテナ)を起動
$ docker container run --rm -d -p 8000:80 --name web nginx
# ローカルホストからアクセス
$ curl http://example.com:8000
# リモートホスト上の Web サーバーを停止
$ docker container stop web
上記の例では、あらかじめ docker context use
コマンドで使用するコンテキストを切り替えておきましたが、docker
コマンド実行時に --context
オプションでコンテキスト名を指定することもできます。
次のように実行すれば、各コンテキストで使用する Docker ホストの詳細情報を確認できます。
$ docker --context default info # デフォルトコンテキスト(ローカルホスト)で実行
$ docker --context my-context info # 今回作成したコンテキスト(リモートホスト)で実行
本番環境用のコンテキストは常に選択していると危険なので、--context
オプションでのみ使用した方がよいかもしれません。
$ docker --context production compose up -d
Docker コンテキストを削除する
必要なくなった Docker コンテキストは、docker context rm
コマンドで削除できます。
$ docker context use default # デフォルトコンテキストに戻しておく
$ docker context rm my-context # 不要なコンテキストを削除
$ docker context ls # コンテキストの一覧を確認
NAME ...(省略)...
default * ...(省略)...
バインドマウント時の注意
Docker コンテキストとバインドマウントを組み合わせて使用するときは、マウント時の source
パスの指定方法に注意する必要があります。
source
パスを下記例の ./meili_data
のように相対パスで記述すると、それは docker
コマンドを実行する PC 上のカレントパスとして展開されます。
version: "3.9"
services:
meilisearch:
image: "getmeili/meilisearch:v1.6"
container_name: meilisearch
ports:
- "7700:7700"
environment:
- MEILI_ENV=production
- MEILI_NO_ANALYTICS=true
- MEILI_MASTER_KEY
volumes:
- type: bind
source: ./meili_data
target: /meili_data
bind:
create_host_path: true # ./meili_data が存在しなければ作成
例えば、カレントディレクトリが /Users/maku/myproject
の状態で docker --context my-context compose up -d
と実行すると、my-context
コンテキストが指し示すリモートホスト上に /Users/maku/myproject
というディレクトリが生成されてしまいます(存在しなければ)。
このパスは、おそらくリモートホスト上では意味を持ちません。
このような振る舞いを防ぐには、source
パスを最初から絶対パスで指定する必要があります。
volumes:
- type: bind
source: /opt/meili_data # ターゲットホスト上の絶対パスを想定して記述
target: /meili_data
bind:
create_host_path: true
あるいは、バインドマウントではなく、ボリュームマウント を使ってしまうのが手っ取り早いです。 ボリュームマウントを使用すると Docker のシステムがデータを一元管理してくれるため、ファイルシステム上のパスに関連する問題が発生しなくなります。
version: "3.9"
services:
meilisearch:
image: "getmeili/meilisearch:v1.6"
container_name: meilisearch
ports:
- "7700:7700"
environment:
- MEILI_ENV=production
- MEILI_NO_ANALYTICS=true
- MEILI_MASTER_KEY
volumes:
- type: volume # ボリュームマウントを使用する
source: meili_data # 一番下で定義しているボリュームを指定
target: /meili_data
volume:
nocopy: true # ボリューム生成時にコンテナから内容をコピーしない
# 作成するボリュームの定義
volumes:
meili_data:
# name: meili_data # プロジェクト名のプレフィックスを付けたくないとき