3

React アプリケーション ( github repo )の Google Cloud Build を高速化しようとしています。そのため、Cloud Build の公式ドキュメントで提案されているように、Kaniko Cache の使用を開始しました。

私のビルドプロセスのnpm install一部が実際にキャッシュされているようです。npm run buildただし、ソース ファイルが変更されていない場合もキャッシュされると予想していました。

私の Dockerfile:

# Base image has ubuntu, curl, git, openjdk, node & firebase-tools installed
FROM gcr.io/team-timesheets/builder as BUILDER   

## Install dependencies for functions first
WORKDIR /functions
COPY functions/package*.json ./

RUN npm ci

## Install app dependencies next
WORKDIR /
COPY package*.json ./

RUN npm ci

# Copy all app source files
COPY . .

# THIS SEEMS TO BE NEVER CACHED, EVEN WHEN SOURCE FILES HAVENT CHANGED
RUN npm run build:refs \
    && npm run build:production

ARG VCS_COMMIT_ID
ARG VCS_BRANCH_NAME
ARG VCS_PULL_REQUEST
ARG CI_BUILD_ID
ARG CODECOV_TOKEN

ENV VCS_COMMIT_ID=$VCS_COMMIT_ID
ENV VCS_BRANCH_NAME=$VCS_BRANCH_NAME
ENV VCS_PULL_REQUEST=$VCS_PULL_REQUEST
ENV CI_BUILD_ID=$CI_BUILD_ID
ENV CODECOV_TOKEN=$CODECOV_TOKEN

RUN npm run test:cloudbuild \
    && if [ "$CODECOV_TOKEN" != "" ]; \
        then curl -s https://codecov.io/bash | bash -s - -X gcov -X coveragepy -X fix -s coverage; \
    fi

WORKDIR /functions

RUN npm run build

WORKDIR /

ARG FIREBASE_PROJECT_ID
ARG FIREBASE_TOKEN

RUN if [ "$FIREBASE_TOKEN" != "" ]; \
       then firebase deploy --project $FIREBASE_PROJECT_ID --token $FIREBASE_TOKEN; \
    fi

ビルド出力:

BUILD
Pulling image: gcr.io/kaniko-project/executor:latest
latest: Pulling from kaniko-project/executor
Digest: sha256:b9eec410fa32cd77cdb7685c70f86a96debb8b087e77e63d7fe37eaadb178709
Status: Downloaded newer image for gcr.io/kaniko-project/executor:latest
gcr.io/kaniko-project/executor:latest
INFO[0000] Resolved base name gcr.io/team-timesheets/builder to builder 
INFO[0000] Using dockerignore file: /workspace/.dockerignore 
INFO[0000] Retrieving image manifest gcr.io/team-timesheets/builder 
INFO[0000] Retrieving image gcr.io/team-timesheets/builder 
INFO[0000] Retrieving image manifest gcr.io/team-timesheets/builder 
INFO[0000] Retrieving image gcr.io/team-timesheets/builder 
INFO[0000] Built cross stage deps: map[]                
INFO[0000] Retrieving image manifest gcr.io/team-timesheets/builder 
INFO[0000] Retrieving image gcr.io/team-timesheets/builder 
INFO[0000] Retrieving image manifest gcr.io/team-timesheets/builder 
INFO[0000] Retrieving image gcr.io/team-timesheets/builder 
INFO[0001] Executing 0 build triggers                   
INFO[0001] Resolving srcs [functions/package*.json]...  
INFO[0001] Checking for cached layer gcr.io/team-timesheets/app/cache:9307850446a7754b17d62c95be0c1580672377c1231ae34b1e16fc284d43833a... 
INFO[0001] Using caching version of cmd: RUN npm ci     
INFO[0001] Resolving srcs [package*.json]...            
INFO[0001] Checking for cached layer gcr.io/team-timesheets/app/cache:7ca523b620323d7fb89afdd0784f1169c915edb933e1d6df493f446547c30e74... 
INFO[0001] Using caching version of cmd: RUN npm ci     
INFO[0001] Checking for cached layer gcr.io/team-timesheets/app/cache:1fd7153f10fb5ed1de3032f00b9fb904195d4de9dec77b5bae1a3cb0409e4530... 
INFO[0001] No cached layer found for cmd RUN npm run build:refs     && npm run build:production 
INFO[0001] Unpacking rootfs as cmd COPY functions/package*.json ./ requires it. 
INFO[0026] WORKDIR /functions                           
INFO[0026] cmd: workdir                                 
INFO[0026] Changed working directory to /functions      
INFO[0026] Creating directory /functions                
INFO[0026] Taking snapshot of files...                  
INFO[0026] Resolving srcs [functions/package*.json]...  
INFO[0026] COPY functions/package*.json ./              
INFO[0026] Resolving srcs [functions/package*.json]...  
INFO[0026] Taking snapshot of files...                  
INFO[0026] RUN npm ci                                   
INFO[0026] Found cached layer, extracting to filesystem 
INFO[0029] WORKDIR /                                    
INFO[0029] cmd: workdir                                 
INFO[0029] Changed working directory to /               
INFO[0029] No files changed in this command, skipping snapshotting. 
INFO[0029] Resolving srcs [package*.json]...            
INFO[0029] COPY package*.json ./                        
INFO[0029] Resolving srcs [package*.json]...            
INFO[0029] Taking snapshot of files...                  
INFO[0029] RUN npm ci                                   
INFO[0029] Found cached layer, extracting to filesystem 
INFO[0042] COPY . .                                     
INFO[0043] Taking snapshot of files...                  
INFO[0043] RUN npm run build:refs     && npm run build:production 
INFO[0043] Taking snapshot of full filesystem...        
INFO[0061] cmd: /bin/sh                                 
INFO[0061] args: [-c npm run build:refs     && npm run build:production] 
INFO[0061] Running: [/bin/sh -c npm run build:refs     && npm run build:production] 

