【Docker】マルチステージビルドを試してみた

こんにちは

今回は以前から気になっていたDockerのマルチステージビルドを試してみたのでここに書きます

 

■マルチステージビルドとは

Dockerfile内で複数のビルドステージを定義することにより、最終的に利用するイメージの軽量化を実現できたり、開発環境と検証環境でDockerイメージ内の中身を調整できたりします

また、イメージを軽量化できることで、Dockerイメージの転送量を削減できたり、ECRの課金料金削減にも役立ちます

なお、マルチステージビルドを実現する際は、Dockerfile内で FROM を利用して区切っていきます

 

■構成

docker-composeを利用して、 Nginx + PHP-FPM + MySQL を構成しているものの中で、PHP-FPMに焦点を当ててみました

.
├── app
│   ├── Dockerfile
│   ├── conf
│   │   ├── disable_display_error.ini
│   │   └── zz-docker.conf
│   └── src
│       ├── common
│       │   └── db_connect.php
│       ├── css
│       │   └── user_list.css
│       ├── get_all_users.php
│       ├── healthchek.html
│       └── index.php
├── db
│   ├── Dockerfile
│   ├── conf
│   │   └── my.cnf
│   ├── data
│   └── sql
│       └── init.sql
├── docker-compose.yml
└── web
    ├── Dockerfile
    └── conf
        └── www.example.com.conf

 

■マルチステージビルド実装前の中身

今回大事なところはappの Dockerfile と docker-compose.yml になります

なので、まずはそれぞれ中身を確認します

 

・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 groupmod -g 1000 www-data
RUN usermod -u 1000 www-data

#ドキュメントルート作成
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

# ユーザー切り替え
USER www-data

#作業ディレクトリ設定
WORKDIR /var/www/vhosts/www.example.com/public_html

#ポート開放
EXPOSE 9000

→ FROM が1つのみであり、マルチステージビルドにはなっていません

 

・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
    restart: always  
    ports:
      - "9000:9000"
    
  db01:
    build:
      context: ./
      dockerfile: db/Dockerfile
    container_name: db01-container
    env_file: .env/db_env_file
    restart: always
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/conf/my.cnf:/etc/my.cnf
    ports:
      - "3307:3306"

 

また、この状態だとDockerイメージ容量は 500MB ほどになっています

nginx_php-fpm_mysql_multibuild_docker-app01   latest  1de25957b816   About an hour ago   500MB

 

■マルチステージビルドを実装する

それでは、appのDockerファイルでマルチステージビルドを実装します

今回はテストとして、devとproductionのステージを作成し、docker-compose の target でそれぞれ指定してどうなるのか確認してみます

 

まずはDockerファイルを下記へ修正します

#開発環境ステージのビルド
FROM php:8.0.30-fpm AS dev

#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 groupmod -g 1000 www-data
RUN usermod -u 1000 www-data

#ドキュメントルート作成
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

# ユーザー切り替え
USER www-data

#作業ディレクトリ設定
WORKDIR /var/www/vhosts/www.example.com/public_html

#本番環境ステージのビルド
FROM php:8.0.30-fpm-alpine3.16 AS prod

#ドキュメントルート作成
RUN mkdir -p /var/www/vhosts/www.example.com/public_html && \
    docker-php-ext-install pdo_mysql

#php.iniのコピー
COPY --from=dev /usr/local/etc/php/conf.d/disable_display_error.ini /usr/local/etc/php/conf.d/

#php-fpm用confファイルコピー
COPY --from=dev /usr/local/etc/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/

# ユーザー切り替え
USER www-data

#ポート開放
EXPOSE 9000

→ 新たに FROM php:8.0.30-fpm-alpine3.16 AS prod 配下を作成し、本番環境ではこちらを利用するようにします

なお、今回はベースイメージが dev と異なるので、ドキュメントルートの作成や、pdo_mysql のインストールは prod ステージでも実行する必要があります

※docker-compose.yml のtargetで何も指定しなければ、最後の prod ステージのイメージが利用されます

 

それでは、一度ビルドしてみます

docker-compose build app01 --no-cache

 

容量を確認してみます

nginx_php-fpm_mysql_multibuild_docker-app01     latest    7f1cf400b227   24 minutes ago   72.3MB

→ ベースをalpineに変更したので、500MB から 72MB まで容量が小さくなっています

 

次に docker-compose.yml で target を指定して、実行してみます

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
      target: prod
    container_name: app01-container
    volumes:
      - ./app/src:/var/www/vhosts/www.example.com/public_html
    restart: always  
    ports:
      - "9000:9000"
    
  db01:
    build:
      context: ./
      dockerfile: db/Dockerfile
    container_name: db01-container
    env_file: .env/db_env_file
    restart: always
    volumes:
      - ./db/data:/var/lib/mysql
      - ./db/conf/my.cnf:/etc/my.cnf
    ports:
      - "3307:3306"

→ app01サービス内で target: prod  を指定しています

 

利用されているイメージを確認してみます

docker-compose images app01
CONTAINER           REPOSITORY                                    TAG                 IMAGE ID            SIZE
app01-container     nginx_php-fpm_mysql_multibuild_docker-app01   latest              7f1cf400b227        72.3MB

→ prod ステージのイメージが利用されています

 

最後にdocker-compose.yml内の target を dev へ変更し、実行してみます

app01:
  build:
    context: ./
    dockerfile: app/Dockerfile
    target: dev
  container_name: app01-container
  volumes:
    - ./app/src:/var/www/vhosts/www.example.com/public_html
  restart: always  
  ports:
    - "9000:9000"

 

なお、変更した後はイメージの再ビルドが必要となり、再ビルドなしではそのまま prod ステージのものが利用され続けます

docker-compose down
docker-compose build app01 --no-cache
docker images | grep app01
nginx_php-fpm_mysql_multibuild_docker-app01  latest   3b17ec085d50   8 seconds ago    500MB

→ イメージの容量が 500MB へと戻りました

 

それでは、dev を指定した状態で起動します

docker-compose up -d
docker-compose images app01
CONTAINER           REPOSITORY                                    TAG                 IMAGE ID            SIZE
app01-container     nginx_php-fpm_mysql_multibuild_docker-app01   latest              3b17ec085d50        500MB

500MB となっているので、dev ステージのものとなっていますね

 

■所感

いかがでしたでしょうか

初めてマルチステージビルドをした割にはうまくいき、利便性も感じることができました

また、ビルドステージ毎に環境を分けることで、1枚のDockerfileを環境ごとに使いまわすことができ、docker-compose側でも target を指定するだけで、簡単にマルチ環境の構築ができるとも考えました

次はより実践的に Laravel などを利用して、マルチステージビルドを試していきたいと思います

コメントを残す

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

CAPTCHA