Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPコンテナのマルチステージビルドとキャッシュ戦略 / PHPerKaigi 2022

na_it_o
April 11, 2022

PHPコンテナのマルチステージビルドとキャッシュ戦略 / PHPerKaigi 2022

PHPerKaigi 2022のLT資料

na_it_o

April 11, 2022
Tweet

More Decks by na_it_o

Other Decks in Technology

Transcript

  1. レイヤーキャッシュ . ├── Dockerfile ├── app.js ├── bin/ ├── package.json

    ├── package-lock.json ├── public/ ├── routes/ └── views/ # syntax=docker/dockerfile:1 FROM node:16 # Create app directory WORKDIR /usr/src/app # Install app dependencies # copy package.json AND package-lock.json COPY package*.json ./ RUN npm install # Bundle app source COPY . . EXPOSE 8080 CMD [ "node", "server.js" ]
  2. レイヤーキャッシュ . ├── Dockerfile ├── app.js ├── bin/ ├── package.json

    ├── package-lock.json ├── public/ ├── routes/ └── views/ # syntax=docker/dockerfile:1 FROM node:16 # Create app directory WORKDIR /usr/src/app # Install app dependencies # copy package.json AND package-lock.json COPY package*.json ./ RUN npm install # Bundle app source COPY . . EXPOSE 8080 CMD [ "node", "server.js" ] update use cache re-build
  3. Best Practices for writing Dockerfiles Best practices for writing Dockerfiles

    | Docker Documentation https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ Use multi-stage builds - if your build contains several layers, you can order them from the less frequently changed (to ensure the build cache is reusable) to the more frequently changed Don’t install unnecessary packages Minimize the number of layers
  4. こうなった # syntax=docker/dockerfile:1 FROM composer:2.2 as dep COPY composer.* /app/

    RUN --mount=type=cache,target=/tmp/cache/files \ composer install \ --no-dev \ --ignore-platform-reqs \ --no-interaction \ --prefer-dist \ --no-plugins \ --no-scripts \ --no-autoloader COPY ./ /app/ RUN composer dump-autoload \ --no-dev \ --optimize FROM php:8.1-apache ENV WWWUSER www-data ENV APP_ROOT /app ENV APACHE_DOCUMENT_ROOT /app/public RUN apt-get update && apt-get install -y \ libicu-dev \ libzip-dev \ && rm -rf /var/lib/apt/lists/* RUN NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \ docker-php-ext-install -j${NPROC} intl opcache pdo_mysql zip RUN pecl install redis apcu xdebug && \ docker-php-ext-enable redis RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' \ /etc/apache2/sites-available/*.conf RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' \ /etc/apache2/apache2.conf /etc/apache2/conf-available/* COPY --from=dep /app/ ${APP_ROOT} RUN chown -R ${WWWUSER}:${WWWUSER} ${APACHE_DOCUMENT_ROOT} \ ${APP_ROOT}/storage WORKDIR ${APP_ROOT} ↩
  5. 大方針 1. composerで準備する 2. 結果だけ持ってくる # stage-0 FROM composer:2.2 AS

    dep COPY composer.* /app/ RUN composer install # stage-1 FROM php:8.1-apache COPY --from=dep /app/vendor/ \ /var/www/html/vendor/ COPY . /var/www/html/
  6. レイヤーキャッシュを効かせる COPYでcomposer.json, composer.lockだけ 持ってくることでアプリケーションの変更 時はキャッシュが使われる # stage-0 FROM composer:2.2 AS

    dep COPY composer.* /app/ RUN composer install # stage-1 FROM php:8.1-apache COPY --from=dep /app/vendor/ \ /var/www/html/vendor/ COPY . /var/www/html/
  7. これだけだとERRORになる $ docker build -t app:latest . => ERROR [dep

    3/3] RUN composer install ∵ scriptが走っている # syntax=docker/dockerfile:1 FROM composer:2.2 AS dep COPY composer.* /app/ RUN composer install FROM php:8.1-apache COPY --from=dep /app/vendor/ \ /var/www/html/vendor/ COPY . /var/www/html/
  8. ダメだった対処 scriptsの依存関係を解消する ./app/の中まで見ている ……沼 # syntax=docker/dockerfile:1 FROM composer:2.2 AS dep

    COPY artisan /app/ COPY bootstrap/app.php \ /app/bootstrap/ COPY app/Console/Kernel.php \ /app/app/Console/ COPY app/Exceptions/Handler.php \ /app/app/Exceptions/ COPY composer.* /app/ RUN composer install FROM php:8.1-apache COPY --from=dep /app/vendor/ \ /var/www/html/vendor/ COPY . /var/www/html/
  9. 良さそうな対処 要はscriptを後回しにできればいい 1. --no-scripts --no-autoloaderオプション でダウンロードだけやってしまう 2. その後にまるっとファイルをCOPYして composer dump-autoload

    3. appイメージの方もvendor以外もCOPY するように変更 # syntax=docker/dockerfile:1 FROM composer:2.2 AS dep COPY composer.* /app/ RUN composer install \ --no-scripts \ --no-autoloader COPY ./ /app/ RUN composer dump-autoload FROM php:8.1-apache COPY --from=dep /app/ /var/www/html/
  10. 結論 # syntax=docker/dockerfile:1 FROM composer:2.2 as dep COPY composer.* /app/

    RUN --mount=type=cache,target=/tmp/cache/files \ composer install \ --no-dev \ --ignore-platform-reqs \ --no-interaction \ --prefer-dist \ --no-plugins \ --no-scripts \ --no-autoloader COPY ./ /app/ RUN composer dump-autoload \ --no-dev \ --optimize FROM php:8.1-apache ENV WWWUSER www-data ENV APP_ROOT /app ENV APACHE_DOCUMENT_ROOT /app/public RUN apt-get update && apt-get install -y \ libicu-dev \ libzip-dev \ && rm -rf /var/lib/apt/lists/* RUN NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \ docker-php-ext-install -j${NPROC} intl opcache pdo_mysql zip RUN pecl install redis apcu xdebug && \ docker-php-ext-enable redis RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' \ /etc/apache2/sites-available/*.conf RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' \ /etc/apache2/apache2.conf /etc/apache2/conf-available/* COPY --from=dep /app/ ${APP_ROOT} RUN chown -R ${WWWUSER}:${WWWUSER} ${APACHE_DOCUMENT_ROOT} \ ${APP_ROOT}/storage WORKDIR ${APP_ROOT} ↩ 本番環境向けに--no-devとか--optimizeとかい れたり、拡張とかApacheの設定とか諸々加えて いくとこんな感じになりそう (.dockerignoreも使おうね!)
  11. CI/CDのキャッシュ • レイヤーキャッシュ ◦ 有料だったりする、、、 • ファイルキャッシュ ◦ BuildKitのmountはこれでいける ◦

    Dockerのレイヤー • アーティファクト ◦ イメージやパッケージを固めて 残しておけるか? actions/cache - https://github.com/actions/cache/blob/main/examples.md#php---composer docker/build-push-action - https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md#local-cache
  12. 変更タイミング 1. ソースコードを変更した時 2. パッケージを更新した時 3. ベースイメージが更新された時 2, 3 はツール(depedabotとか)にお任せ

    Dockerfileは 1 > 2 > 3 の頻度で更新がある想定 保守フェーズに入いると頻度が 1 < 2 になってくるかも? Dockerfileも見直せる?
  13. まとめ • レイヤーキャッシュを上手に使ってさくさくビルド ◦ composerはダウンロードとスクリプト処理を分ける • マルチステージビルドで最終イメージは小さく • ファイルキャッシュも有効 •

    CI/CDではファイルキャッシュ • BuildKitでリモートのイメージをキャッシュとして使える • ツールにまかせて継続的にイメージを更新