빌드 캐시 사용하기
빌드 캐시 사용하기
빌드 캐시를 잘 활용하면 이전 빌드의 결과를 재사용하고 불필요한 작업을 건너뛰어 빌드를 더 빠르고 효율적으로 만들 수 있어요. 이 가이드에서는 캐시 무효화가 어떻게 동작하는지 이해하고, Node.js 애플리케이션에서 빌드 캐시를 효과적으로 사용하는 법을 배워요.
출처: 문서
본문
설명
getting-started 앱을 위해 만든 다음 Dockerfile을 살펴볼게요.
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "./src/index.js"]
docker build 명령으로 새 이미지를 만들면 Docker는 Dockerfile의 각 지시문을 순서대로 실행하며, 각 명령마다 레이어를 만들어요. 각 지시문에 대해 Docker는 이전 빌드의 지시문을 재사용할 수 있는지 확인해요. 이전에 비슷한 지시문을 실행한 적이 있으면 다시 실행할 필요 없이 캐시된 결과를 사용해요. 이렇게 하면 빌드 과정이 더 빠르고 효율적이 되어 귀중한 시간과 리소스를 절약할 수 있어요.
빌드 캐시를 효과적으로 사용하면 이전 빌드의 결과를 재사용하고 불필요한 작업을 건너뛰어 더 빠른 빌드를 할 수 있어요.
캐시 사용을 최대화하고 리소스 집약적이고 시간이 오래 걸리는 재빌드를 피하려면 캐시 무효화(cache invalidation)가 어떻게 동작하는지 이해하는 게 중요해요. 캐시가 무효화될 수 있는 상황의 몇 가지 예는 다음과 같아요.
RUN지시문의 명령이 변경되면 그 레이어가 무효화돼요. Docker는 변경을 감지하고, Dockerfile의RUN명령에 수정이 있으면 빌드 캐시를 무효화해요.COPY나ADD지시문으로 이미지에 복사되는 파일에 변경이 생기면 무효화돼요. Docker는 프로젝트 디렉터리 안의 파일 변경을 계속 감시해요. 내용이나 권한 같은 속성의 변경이든, Docker는 이런 수정을 캐시 무효화의 트리거로 간주해요.- 한 레이어가 무효화되면 그 뒤의 모든 레이어도 함께 무효화돼요. 베이스 이미지나 중간 레이어를 포함해 이전 레이어 중 하나라도 변경으로 인해 무효화되면, Docker는 그에 의존하는 이후 레이어들도 무효화되도록 보장해요. 이렇게 해서 빌드 과정의 일관성을 유지하고 불일치를 방지해요.
Dockerfile을 작성하거나 편집할 때는 불필요한 캐시 미스가 없는지 주의해서, 빌드가 가능한 한 빠르고 효율적으로 실행되도록 해야 해요.
직접 해보기
이 실습 가이드에서는 Node.js 애플리케이션에서 Docker 빌드 캐시를 효과적으로 사용하는 방법을 배워요.
애플리케이션 빌드
-
Docker Desktop을 다운로드하고 설치해요.
-
터미널을 열고 이 샘플 애플리케이션을 클론해요.
$ git clone https://github.com/dockersamples/todo-list-app -
todo-list-app디렉터리로 이동해요.$ cd todo-list-app이 디렉터리 안에 다음 내용을 담은
Dockerfile파일이 있어요.FROM node:22-alpine WORKDIR /app COPY . . RUN yarn install --production EXPOSE 3000 CMD ["node", "./src/index.js"] -
다음 명령을 실행해 Docker 이미지를 빌드해요.
$ docker build .빌드 과정의 결과는 이래요.
[+] Building 20.0s (10/10) FINISHED첫 번째 줄은 전체 빌드 과정이 20.0초가 걸렸다는 뜻이에요. 첫 빌드는 의존성을 설치하느라 다소 시간이 걸릴 수 있어요.
-
변경 없이 다시 빌드해요. 이제 소스 코드나 Dockerfile을 변경하지 않고
docker build명령을 다시 실행해 보세요.$ docker build .명령과 컨텍스트가 그대로라면 첫 빌드 이후의 빌드들은 캐싱 메커니즘 덕분에 더 빨라져요. Docker는 빌드 과정에서 생성된 중간 레이어를 캐시해요. Dockerfile이나 소스 코드를 변경하지 않고 이미지를 다시 빌드하면 Docker는 캐시된 레이어를 재사용할 수 있어 빌드 과정을 크게 단축해요.
[+] Building 1.0s (9/9) FINISHED docker:desktop-linux => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 187B 0.0s ... => [internal] load build context 0.0s => => transferring context: 8.16kB 0.0s => CACHED [2/4] WORKDIR /app 0.0s => CACHED [3/4] COPY . . 0.0s => CACHED [4/4] RUN yarn install --production 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => exporting manifest이후 빌드는 캐시된 레이어를 활용해 단 1.0초 만에 완료됐어요. 의존성 설치 같은 시간이 오래 걸리는 단계를 반복할 필요가 없어요.
단계 설명 소요 시간(1번째 실행) 소요 시간(2번째 실행) 1 Load build definition from Dockerfile0.0초 0.0초 2 Load metadata for docker.io/library/node:22-alpine2.7초 0.9초 3 Load .dockerignore0.0초 0.0초 4 Load build context(컨텍스트 크기: 4.60MB)0.1초 0.0초 5 Set the working directory (WORKDIR)0.1초 0.0초 6 Copy the local code into the container0.0초 0.0초 7 Run yarn install --production10.0초 0.0초 8 Exporting layers2.2초 0.0초 9 Exporting the final image3.0초 0.0초 docker image history출력을 다시 보면, Dockerfile의 각 명령이 이미지의 새 레이어가 되는 걸 볼 수 있어요. 이미지에 변경을 가했을 때yarn의존성을 다시 설치해야 했던 걸 기억하시나요? 이 문제를 해결할 방법이 있을까요? 빌드할 때마다 같은 의존성을 다시 설치하는 건 별로 합리적이지 않잖아요. 이 문제를 해결하려면 정말 무효화할 필요가 없을 때는 의존성 캐시가 유효하게 유지되도록 Dockerfile을 재구성하면 돼요. Node 기반 애플리케이션에서 의존성은package.json파일에 정의돼요. 이 파일이 변경되면 의존성을 다시 설치하고 싶지만, 파일이 변경되지 않았다면 캐시된 의존성을 사용하고 싶을 거예요. 그러니 그 파일만 먼저 복사하고, 의존성을 설치한 다음, 나머지를 모두 복사하면 돼요. 그러면package.json파일에 변경이 있을 때만 yarn 의존성을 다시 만들면 돼요. -
package.json파일을 먼저 복사하고, 의존성을 설치한 다음, 나머지를 모두 복사하도록 Dockerfile을 업데이트해요.FROM node:22-alpine WORKDIR /app COPY package.json yarn.lock ./ RUN yarn install --production COPY . . EXPOSE 3000 CMD ["node", "src/index.js"] -
Dockerfile이 있는 같은 폴더에 다음 내용을 담은
.dockerignore파일을 만들어요.node_modules -
새 이미지를 빌드해요.
$ docker build .그러면 다음과 비슷한 출력이 보여요.
[+] Building 16.1s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 175B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/node:22-alpine 0.0s => [internal] load build context 0.8s => => transferring context: 53.37MB 0.8s => [1/5] FROM docker.io/library/node:22-alpine 0.0s => CACHED [2/5] WORKDIR /app 0.0s => [3/5] COPY package.json yarn.lock ./ 0.2s => [4/5] RUN yarn install --production 14.0s => [5/5] COPY . . 0.5s => exporting to image 0.6s => => exporting layers 0.6s => => writing image sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25 0.0s => => naming to docker.io/library/node-app:2.0 0.0s모든 레이어가 다시 빌드된 걸 볼 수 있어요. Dockerfile을 꽤 많이 변경했으니 당연한 결과예요.
-
이제
src/static/index.html파일을 변경해 봐요. (예: 제목을 "The Awesome Todo App"으로 변경) -
Docker 이미지를 빌드해요. 이번에는 출력이 조금 달라질 거예요.
$ docker build -t node-app:3.0 .그러면 다음과 비슷한 출력이 보여요.
[+] Building 1.2s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/node:22-alpine 0.0s => [internal] load build context 0.2s => => transferring context: 450.43kB 0.2s => [1/5] FROM docker.io/library/node:22-alpine 0.0s => CACHED [2/5] WORKDIR /app 0.0s => CACHED [3/5] COPY package.json yarn.lock ./ 0.0s => CACHED [4/5] RUN yarn install --production 0.0s => [5/5] COPY . . 0.5s => exporting to image 0.3s => => exporting layers 0.3s => => writing image sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda 0.0s => => naming to docker.io/library/node-app:3.0 0.0s먼저 빌드가 훨씬 빨라진 걸 눈치챘을 거예요. 여러 단계에서 이전에 캐시된 레이어를 사용하는 게 보여요. 좋은 소식이죠. 여러분은 빌드 캐시를 활용하고 있는 거예요. 이 이미지와 그 업데이트를 푸시하고 풀하는 것도 훨씬 빨라질 거예요.
이런 최적화 기법을 따르면 Docker 빌드를 더 빠르고 효율적으로 만들어, 더 빠른 반복 주기와 향상된 개발 생산성을 얻을 수 있어요.
추가 자료
다음 단계
이제 Docker 빌드 캐시를 효과적으로 사용하는 법을 이해했으니, 멀티 스테이지 빌드에 대해 배워 볼 준비가 됐어요.