【Docker】rootユーザー以外でDooD(Docker outside of Docker)を試してみる

こんにちは

今回は 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のソケットについても、手を動かしながら知ることができたので、今後も色々と応用が利くかなとも思いました

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA