こんにちは
今回は root ユーザー以外で DooD(Docker outside of Docker) を試してみたので、ここに書いておきます
※全体的に長文 + APPコンテナ用Dockerファイルは実行結果に従って何度か書き換えているので、その旨ご了承ください
■きっかけ
最近 サーバー内にDockerを入れて、そこでコンテンツを動かす機会が多くなっており、その際に Docker 内で Dockerの操作をする場合は、どうするのだろうと疑問に思ったからとなります
なお、ユースケースは今のところよく分かりませんが、プログラム側で他のdockerコンテナに対して、何らかの操作を実行するときなどでしょうか、、、
■DooD(Docker outside of Docker) とは
簡単に言うと、Docker コンテナ内に Docker CLI をインストールし、そのdockerコマンドを利用して、他のコンテナを操作することを指します
また、ホストマシンの Docker socket をマウントして利用するので、操作の際は コンテナA から ホストマシン の Docker socket を介して、コンテナBへアクセスします
そのため、コンテナA から ホストマシン の Docker socket へ対して接続を許可する必要があるので、もしコンテナAをrootユーザー以外(例:www-dataなど)で実行している場合は、
コンテナA内でGIDの調整などを実施必要が出てくるので、この辺りは後述します
なお、dockerコンテナ内でdockerコマンドを使う方法には、DooD のほかに DinD(Docker in Docker) も存在しますが、今回は扱わないので説明は割愛します
■構成
下記シンプルな構成で、docker-compose を利用します
ホストOS:AlmaLinux9
Web:Nginx
APP:PHP
■流れ
最終的には APPコンテナから root ユーザー以外で Webコンテナを操作したいので、まずは root ユーザーで DooD が実行できることを確認し、
その後、APPコンテナイメージで実行ユーザーを変更した後、再度 APPコンテナ から Webコンテナを操作するようにします
1. ホストOS上に Docker と docker-compose のインストール
2. Dockerファイルとdocker-compose.ymlの確認
3. APPコンテナからWebコンテナの操作
4. APP用Dockerファイルの修正
5. APPコンテナからWebコンテナの操作
■手順
1. ホストOS上に Docker と docker-compose のインストール
まずは下記記事を参考にして、ホストOS上に Docker と docker-compose のインストールします
【Docker】docker-composeを利用してVM上に独自ドメインでWebサイトを公開する
2. Dockerファイルとdocker-compose.ymlの確認
下記のような感じでdocker-composeを作成します
・docker-compose.yml
version: "3.9"
services:
web01:
build:
context: ./
dockerfile: web/Dockerfile
container_name: web01-container
volumes:
- ./app/src:/var/www/vhosts/www.example.com/public_html
depends_on:
- app01
ports:
- "80:80"
app01:
build:
context: ./
dockerfile: app/Dockerfile
container_name: app01-container
volumes:
- ./app/src:/var/www/vhosts/www.example.com/public_html
- /var/run/docker.sock:/var/run/docker.sock
restart: always
ports:
- "9000:9000"
→ /var/run/docker.sock:/var/run/docker.sock でホスト側の docker.sock とコンテナ側の docker.sock をマウントしています
これらをマウントすることで WebコンテナからホストOS上の docker.sock を介して、APPコンテナへ接続できるようになります
・web/Dockerfile
FROM nginx:1.25.0 # confファイルのコピー COPY ./web/conf/www.example.com.conf /etc/nginx/conf.d #ドキュメントルート作成 RUN mkdir -p /var/www/vhosts/www.example.com/public_html EXPOSE 80
→ 何の変哲もないwebイメージです
・app/Dockerfile
FROM php:8.0.30-fpm
#php.ini用confファイルコピー
COPY ./app/conf/disable_display_error.ini /usr/local/etc/php/conf.d/
#php-fpm用confファイルコピー
COPY ./app/conf/zz-docker.conf /usr/local/etc/php-fpm.d/
#ドキュメントルート作成
RUN apt-get update -y && \
apt-get install -y vim less && \
docker-php-ext-install pdo_mysql && \
mkdir -p /var/www/vhosts/www.example.com/public_html
COPY ./app/src /var/www/vhosts/www.example.com/public_html
# Docker CLI(静的バイナリ)
RUN curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-27.0.3.tgz \
| tar xz --strip-components=1 -C /usr/local/bin docker/docker
# Docker Compose v2 プラグイン
RUN mkdir -p /usr/local/lib/docker/cli-plugins && \
curl -fsSL "https://github.com/docker/compose/releases/download/v2.27.1/docker-compose-linux-x86_64" \
-o /usr/local/lib/docker/cli-plugins/docker-compose && \
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
#作業ディレクトリ設定
WORKDIR /var/www/vhosts/www.example.com/public_html
#ポート開放
EXPOSE 9000
→ Docker CLI と Docker Compose v2 プラグイン をインストールしています
なお、現時点では root が実行ユーザーになります
3. APPコンテナからWebコンテナの操作
まずは、コンテナイメージをビルドします
docker-compose build --no-cache
次にコンテナを起動します
docker-compose up -d
起動を確認します
docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0abf1c3d0952 nginx_php-fpm_docker-web01 "/docker-entrypoint.…" 12 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp, [::]:80->80/tcp web01-container f05e6004661e nginx_php-fpm_docker-app01 "docker-php-entrypoi…" 13 seconds ago Up 11 seconds 0.0.0.0:9000->9000/tcp, [::]:9000->9000/tcp app01-container
→問題なく起動してそうです
APPコンテナ内に入ります
docker exec -it f05e6004661e bash
dockerコマンドを実行します
docker --version
Docker version 27.0.3, build 7d4bcd8
→ 問題なく入ってそうですね
Webコンテナを操作してみます
docker exec web01-container nginx -v
root@f05e6004661e:/var/www/vhosts/www.example.com/public_html# docker exec web01-container nginx -v nginx version: nginx/1.25.0
→ 問題なく接続できてそうです
4. APP用Dockerファイルの修正
これまでにrootユーザーであれば問題なくコンテナ間で操作ができることを確認しましたが、セキュリティ的には root ユーザーでコンテナを実行することは非推奨となります
そのため、次は APPコンテナの実行ユーザーを www-data へ変更し、操作できるかどうか確認してみます
それに伴い、Dockerファイルを下記へ修正します
FROM php:8.0.30-fpm
#php.ini用confファイルコピー
COPY ./app/conf/disable_display_error.ini /usr/local/etc/php/conf.d/
#php-fpm用confファイルコピー
COPY ./app/conf/zz-docker.conf /usr/local/etc/php-fpm.d/
#ドキュメントルート作成
RUN apt-get update -y && \
apt-get install -y vim less && \
docker-php-ext-install pdo_mysql && \
mkdir -p /var/www/vhosts/www.example.com/public_html
COPY ./app/src /var/www/vhosts/www.example.com/public_html
# Docker CLI(静的バイナリ)
RUN curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-27.0.3.tgz \
| tar xz --strip-components=1 -C /usr/local/bin docker/docker
# Docker Compose v2 プラグイン
RUN mkdir -p /usr/local/lib/docker/cli-plugins && \
curl -fsSL "https://github.com/docker/compose/releases/download/v2.27.1/docker-compose-linux-x86_64" \
-o /usr/local/lib/docker/cli-plugins/docker-compose && \
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
RUN groupmod -g 1000 www-data
RUN usermod -u 1000 www-data
# ユーザー切り替え
USER www-data
#作業ディレクトリ設定
WORKDIR /var/www/vhosts/www.example.com/public_html
#ポート開放
EXPOSE 9000
→ gid 1000 と uid 1000 とwww-data とし、実行ユーザーも www-data へと変更しました
修正後、イメージを再ビルドします
docker-compose build app01 --no-cache
起動 + APPコンテナへ入ります
docker-compose up -d
docker container ps -a
docker exec -it c830f7e10ff1 bash
www-data@c830f7e10ff1:~/vhosts/www.example.com/public_html$
→ 実行ユーザーが www-data になってそうです
改めてWebコンテナを操作してみます
docker exec web01-container nginx -v
www-data@c830f7e10ff1:~/vhosts/www.example.com/public_html$ docker exec web01-container nginx -v permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.46/containers/web01-container/json": dial unix /var/run/docker.sock: connect: permission denied
→ permission denied になりました
★なぜ実行ユーザーを変更すると permission denied になったのか?
結論から話すと、コンテナ内の www-data ユーザーからホストOS側の /var/run/docker.sock へのアクセス権限不足となります
これを理解するためには、ホストOSとDockerのGID(UID)の関係を理解する必要があります
まず最初に、ホストOS上のGIDとDockerコンテナ上のGID(UID)は異なるものではありますが、Dockerの仕様上 ホストOS上のGID/UIDとコンテナ内のGID/UIDは同じ番号のまま共有されます
例:ホストOS上で、GID が 1000 であれば、コンテナ内でも GID が1000 になる
また、識別もグループ名ではなく GID が利用されるので、ホストOS上のグループ名とコンテナ内のグループ名を合わせたところで、GIDの番号が異なっていれば意味を成しません
それらを踏まえて、今回の構成を見ていきます
まずは、ホストOS上の /var/run/docker.sock の権限を確認すると、下記のように 660 であり、所有者がroot、グループが docker となっています
[root@dood01 nginx_php-fpm_docker]# ls -la /var/run/docker.sock srw-rw----. 1 root docker 0 Jul 11 07:50 /var/run/docker.sock
次に、ホストOS上で dockerグループの GID を確認すると、991 となっていることが分かりました
[root@dood01 nginx_php-fpm_docker]# cat /etc/group | grep docker docker:x:991:
上記により、Dockerコンテナ上で www-data ユーザーが GID 991 のグループに所属していれば、APPコンテナからホストOS上の /var/run/docker.sock へグループ権限でアクセスできるようになります
それでは、実際に設定を調整してみましょう
APP用Dockerファイルを下記へ更新します
FROM php:8.0.30-fpm
#php.ini用confファイルコピー
COPY ./app/conf/disable_display_error.ini /usr/local/etc/php/conf.d/
#php-fpm用confファイルコピー
COPY ./app/conf/zz-docker.conf /usr/local/etc/php-fpm.d/
#ドキュメントルート作成
RUN apt-get update -y && \
apt-get install -y vim less && \
docker-php-ext-install pdo_mysql && \
mkdir -p /var/www/vhosts/www.example.com/public_html
COPY ./app/src /var/www/vhosts/www.example.com/public_html
# Docker CLI(静的バイナリ)
RUN curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-27.0.3.tgz \
| tar xz --strip-components=1 -C /usr/local/bin docker/docker
# Docker Compose v2 プラグイン
RUN mkdir -p /usr/local/lib/docker/cli-plugins && \
curl -fsSL "https://github.com/docker/compose/releases/download/v2.27.1/docker-compose-linux-x86_64" \
-o /usr/local/lib/docker/cli-plugins/docker-compose && \
chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
RUN groupmod -g 1000 www-data
RUN usermod -u 1000 www-data
# GID 991 のdockerグループを作成し、www-data ユーザーをdockerグループへ追加する ★
RUN groupadd -g 991 docker && usermod -aG docker www-data ★
# ユーザー切り替え
USER www-data
#作業ディレクトリ設定
WORKDIR /var/www/vhosts/www.example.com/public_html
#ポート開放
EXPOSE 9000
→ 「RUN groupadd -g …」の行を追記しました
なお、より綺麗に書くのであれば、GIDのハードコーディングではなく、docker-compose.yml ファイル上で group_add を利用したほうがいいと思います
修正後、再度イメージを再ビルド + コンテナを起動します
docker-compose build app01 --no-cache
docker-compose up -d
5. APPコンテナからWebコンテナの操作
まずは、www-data ユーザーのグループを確認してみます
id -a
www-data@f491c7cd0510:~/vhosts/www.example.com/public_html$ id -a uid=1000(www-data) gid=1000(www-data) groups=1000(www-data),991(docker)
→ 991(docker) グループに所属しているので、OKですね
それでは、最後に APPコンテナからWebコンテナへ操作してみます
docker exec web01-container nginx -v
www-data@f491c7cd0510:~/vhosts/www.example.com/public_html$ docker exec web01-container nginx -v nginx version: nginx/1.25.0
→ エラーが出力されず、Nginx のバージョンが表示されたことを確認しました
■最後に
いかがでしたでしょうか?
少し長くなってしまいましたが、結構面白い試みだったかなと思います
また、自分的にもホストOSとDockerコンテナのGID/UID関連の話は苦手だったので、これを機にきちんと学ぶことができてよかったです
加えて、Dockerのソケットについても、手を動かしながら知ることができたので、今後も色々と応用が利くかなとも思いました