-
Notifications
You must be signed in to change notification settings - Fork 2
Git action에서 도커 이미지 빌드 시간을 단축시켜보자

현재 Media 서버를 한번 배포하는데 걸리는 시간이 평균적으로 10분정도 걸리고 있다. 이는 다른 Api, Chat, Record 서버들에 비교했을 때, 약 5배 이상 오래걸리고 있다.
때문에 변경 사항이 생겨서 배포해서 운영 환경에서도 잘 돌아가는지 확인해야 할 때마다, 코드를 올리고 10분동안 가만히 앉아서 배포가 완료될 때까지 기다리고 있어야 했다.

- Docker 이미지 빌드가 오래 걸리고 있다.
- 미디어 서버에서 Mediasoup을 사용하고 있는데, 이게 설치하는데 시간을 많이 사용하고 있다.
- GitHub Actions는 매번 새로운 인스턴스를 생성하여 작업하기 때문에 Docker 레이어 캐싱이 제대로 활용되지 않는다.
- 멀티 스테이지 빌드 전략을 사용하고 있어 Base 이미지를 미리 만들어도 최종 이미지에 포함되지 않는다.
Docker 이미지의 레이어는 파일 시스템 변경사항과 명령어 실행 결과를 저장한 읽기 전용(read-only) 파일 시스템이다.
- 이미지의 구조적 구성 요소
- Docker 이미지는 여러 개의 읽기 전용 레이어로 구성된다.
- 각 레이어는 Dockerfile의 명령어(RUN, COPY, ADD)로 생성된다.
- 불변성
- 한 번 생성된 레이어는 변경되지 않는다.
- 변경이 필요한 경우 새로운 레이어가 생성된다.
- 종속성
- 각 레이어는 바로 아래 레이어에 의존하며, 최종 이미지는 모든 레이어가 병합된 형태이다.
FROM node:16-alpine
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
CMD ["node", "app.js"]
위 도커파일은 다음과 같은 레이어로 구성된다.
- 베이스 레이어
- 작업 디렉토리 레이어
- 파일 복사 레이어
- 의존성 설치 레이어
- 애플리케이션 레이어
- 메타데이터 레이어
Docker는 레이어 단위로 캐싱을 수행한다. 이전 빌드에서 변경되지 않은 레이어를 재사용함으로써 빌드 시간을 줄이고 자원을 절약할 수 있다.
- Dockerfile 실행 Docker는 Dockerfile의 각 명령을 순차적으로 실행하며, 이전 빌드 결과를 캐시로 확인한다.
- 명령과 결과 비교 각 명령이 이전 빌드에서 생성된 결과와 동일하면, 해당 레이어를 캐시에서 재사용한다.
- 변경사항 발생 시 캐시 무효화 이전 단계와 다른 입력이 발생하면 해당 단계부터 새로운 레이어가 생성된다.
- 명령어가 동일해야 한다
- 명령어가 동일한 경우에만 캐시를 재사용한다.
- 명령어 입력이 동일해야 한다
- COPY와 ADD는 입력 파일이 변경되면 캐싱되지 않는다.
- package.json이 변경되면 이후 레이어는 재사용되지 않는다.
COPY package.json ./
- 캐시된 결과가 존재해야 한다
- 이전 빌드 결과가 삭제되었거나 캐시를 비활성화(--no-cache)하면 캐시를 사용할 수 없다.
Docker는 Dockerfile의 각 명령어를 위에서 아래로 순차적으로 실행하며, 이전 명령의 결과를 기반으로 캐싱 여부를 결정한다. 때문에 명령어의 순서가 변경되면 Docker는 이전 캐시를 무효화 한다.
-
기존 Dockerfile (캐싱 활용 가능)
- COPY package.json ./ 단계에서 파일이 변경되지 않으면 RUN npm install은 캐싱을 재사용한다
COPY package.json ./ # 파일 복사
RUN npm install # 의존성 설치
COPY . . # 나머지 파일 복사
-
순서가 변경된 Dockerfile (캐싱 무효화)
- COPY . . 단계에서 전체 파일을 복사하므로 파일 중 하나라도 변경되면 캐시가 무효화된다.
- 이로 인해 RUN npm install도 새로 실행된다.
COPY . . # 나머지 파일 복사
RUN npm install # 의존성 설치
위와 같은 상황이 발생하는 이유는 다음과 같다.
- Docker는 명령어의 입력(파일, 환경 변수 등)과 결과를 기반으로 캐싱 여부를 결정한다.
- 순서가 바뀌면 이전 단계의 입력이 변경된 것으로 간주되어 캐시를 무효화한다.
- 파일 변경 가능성이 높은 명령어(COPY . .)를 앞에 배치하면, 그 이후 명령어들의 캐시가 전부 무효화된다.
Git action은 기본적으로 일회성 환경을 제공한다. 각 워크플로 실행 시 새로운 환경이 생성되며, 이전 빌드에서 생성된 Docker 캐시를 기본적으로 사용할 수 없다. 때문에 캐싱을 유지하려면 추가적으로 캐시를 외부에 저장하고 재사용하도록 구성해야 한다.
GitHub Actions는 Docker의 BuildKit기반 캐싱 메커니즘을 제공한다.
type=gha
설정을 통해 Docker 레이어를 GitHub Actions의 캐시 저장소에 저장하고, 이후 빌드에서 이를 재사용 할 수 있다.
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
cache-from: type=gha # 이전 빌드 캐시 불러오기
cache-to: type=gha,mode=max # 새 캐시 저장
push: true # 이미지 푸시 활성화
tags: my-docker-repo/my-app:latest


- Mediasoup 포트 매핑 문제
- swagger 같은 응답 코드에 다양한 응답 보여주기
- Sudo가 계속 비밀번호를 요청함
- Docker 이미지가 너무 크다
- Git action에서 도커 이미지 빌드 시간을 단축시켜보자
- Docker compose를 이용해서 메모리 사용률을 줄여보자
- 방송 녹화 시 CPU 과부하 문제를 해결해보자
- Release 브랜치? 너 필요해?
- 로딩이 너무 짧아…!
- NestJS ORM으로 무엇을 사용해야 할까?
- WebRTC를 이용한 1:N 스트리밍 서비스에서 시그널링 서버가 필요할까?
- 실시간 채팅 구현: 인메모리 방식을 선택한 이유
- MySQL 아키텍처 개선: DB 의존성 분리와 서버 역할 명확화
- 브라우저 창이 최소화되면 비디오 송출이 안된다…!
- Mediasoup 기본 개념
- DLTS와 Signaling
- Tell, Don't Ask (TDA) 원칙이란
- VPC(Virtual Private Cloud) 학습 정리
- 순환참조: A 서비스 ‐ B 서비스 vs. A 서비스 ‐ B 레포지토리
- Dto 메서드 전략
- WebRTC란?
- 자바스크립트 패키지 매니저(npm, yarn, pnpm)
- shadcn/ui을 이용해 UI 개발 생산성 높이기
- React 이벤트 핸들러 네이밍(on vs handle)
- React-router-dom의 createBrowserRouter을 사용해보기
- fetch vs axios