Search
Duplicate
📒

[Docker Container] 04-2. Dockerfile 경량화(alpine, scratch, multistage build)

상태
미진행
수업
Docker Container
주제
Docker
4 more properties
참고

Dockerfile 최적화

NOTE
도커 파일의 최적화 ⇒ 경량의 컨테이너 서비스 제공!
이미지에서 최적의 환경을 제공하기 위한 요소
빌드시간
이미지 크기
재사용성
보안
유지보수성 등
경량의 컨테이너 서비스란?
도커에서 제공하는 베이스 이미지들의 지향점에 필요한 최소한의 파일만 보유한다.
이미지에 불필요한 파일을 포함하지 않기 위해 .dockerignore을 사용한다.

최적화 방안

NOTE
1.
컨테이너 이미지에 불필요한 바이너리 모두 제거
불필요한 패키지 설치를 피하고, 설치된 패키지 파일은 autoremove, clean 등의 명령으로 제거한다.
ex) apt install -y --no-install-recommands <패키지명>
2.
Docker에서 제공하는 최소 이미지인 alpine linux, scratch를 사용한다.
scratch 이미지에 Go와 같은 언어를 사용하여 정적 링크 바이너리를 만들어서 Dockerfile에 참조하면 경량화가 가능하다.
Alpine Linux를 사용하면 최종 이미지 크기와 복잡성을 줄이는데 도움이 된다!
3.
Multi-stage-build를 사용하여 최종 이미지 크기를 최소화한다.
2개의 베이스 이미지(FROM)를 사용한다.
Dokcerfile의 From이 2개이상이면 분리된 작업 공간(stage 제공)
1번째 stage에서 생성된 실행파일 등을 2번째(배포 이미지)에 제공
4.
레이어 최소화 (명령어 그룹화 개념)
RUN 명령어를 한번에 실행시켜 레이어를 줄인다.
레이어는 최대 127개까지만 가질 수 있다.
새로운 레이어 생성 명령어 ⇒ FROM, RUN, COPY, ADD, ENV, LABEL
다른 명령어는 build 과정에서 이미지 생성하고 사라진다.

도커파일 최적화 핵심

NOTE
하나의 어플리케이션, 하나의 컨테이너
하나의 컨테이너에 2개 이상의 어플리케이션을 설정하면 결합성이 높고, 확장성이 낮다.
하나의 컨테이너에 하나의 애플리케이션 동작을 보장해야함
이러한 설계는 MSA 지향적 설계를 고려해야 장애가 발생해도 애플리케이션 자체가 유지될 수 있게된다.
여러 컨테이너로 나눠야 재사용성이 좋다.
3-Tier ⇒ Front, Back, Database 컨테이너 분리
많은 수의 프로세스가 컨테이너 내에서 실행된다면 그만큼 CPU, Memory 소비가 커진다.
캐쉬를 사용하자
도커는 이미지를 만들때 레이어 개념을 사용해 데이터를 저장하고, 저장된 레이어가 있다면 재사용한다.
이미지를 빌드하면 각 명령어 단위로 캐싱이 이루어진다.
동일한 명령의 실행은 캐싱을 통해 재사용되어 빌드가 빨라진다.
캐싱 효과를 높이기 위해 명령어의 위치를 명확히 해야한다.
일정하게 유지될 명령은 Dockerfile의 위쪽에 배치하고,
변경될 수 있는 명령은 아래쪽에 배치하면 기존 캐시를 부정할 가능성이 줄어든다.
보안 강화
Dockerfile의 사용자를 지정하지 않으면 root를 사용한다 (별로 권장되지 않음)
USER 명령어 사용해서 root 못쓰게 막을 수 있다.
커널 정보를 공유하기에 잠재적 보안 문제를 가진다, Docker Host에 대한 권한이 쉽게 상승하게됨
이미지 서명
도커 매니페스트의 서명을 확인하여 신뢰할만한가 확인하며 위조되지 않았나 검증
익숙하지 않은 이미지를 사용하는 경우 Dive를 사용해 이미지 검사
SETUID 및 SETGID 비트가 있는 모든 바이너리를 찾아서 제거
이러한 바이너리는 권한을 escalation하는데 사용됨

다양한 Dockerfile build

이미지 빌드과정

NOTE
# IMAGE_NAME:TAG -> -t 생성할 이미지명:버전 # DOCKERFILE_NAME -> Dockerfile이 아닌 다른 파일명인 경우 -f # DOCERFILE_LOCATION -> 현재 경로면 "." | 다른 경로 명시 docker build -t IMAGE_NAME:TAG [-f DOCKERFILE_NAME] DOCKERFILE_LOCATION
Bash
복사
EXPOSE, CMD는 실제로 레이어를 생성하지 않음(걍 이미지용이다.)
docker run을 실행하면 이제 수정이 가능해진다.

