コンテナ環境の持続的な統合最適化、Drone CIの500%の高速化
11398 ワード
コンテナ環境でDrone+semantic releaseが実現した意味化持続的なワークフローの統合について説明したが、プレゼンテーションを容易にするために、ワークフローの中で最も重要ないくつかの一環しか与えられず、実際に使用すると最適化に値する点が少なくない可能性がある.
そこで今回は、このワークフローに基づいて、コンテナ環境におけるCIの最適化とスピードアップ方法を紹介します.方法自体はDroneを必ず使用することに限定されず、同じ考え方で他のCIツールに完全に適用できます.
最適化前のプロジェクトの概要
1つの生産環境の実際のプロジェクトを例にとると、プロジェクトの主な構造は以下の通りである.
これは比較的一般的なReactベースのフロントエンドプロジェクトであり、
Dockerfileはこう書いてあります
Dockerのマルチステージ構築機能であるnpmのインストールを使用し、コンパイルは第1ステージとして、コンパイル完了後にコンパイル結果
プロセスボトルネック分析
パブリッシュプロセスは、前述のGitflow+semantic releaseワークフローを直接適用します.この時点での1回のパブリケーションは比較的遅く、pushがmasterにstagingミラーを構築するのに9:18、semantic releaseがTag構築productionミラーを打つのに6:24であることがわかります.
この過程はいったいどこが遅いのか、Droneの構築過程で見ると、コンテナ構築の時間が90%以上かかり、npmはダウンロードインストール3000以上の依存が必要である一方、webpackのコンパイルも30 s程度かかり、ネットワークがさらに不安定であれば、待ち時間が長くなるに違いない.
もう一つの時間のかかる元凶も明らかで、semantic releaseが導入されたため、push masterとreleaseの2つの動作は2回のCIをトリガし、毎回CIはDockerミラーの構築を行ったが、実際には異常が発生しなければ、2つのDockerミラーは実際には同じコードであり、完全に一致しているはずだ.すなわちrelease時のミラー構築にかかる時間は無駄である.
他にもnpmの代わりにyarnを用いることができ,より速いソースを用いることができ,不要な依存を除去することができるなどの応用面の最適化があるが,これらは本論文の議論の範囲内ではなく,省略している.
キャッシュの導入による重複ダウンロードの削減
構築ごとに3000以上の依存をダウンロードすると、最も考えやすいのは当然これらの依存をキャッシュすることですが、このプロジェクトではnpmダウンロード/コンパイルがコンテナ構築の一環で発生し、キャッシュを導入するのは難しいです.まず、ダウンロード/コンパイルプロセスをコンテナからCIに移行し、CIによってダウンロード/コンパイルを完了し、結果をコンテナミラーにコピーします.
ダウンロード/コンパイルがCIに移行した上で、Droneが提供するキャッシュカードを直接使用することができ、現在、異なるファイルシステムによって、Droneが選択できるキャッシュカードは Volume Cache AWS S3 Cache SFTP Cache Google Cloud Storage Cache
ここでVolume Cacheを例にとると、
Volume Cacheプラグインの使用は簡単です.まず、
また注意Volumeを使用するにはDroneでRepoをTrustedに設定する必要があります.
このときのDockerfileはファイルコピーの部分しか残っていません
キャッシュを追加すると,構築時間は2:38に大幅に減少し,全体の消費時間は50%以上減少した.
Docker Tagによる重複構築の省略
以上のことから,push masterとreleaseによる繰返し構築が考えられ,同様にキャッシュにより除去できるかどうかが考えられる.これはもちろん理論的にも可能であるが,キャッシュが安定していないため,より汎用的な方法が必要である.
semantic releaseの流れの中でpush masterとreleaseの唯一の違いはreleaseがgit tagを追加したことである.git tagは本質的に特定のcommitへの参照にすぎず、commitレコードは変更されないため、push masterとreleaseが2回トリガーしたCIでは、最後のcommitは同じであり、
これに基づいて、push masterの構築では、
このプロセスは、次の
最後のcommitのhashが staging sha_c0558777
2つのTagは、releaseの後、ミラーに
ミラー用にTagを打つにはDocker-in-Dockerを使用し、privileged flag、すなわち
同時にdocker tagなどのコマンドはdocker daemonの起動に依存します.そうしないとエラーが発生します.
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
1つの方法はホストをマウントするdaemon
このように最適化するとreleaseの一環の時間が1分以内に短縮され、最終的な成果を見てみると、コードの提出からパブリケーションの完了まで、総時間は5分未満で、比較的友好的です.
そこで今回は、このワークフローに基づいて、コンテナ環境におけるCIの最適化とスピードアップ方法を紹介します.方法自体はDroneを必ず使用することに限定されず、同じ考え方で他のCIツールに完全に適用できます.
最適化前のプロジェクトの概要
1つの生産環境の実際のプロジェクトを例にとると、プロジェクトの主な構造は以下の通りである.
├── Dockerfile
├── dist/
├── node_modules/
├── package.json
└── src/
これは比較的一般的なReactベースのフロントエンドプロジェクトであり、
npm list | wc -l
で3952の依存が見られ、プロジェクトはwebpackを通じてdist
ディレクトリにパッケージされ、パッケージコマンドはnpm run build
にパッケージされる.最終的なdist
ディレクトリはDockerfile
を介してNginxのDockerミラー内にパッケージ化され、本番環境でパッケージ化されたミラーを直接実行すればよい.Dockerfileはこう書いてあります
FROM node:10 as build
WORKDIR /app
COPY . /app
RUN npm install
RUN npm run build
FROM nginx:1.15-alpine
COPY --from=build /app/dist /usr/share/nginx/html
Dockerのマルチステージ構築機能であるnpmのインストールを使用し、コンパイルは第1ステージとして、コンパイル完了後にコンパイル結果
dist
フォルダのみをコピーし、残りのコピーされていないファイルを破棄することで、パッケージ化されたミラーは23.2 MBにすぎず、導入に有利である.プロセスボトルネック分析
パブリッシュプロセスは、前述のGitflow+semantic releaseワークフローを直接適用します.この時点での1回のパブリケーションは比較的遅く、pushがmasterにstagingミラーを構築するのに9:18、semantic releaseがTag構築productionミラーを打つのに6:24であることがわかります.
この過程はいったいどこが遅いのか、Droneの構築過程で見ると、コンテナ構築の時間が90%以上かかり、npmはダウンロードインストール3000以上の依存が必要である一方、webpackのコンパイルも30 s程度かかり、ネットワークがさらに不安定であれば、待ち時間が長くなるに違いない.
もう一つの時間のかかる元凶も明らかで、semantic releaseが導入されたため、push masterとreleaseの2つの動作は2回のCIをトリガし、毎回CIはDockerミラーの構築を行ったが、実際には異常が発生しなければ、2つのDockerミラーは実際には同じコードであり、完全に一致しているはずだ.すなわちrelease時のミラー構築にかかる時間は無駄である.
他にもnpmの代わりにyarnを用いることができ,より速いソースを用いることができ,不要な依存を除去することができるなどの応用面の最適化があるが,これらは本論文の議論の範囲内ではなく,省略している.
キャッシュの導入による重複ダウンロードの削減
構築ごとに3000以上の依存をダウンロードすると、最も考えやすいのは当然これらの依存をキャッシュすることですが、このプロジェクトではnpmダウンロード/コンパイルがコンテナ構築の一環で発生し、キャッシュを導入するのは難しいです.まず、ダウンロード/コンパイルプロセスをコンテナからCIに移行し、CIによってダウンロード/コンパイルを完了し、結果をコンテナミラーにコピーします.
ダウンロード/コンパイルがCIに移行した上で、Droneが提供するキャッシュカードを直接使用することができ、現在、異なるファイルシステムによって、Droneが選択できるキャッシュカードは
ここでVolume Cacheを例にとると、
.drone.yml
は以下の通りである.ここの文法はDrone-v 1.0以上のバージョンに対応しており、公式部分の古いドキュメントとは異なる可能性があります.steps:
- name: restore-cache
image: drillster/drone-volume-cache
settings:
restore: true
mount:
- ./.npm-cache
- ./node_modules
volumes:
- name: cache
path: /cache
- name: npm-install
image: node:10
commands:
- npm config set cache ./.npm-cache --global
- npm install
- name: build-dist
image: node:10
commands:
- npm run build
- name: rebuild-cache
image: drillster/drone-volume-cache
settings:
rebuild: true
mount:
- ./.npm-cache
- ./node_modules
volumes:
- name: cache
path: /cache
volumes:
- name: cache
host:
path: /tmp/cache
Volume Cacheプラグインの使用は簡単です.まず、
/tmp/cache
を使用するホストのフォルダに対応するVolumeを宣言する必要があります.Volume Cacheプラグインのパラメータでは、mount
にキャッシュが必要なフォルダがリストされ、restore: true
はファイルをホストからコンテナにコピーするため、pipelineの先頭に、rebuild: true
は逆にpipelineの最後に配置されます.また注意Volumeを使用するにはDroneでRepoをTrustedに設定する必要があります.
このときのDockerfileはファイルコピーの部分しか残っていません
FROM nginx:1.15-alpine
COPY ./dist /usr/share/nginx/html
キャッシュを追加すると,構築時間は2:38に大幅に減少し,全体の消費時間は50%以上減少した.
Docker Tagによる重複構築の省略
以上のことから,push masterとreleaseによる繰返し構築が考えられ,同様にキャッシュにより除去できるかどうかが考えられる.これはもちろん理論的にも可能であるが,キャッシュが安定していないため,より汎用的な方法が必要である.
semantic releaseの流れの中でpush masterとreleaseの唯一の違いはreleaseがgit tagを追加したことである.git tagは本質的に特定のcommitへの参照にすぎず、commitレコードは変更されないため、push masterとreleaseが2回トリガーしたCIでは、最後のcommitは同じであり、
DRONE_COMMIT_SHA
は変更されない.これに基づいて、push masterの構築では、
DRONE_COMMIT_SHA
をDockerミラーの追加のTagとして使用することができ、releaseの一環では、DRONE_COMMIT_SHA
Tagのミラーに最終的なバージョン番号Tagを打つだけで、releaseの一環でミラーを最初から構築する必要はありません.このプロセスは、次の
.drone.yml
に対応する. - name: push-docker-staging
image: plugins/docker
settings:
repo: allovince/xxx
username: allovince
password:
from_secret: DOCKER_PASSWORD
tag:
- staging
- sha_${DRONE_COMMIT_SHA}
when:
branch: master
event: push
- name: semantic-release
image: gtramontina/semantic-release:15.13.3
environment:
GITHUB_TOKEN:
from_secret: GITHUB_TOKEN
entrypoint:
- semantic-release
when:
branch: master
event: push
- name: push-docker-production
image: plugins/docker
environment:
DOCKER_PASSWORD:
from_secret: DOCKER_PASSWORD
commands:
- docker -v
- nohup dockerd &
- docker login -u allovince -p $${DOCKER_PASSWORD}
- docker pull allovince/xxx:sha_$${DRONE_COMMIT_SHA}
- docker tag allovince/xxx:sha_$${DRONE_COMMIT_SHA} allovince/xxx:$${DRONE_TAG}
- docker push allovince/xxx:$${DRONE_TAG}
when:
event: tag
privileged: true
最後のcommitのhashが
c0558777
、releaseバージョンがv 1.0.9であると仮定し、push master後、ミラーが2つのTagは、releaseの後、ミラーに
v1.0.9
のTagを追加します.ミラー用にTagを打つにはDocker-in-Dockerを使用し、privileged flag、すなわち
privileged: true
が必要であることに注意してください.同時にdocker tagなどのコマンドはdocker daemonの起動に依存します.そうしないとエラーが発生します.
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
1つの方法はホストをマウントするdaemon
/var/run/docker.sock
であり、もう1つの方法はコンテナ内でdocker daemonを起動することである.私がここで使用しているのは後者であり、nohup dockerd &
に対応しているが、release段階ではdockerミラーのためにtagを追加し、生産環境に発表を通知するだけであるため、上記のcacheなどの段階は条件を省略することができる.結果は以下の通りです.このように最適化するとreleaseの一環の時間が1分以内に短縮され、最終的な成果を見てみると、コードの提出からパブリケーションの完了まで、総時間は5分未満で、比較的友好的です.