コンテナ環境の持続的な統合最適化、Drone CIの500%の高速化

11398 ワード

コンテナ環境でDrone+semantic releaseが実現した意味化持続的なワークフローの統合について説明したが、プレゼンテーションを容易にするために、ワークフローの中で最も重要ないくつかの一環しか与えられず、実際に使用すると最適化に値する点が少なくない可能性がある.
そこで今回は、このワークフローに基づいて、コンテナ環境における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
  • AWS S3 Cache
  • SFTP Cache
  • Google Cloud Storage Cache

  • ここで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後、ミラーが
  • staging
  • sha_c0558777

  • 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分未満で、比較的友好的です.