Dockerfile 경량화 (패키지 최소화, alpine 사용)

NOTE

1. 기본 Dockerfile

FROM ubuntu:14.04 MAINTAINER "kevin-lee <hylee@dshub.cloud>" LABEL "purpose"="webserver practice" RUN apt update && apt install apache2 -y WORKDIR /var/www/html COPY index.html . EXPOSE 80 CMD apachectl -D FOREGROUND
Docker
복사
원본 그대로 사용
28.3초

2. clean, autoremove를 통한 패키지 최소화

FROM ubuntu:14.04 MAINTAINER "kevin-lee <hylee@dshub.cloud>" LABEL "purpose"="webserver practice" RUN apt update && apt install apache2 -y && \ apt clean autoclean && \ apt autoremove -y && \ rm -rfv /tmp/* /var/lib/apt/lists/* /var/tmp/* WORKDIR /var/www/html COPY index.html . EXPOSE 80 CMD apachectl -D FOREGROUND
Docker
복사
clean, autoremove를 통한 불필요한 파일 제거
24초

3. alpine 이미지 사용

FROM alpine MAINTAINER "kevin-lee <hylee@dshub.cloud>" LABEL "purpose"="webserver practice" RUN apk update && apk add apache2 WORKDIR /var/www/html COPY index.html . EXPOSE 80 CMD ["/usr/sbin/httpd","-D","FOREGROUND"]
Docker
복사
알파인 사용
4초
각 버전별 용량차이 (알파인이 압도적임)

스크래치를 통한 최적화

NOTE
scratch ⇒ 아무것도 없는 빈 이미지!
# 일반 도커파일 vi dockerfile-ubuntu FROM ubuntu:20.04 COPY go-to-lightweight-image / CMD ["/go-to-lightweight-image"] --- # 스크래치 파일 vi dockerfile-scratch FROM scratch COPY go-to-lightweight-image / CMD ["/go-to-lightweight-image"] --- docker build -t lightweight:1.0 -f dockerfile-ubuntu --no-cache . docker build -t lightweight:2.0 -f dockerfile-scratch --no-cache .
Bash
복사
일반 빌드 2.3초
스크래치 빌드 0.4초
용량도 압도적으로 좋음
sratch 내부 실행을 위해 다음이 필요하다.
정적 바이너리 실행파일을 위해 gcc 활용
busybox 컨테이너 실행 도구를 cp
실행 파일이 있는 rootfs.tar.xz를 cp

멀티스테이지 빌드

NOTE
컨테이너 이미지를 만들면서 빌드 등에는 필요하지만 최종 컨테이너 이미지에 필요없는 환경을 제거할 수 있도록 단계를 나누어서 이미지를 만드는 방법!
컨테이너 실행 시에는 빌드에 사용한 파일 및 디렉토리를 모두 삭제하고 실행한다!
FROM node:16-alpine as builder WORKDIR /app COPY ./my-app ./ RUN npm install && npm run build FROM nginx:1.21.0-alpine as production COPY --from=builder /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80
Docker
복사
대표적으로 node-Nginx 멀티 스테이지 빌드가 자주 사용된다.
# 일반 빌드 vi dockerfile-go1 FROM golang:1.15-alpine3.12 WORKDIR /app/ COPY gostart.go /app/ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/gostart ENTRYPOINT [ "/app/gostart" ] --- # 멀티스테이지 빌드 vi dockerfile-go2 FROM golang:1.15-alpine3.12 AS gobuilder-stage MAINTAINER kevin,lee <hylee@dshub.cloud> LABEL "purpose"="Application Deployment using Multi-stage builds. " WORKDIR /app/ COPY gostart.go /app/ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/gostart FROM scratch COPY --from=gobuilder-stage /app/gostart /app/gostart CMD ["/app/gostart"] --- docker build -t goapp:1.0 -f dockerfile-go1 --no-cache . docker build -t goapp:2.0 -f dockerfile-go2 --no-cache . docker run --name goapp1 -p 9091:9090 -d -h goapp1 goapp:1.0 docker run --name goapp2 -p 9092:9090 -d -h goapp2 goapp:2.0
Bash
복사
51초 - 일반 빌드
7.6초 - 멀티 스테이지 빌드
용량 차이가 제일 뚜렷함