時折、ある Docker コンテナの中から別の Docker 関連の何かに命令したい時があります。Docker コンテナ・ネットワークを経由して命令できれば楽のですが、そうもいかない時があります(完了したら exit する前提のイメージの使用など)。そういった時は Docker コンテナ内で新たに Docker を立ち上げるか、Docker コンテナ内から外の Docker デーモンに命令するかの二択になります。この記事では Docker コンテナ内から外の Docker デーモンに命令する方法を紹介します。ちなみに Docker outside of Docker でググるとこの方式について色々出てきます。
Docker コンテナ・ネットワークの理解 — Docker-docs-ja 17.06 ドキュメント
大筋はシンプルです。外側の Docker デーモンとやり取りする docker.sock を内側であるコンテナにマウント。コンテナ内から Docker Client を使ってマウントした docker.sock 経由で外側の Docker デーモンとやり取りします。例えば、次のコードでこれは実現できます。
# docker-compose.yml
version: "3"
services:
app:
build:
context: ./docker/php
volumes:
- ./:/work
# ここで docker.sock をマウントすると定義
- /var/run/docker.sock:/var/run/docker.sock
# docker-compose.yml に書いた ./docker/php 直下の Dockerfile
FROM php:7.4-fpm-buster
# 省略( PHP を構築する色々)
# Docker Client をインストール
ENV DOCKER_CLIENT_VERSION=latest
# API_VERSION と Docker 自身のバージョン対応は次のリンクを参照
# @see https://docs.docker.com/engine/api/
# とりあえず外の Docker Client と合わせておけば動きます。
ENV DOCKER_API_VERSION=1.39
# Docker Client になるバイナリを入手. ドキュメントが古いのでもっといいやり方があるかもしれません。
# バイナリから Docker CE のインストール — Docker-docs-ja 17.06 ドキュメント
# @see http://docs.docker.jp/engine/installation/linux/docker-ce/binaries.html
RUN curl -fsSL https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_CLIENT_VERSION}.tgz \
| tar -xzC /usr/local/bin --strip=1 docker/docker
これでコンテナ内部から外の Docker デーモンを操作できます。具体的には内部のシェルで docker hoge とコマンドを打って外の Docker を操作できるわけです。例えば PHP 上からは exec 系統で次の様に動かせます。
// docker ps | grep java | awk '{print $1}' でコンテナ名 java のコンテナのコンテナIDを取得
// ここでは docker exec ですが、もちろん docker run も使えます
echo shell_exec("docker exec $(docker ps | grep java | awk '{print $1}') java --version");
// openjdk 16-ea 2021-03-16
// OpenJDK Runtime Environment (build 16-ea+13-521)
// OpenJDK 64-Bit Server VM (build 16-ea+13-521, mixed mode, sharing)
この様に Docker 間で直接命令するやり取りができる様になります。