Multi-Stage build
Multi-Stage 포스팅
Docker 이미지의 목적은 실행이지, 빌드에 있지 않다. 컴파일이나 빌드 관련 명령어는 제외하자
- 과거에는 도커 이미지 생성 시 프로그램 빌드 후 도커 이미지 생성
- 현재에는 이미 프로그램 빌드가 마쳐진 결과물만 가지고 (COPY) 도커 이미지 생성
혹시라도 도커 이미지 내에서 프로그램 빌드(컴파일)을 한다면 멀티 스테이지 도입이 시급
- Java의 경우 `/gradlew clean build`, JS인 경우 `npm install`
- Java인 경우 `build` 디렉토리에 쌓인 `.class` 바이트코드 사이즈가 너무 크고 (.JAR 파일을 COPY해서 사용하는 이유)
- JS의 경우 `node_nodule` 사이즈가 너무 크다.
도커 이미지의 사이즈는 크면 클수록 해당 도커 이미지 실행을 하는 EC2의 메모리와 네트워크에 영향을 준다.
- 이미지 사이즈가 1GB라면 EC2는 적어도 그 이미지를 다운받을 수 있는 1GB 이상의 메모리가 필요
- 네트워크 대역폿 지원이 미흡한 저비용 EC2의 경우 도커 실행(CD 배포 완료)까지 리드타임이 길어진다.
따라서 Muti-Stage Build 를 통해 빌드 이미지(Stage) 따로 실행 이미지(Stage) 따로 Dockerfile에 정의한다.
아래는 빌드에 필요한 파일들을 압축 형태로 빌드한 stage와 실행하는 stage를 따로 정의하고 있다.
# Java 예) JDK -> as 'Build Stage' (node_module 같은거 필요없으니까)
# Java 예) JRE 만 가지고 돌리면되니까 -> as 'App Stage'
from node:18-alpine as buildstage
npm run build
...
from node:18-alpine as app
CMD ["node", "server.js"]
베이스 이미지 경량화
Alpine, Bullseye와 같은 제일 기반이 되는 OS 이미지가 존재한다.
Alpine의 경우 최대 2.7MB 일 정도로 정말 너무 작은 이미지이고, Bullseye의 경우 최대 50MB 정도로 필요한 라이브러리들이 미리 설치되어 있다.
단적인 예로는 Alpine은 /bin/sh 일반 쉘을 사용하지만 bullseye 는 /bin/bash Bash 바쉘을 사용한다.
- Alpine(2.7MB): 컨테이너용 특수 제작 리눅스 프로젝트 | Package Manager : `apk` | Shell: `/bin/sh`
- 리눅스 기반 이미지는 원래 glibc(GNU C library) + coreutils(GNU coreutils) 사용
- 알파일 리눅스 이미지는 대신 musl libc + Busybox 사용
- Bullseye(50MB) Debian 11 (그외 9, 10 버전 등 많은 게 존재) | Shell : `/bin/bash`
- Ubuntu(25MB) : Debian 기반 이미지 | Package Manager `apt` | Shells: `/bin/bash`
Rebuild your images often
이미지를 빌드할 때, Docker는 Dockerfile에 있는 명령어들을 하나하나 순서대로 실행한다. 각각의 명령어들이 검사되기 때문에
Docker는 새로운 이미지를 만드는 대신 재사용할 수 있는 캐시 이미지가 존재하는지 확인할 수 있다.
Dockder가 캐시하는 기본적인 규칙은 해당 포스팅에 설명되어 있다.
이미지를 빌드하면 그 순간의 이미지의 스냅샷을 저장한다. 즉, 모든 base image는 빌드에 사용된 라이브러리와 다른 sw가 포함된다는 말이다. 이미지를 최신 상태로 유지하고 보안을 유지하려면 업데이트된 종속성을 사용하여 이미지를 자주 빌드해야한다.
의존성의 최신 버전을 사용해서 빌드하기 위해서 `--no-cache` 옵션을 사용해서 cache를 사용하지 않아야한다.
docker build --no-cache -t my-image:my-tage .
.dockerignore 통해 불필요한 / 미사용 파일 변경에 다른 Rebuild 방지
Docker 이미지를 빌드하는 디렉토리에 .dockerignore 파일을 작성하는 것으로 `COPY` 나 `ADD` 에서 임의의 파일을 제외할 수 있다. 빼놓지 말고 `.dockerignore` 에 Dockerfile 을 넣어야한다.
Dockerfile 내 `COPY` 명령어가 있으면 매번 Dockerfile 이 바뀔때마다 Rebuild 하게 된다.
.dockerignore을 작성해서 필요없는 파일을 빌더에게 전달하지 않을 수 있고, 빌드 속도 향상시킬 수 있다.
.dockerignore 위치
context의 root에서 `.dockerfile`을 찾으므로 빌드 컨텍스트 제일 상단에 위치해야한다.
`.dockerignorer`가 있다면 `.dockerignore`에 적힌 파일과 디렉토리가 빌더에게 전달되기전에 제외된다.
만일 Dockerfile이 여러개라면 각 Dockerfile에 맞는 `.dockerignore`을 작성할 수 있다.
.dockerignore 여러개인 경우 이름 컨벤션
해당 파일을 Dockerfile과 동일한 위치에서 Dockerfile의 이름을 prefix로 설정
.
├── index.ts
├── src/
├── docker
│ ├── build.Dockerfile
│ ├── build.Dockerfile.dockerignore
│ ├── lint.Dockerfile
│ ├── lint.Dockerfile.dockerignore
│ ├── test.Dockerfile
│ └── test.Dockerfile.dockerignore
├── package.json
└── package-lock.json
이 경우 컨텍스트 루트에 위치한 `.dockerignore`보다 우선시 된다.
'ASAC 웹 풀스택 > DevOps' 카테고리의 다른 글
[Cloud] 클라우드 컴퓨팅 모델: IaaS, PaaS, SaaS (2) | 2024.11.03 |
---|---|
[Github Action] Github Action 동작 원리 및 워크플로 파일 이해하기 (0) | 2024.10.29 |
[Docker] Docker Workflow (1) | 2024.10.19 |
[Docker] Github Action으로 CI/CD - CD (1) | 2024.10.17 |
[Docker] Docker를 통한 어플리케이션 관리: 단일 / 다중 컨테이너 (0) | 2024.10.16 |