> thdk-timesheets-app@1.2.16 build:refs /
> tsc -p common


> thdk-timesheets-app@1.2.16 build:production /
> webpack --env=prod

Hash: e33e0aec56687788a186
Version: webpack 4.43.0
Time: 81408ms
Built at: 12/04/2020 6:57:57 AM
....

現在、キャッシュ システムのオーバーヘッドがあるため、速度の利点はないようです。

私は Dockerfiles に比較的慣れていないので、単純な行が欠けていることを願っています。

4

1 に答える 1

6

簡単な答え: キャッシュの無効化は困難です。

RUNDockerfileのセクションでは、任意のコマンドを実行できます。一般に、docker (ローカル キャッシングを使用する場合) または Kaniko は、このステップをキャッシュできるかどうかを決定する必要があります。これは通常、出力が決定論的であるかどうかを確認することによって決定されます。つまり、同じコマンドを再度実行した場合、以前と同じファイル変更 (最後の画像に対して) が生成されますか?

現在、この単純なビューは、キャッシュ可能なコマンドを決定するのに十分ではありません。なぜなら、どのコマンドにも、ローカル ファイルシステムに影響を与えない副作用 (ネットワーク トラフィックなど) がある可能性があるためです。curl -XPOST https://notify.example.com/build/XYZ成功または失敗したビルドを何らかの通知 API に投稿するためにを実行する場合、これはキャッシュされません。コマンドが管理者ユーザーのランダムなパスワードを生成し、それを外部データベースに保存している可能性があります。このステップも決してキャッシュしないでください。

一方、完全に再現npm run build可能であっても、ミニファイヤとバンドラの動作方法により、2 つの異なるバンドル パッケージが生成される可能性があります。たとえば、縮小されたビルドと醜いビルドが異なる短い変数名を持っている場合などです。結果のビルドは意味的には同じですが、バイトレベルではないため、このステップはキャッシュできますが、docker または kaniko はそれを識別する方法がありません。

キャッシュ可能な動作とキャッシュできない動作を区別することは基本的に不可能であるため、キャッシングで何度も何度も誤検知または誤検知という形で問題のある動作に遭遇します。

パイプラインを構築する際にクライアントに相談するとき、Docker が特定のステップで間違っていると判断した場合は、通常、Dockerfile をステージに分割するか、キャッシュ ミスまたはヒット ロジックをスクリプトに入れます。

Dockerfile を分割すると、基本イメージ (すべての依存関係とその他の準備手順を含む) が作成され、カスタム キャッシュ可能な部分が独自の Dockerfile に分割されます。後者は、前の基本イメージを参照します。これは通常、何らかの形式のテンプレートを配置する必要があることを意味します (たとえば、最初に を配置し、それを helm などのより複雑なシステムFROM ${BASE_IMAGE}を介してレンダリングするなど)。envsubst

それがユースケースに適していない場合は、スクリプトでロジックを自分で実装することを選択できます。どのファイルが変更されたかを調べるには、git diff --name-only HEAD HEAD~1. これを他のロジックと組み合わせることで、スクリプトの動作をカスタマイズして、特定のファイル セットが変更された場合にのみロジックを実行することができます。

#!/usr/bin/env bash
# only rebuild, if something changed in 'app/'
if [[ ! -z "$(git diff --name-only HEAD HEAD~1 | grep -e '^(app/|package.*)')" ]]; then
  npm run build:ref
  curl -XPOST https://notify.api/deploy/$(git rev-parse --short HEAD)
  // ... further steps ...
fi

このロジックを正確なニーズに合わせて簡単に拡張し、キャッシング ロジックを自分で完全に制御できます。非決定的な振る舞いに。

于 2020-12-10T14:33:37.850 に答える