1. Application Decoupling구성
예를들면 Web, WAS구성이 필요한 컨테이너의 경우, 하나의 컨테이너에 Web-WAS를 구성하는것 보단 애플리케이션 별로 2개의 컨테이너로 분리하여 구성하는 것이 좋다는 것이다.
애플리케이션별로 컨테이너를 분리하게 되면, 애플리케이션별로 스케일 인, 아웃, 업, 다운을 하게되어 필요한 애플리케이션에 대한 가용성을 신속하게 확보할 수 있을 것이고, 애플리케이션간 간섭 및 충돌도 최소화 할 수 있다는 장점이 있다.
2. Dockerfile의 Layer수를 최소화 한다.
Dockerfile은 각각의 명령어별로 실행되는 라인마다 Layer가 생겨 해당 Layer가 많아지면 많아질 수록 Size가 커지게 되고, 빌드하는 시간도 길어지게 된다.
그리고, &&연산자와, \ 인수를 활용하여 반복적인 명령을 하나의 레이어를 통해 실행시킬 수 있도록 한다.
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install -y redis-server && \
apt-get clean
EXPOSE 6379
CMD ["redis-server", "--protected-mode no"]
- FROM: 첫 번째 줄인 FROM은 새로운 이미지 레이어를 생성하는데, 레이어는 기본 이미지인 ubuntu:18.04를 기반으로 한다
- RUN: apt-get update 및 apt-get install -y redis-server 명령을 실행하여 Redis를 설치하는 레이어를 생성한다.
- EXPOSE: EXPOSE 명령은 포트를 노출시키는 역할을 하며, 이 명령은 이미지 레이어를 추가하지는 않지만, Docker 컨테이너가 이 포트를 외부로 노출할 것임을 표시한다.
- CMD: CMD 명령은 컨테이너가 시작될 때 실행되는 기본 명령을 지정한다. 해당 명령도 새로운 이미지 레이어를 생성하지 않지만, 컨테이너가 시작될 때 실행되는 기본 명령을 설정한다
가능한 Dockerfile내에 불필요한 Layer를 최소화 하여 Image Size를 작은 용량으로 구성할 수 있도록 한다.
3. 빌드할때 캐시를 활용한다.
Dockerfile은 이미지를 빌드할때 각 Layer별로 캐시를 활용할 수 있는 Image가 있는지 찾게 되는데, 캐시가 있다면 중복된 레이어를 만들지 않아 용량관리에 도움이 되고, 보다 빠른 Build가 가능해지게 된다.
4. 패키지버전을 지정한다.
간혹 베이스이미지를 지정할 때 Tag를 latest로 가져오는 경우가 있는데, 이는 해당 이미지의 버젼이 불분명 하기 때문에 버젼을 지정하여 Image를 Build하는 것이 좋다.
FROM node:latest # X
FROM node:13.12.0-alpine # O
5. 컨테이너가 아무때나 root를 사용하지 못하도록 한다.
FROM node:13.12.0-alpine
USER root
WORKDIR /app
ENV PATH /app/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
COPY package.json ./
RUN npm install --silent
COPY . ./
USER nobody
CMD ["npm", "start"]
위 Dockerfile을 보면 두번째 라인에 USER root가 있는데 해당 라인부터는 컨테이너 내부에서 명령어를 사용할 사용자는 root가 되는 것이다.(다만, Default가 root이기 때문에 지정하지 않아도 root로 실행됨.)
8번째 라인부터는 USER nobody 유저가 npm start 커맨드를 실행하게 된다.
패키지를 설치할때나, 내부 환경을 구성할 때는 root권한으로 명령어가 실행이 되어야겠지만, 단순히 어플리케이션 실행과 같은 커맨드는 별도 유저가 실행할 수 있도록 한다.
6. 멀티스테이지 빌드를 사용한다.
기존에는 하나의 Dockerfile에서 불필요한 파일들을 넣지 않고 layer수를 줄여가며 용량을 최소화 했다면 최근엔 멀티스테이지빌드라는 것을 통해 빌드하여 용량을 최소화 하는 방버을 많이 사용한다.
예를들면 아래와 같은 구성으로 빌드를 했다면,
FROM golang::1.11
WORKDIR /usr/src/app
COPY main.go .
CMD ['main']
최근엔 아래와 같이 Dockerfile내 두번의 빌드가 수행될 수 있도록 하여 정말 프로그램을 구동하기 위한 최소한의 구성만 가지고 빌드될 수 있도록 한다.
FROM golang:1.13-alpine as builder
WORKDIR /usr/src/app
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-s' -o main .
FROM scratch
COPY --from=builder /usr/src/app/main /main
CMD ["/main"]
테스트 결과 당연히 용량 차이는 큰 것으로 확인 된다.
멀티스테이지 방식을 사용하지 않았을때의 이미지 Size이다. Base Image와 main.go파일이 전부이나, 796MB로 적지 않은 것으로 보인다
멀티스테이지 방식으로 Dokerfile을 빌드했을때다
1.43MB로 확인된다. 이는 기존보다 1/556이나 줄인 것으로 확인된다.
'Docker' 카테고리의 다른 글
Dockerfile Best Practice #1 (0) | 2024.03.17 |
---|---|
Harbor Private Registry 설치 및 설정 (0) | 2023.07.20 |