티스토리 뷰

DOCKER

[Docker] Docker 최적화

PeonyF 2023. 12. 28. 19:52
반응형

1. 생성된 레이어 재사용(캐싱)

기존코드1

문제점

  • Docker이미지 빌드시 Dockerfile의 각 명령어를 실행 → 해당 결과들을 새로운 레이어로 저장한다.
  • 아래 코드는 소스 코드중 어떤 파일이든 변경되면 COPY ./ ./ 이후의 모든 레이어가 다시 빌드된다

→ 소스의 작은 변경이라도 RUN npm install 가 다시 실행된다.

FROM node:10
WORKDIR /usr/src/app
COPY ./ ./
RUN npm install
CMD ["node","server.js"]

수정코드1

수정된 이유

  1. package.json 먼저 복사
    1. package.json에 변경사항이 없다 → RUN npm install 명령어 까지의 모든 레이어 캐싱되어 재사용(npm 재설치 없이 빠르게 빌드 가능)
    2. package.json에 변경사항이 있다 → RUN npm install 실행
  2. 이후 코드를 수정한 부분이 있는 COPY ./ ./ 를 복사
FROM node:10
WORKDIR /usr/src/app
COPY package.json ./
RUN npm install
COPY ./ ./
CMD ["node","server.js"]

기존코드2

문제점

  • 모든 파일을 한번에 복사한 후 필요한 Python 패키지를 설치하는 방식
FROM python:3.8-slim
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 8080
ENTRYPOINT ["python"]
CMD ["run.py"] 

수정코드2

수정된 이유

  • WORKDIR /app 의 순서 변경 → 작업 디렉토리를 명확히 하고, 모든 파일 경로와 명령 실행의 기준점을 설정하기 위함
  • requirements.txt를 먼저 복사 → 변경이 없을 경우 pip install 단계를 다시 실행하지 않음(기존 레이어 재사용)
  • 이후 COPY ./app 명령어로 application의 나머지 부분 복사 → 변경이 없을 경우 기존 레이어 재사용
FROM python:3.8-slim
WORKDIR /app
COPY requirements.txt ./requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app
EXPOSE 8080
#ENTRYPOINT : 컨테이너가 시작될 때 실행할 기본 명령을 설정
#컨테이너가 시작될 때 항상 Python 인터프리터를 사용하여 지정된 파이썬 스크립트(CMD에서 지정된 run.py 같은)를 실행
ENTRYPOINT["python"]
CMD["run.py"]

캐싱

변경이 잦은 파일들은 Dockerfile의 뒤에 COPY하는것이 좋고, 의존성 파일(ex.requirements.txt)은 먼저 복사하는게 좋다.

  • Docker이미지 빌드시 Dockerfile의 각 명령어를 실행하면서 해당 명령어의 결과 캐싱
  • 만약 특정 명령어까지 변경사항이 없다면 Docker는 그 부분까지 캐싱된 결과(layer)를 재사용함
  • 변경사항이 있는 명령어의 경우 이전의 캐시된 레이어를 사용할수 없으므로 그 명령어와 그 이후의 명령어들은 새로 실행되어 결과가 빌드됨

 

2. Dockerfile 최적화

더 작은 기본 이미지 사용

ex.multi-stage, 완전한 OS(ubuntu)대신 경량 Alpine Linux 이미지를 기본으로 사용

# 변경전
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y python3-pip
...

# 변경후
FROM alpine:3.12
RUN apk add --no-cache python3 py3-pip
...

불필요한 파일 제거

.dockerignore 파일을 사용하여 빌드 컨텍스트에서 파일이나 디렉터리를 제외

# 버전관리 시스템 폴더
.git
.gitignore
.svn
.hg

# 빌드 도구 관련 파일 및 폴더
Dockerfile
Dockerfile.*
*docker-compose.yml
.dockerenv

#종속성 관련 폴더(파이썬)
.pip
.virtualenv
venv
.env

# 로그 파일 및 디렉토리
*.log
logs

# 임시파일 및 캐시파일
*.tmp
*.temp
*.cache

# 개발 도구 설정 파일
.vscode
.idea
*.swp

# 테스트 및 문서파일
tests
docs
doc
README.md

# 빌드 결과물 미 배포파일
dist
build
out

# 운영체제 관련 파일
.DS_Store
Thumbs.db

레이어 수 최소화

문제점 : 각 명령어는 새레이어를 생성함 → 레이어가 많으면 빌드 프로세스 속도 드려짐 → Docker image 크기 늘어남

RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2

해결점 : RUN 명령을 사용해 여러 주문을 단일 명령어로 결합

RUN apt-get update && \
    apt-get install -y package1 && \
    apt-get install -y package2

ENV,올바른 CMD 및 ENTRYPOINT 사용

#ENV로 환경 설정 지정
FROM node:14
ENV NODE_ENV=production
#ENTRYPOINT로 Nginx 시작하라고 명령함
ENTRYPOINT ["nginx", "-g", "daemon off;"]
CMD ["-c", "/etc/nginx/nginx.conf"]

 

3. Multi-Stage Build사용

Multi-stage 코드

  • Builder 단계: Builder 단계에서 빌드된 결과물 생성 및 이 중간결과물을 캐시함
    • 이미지에 포함되는 파일(O) : 설치된 Python 패키지, application 코드(/app)
    • 이미지에 제거되는 파일(X) :
      • 소스코드 : 빌드를 위해 필요하지만, 실행파일이 생성되면 필요없음
      • 빌드파일 : gradlew,gradle 등 빌드과정을 자동화하는 스크립트,설정파일은 빌드후에는 필요없음
      • 중간 생성파일: 컴파일 과정에서 생성되는 클래스,로그 파일등은 필요없음
  • Deployer단계 : Builder 단계에서 빌드된 결과물(이미지 레이어)를 가져와서 최종 이미지 구성
    • COPY --from=builder 를 통해 Builder 단계에서 필요한 결과물만 가져온다(빌드에 필요한 패키지, 라이브러리, 빌드된 소스 코드 등)
 
# Builder 단계
FROM python:3.8-alpine AS builder
RUN apk update && apk add --no-cache make && apk add --no-cache libpq-dev g++
WORKDIR /app
COPY requirements* ./
RUN pip install --no-cache-dir -r requirements-prod.txt
COPY Makefile . 
COPY src ./src

# Deployer(배포자) 단계
FROM python:3.8-alpine AS deployer
COPY --from=builder /usr/local/lib/python3.8/site-packages /usr/local/lib/python3.8/site-packages
COPY --from=builder /app /app
RUN apk update && apk add --no-cache make
WORKDIR /app

CMD ["make", "run-server"]

 

질문1. 만약 Builder 단계 혹은 Deployer(배포자) 단계에서 코드 수정이 생긴다면?

  • Builder 단계 코드 수정시 : 만약 requirements.txt가 변경시 pip install 를 통해 재설치 및 새로운 중간결과물을 생성한다. 이후 변경된 중간결과물 Deployer(배포자) 단계에서 재빌드되어 최종 이미지에 반영됨
  • Deployer(배포자) 단계 코드 수정시 : 캐시된 Builder 단계의 결과물은 그대로 사용, Deployer(배포자) 단계에서 변경된 사항을 반영하기 위해 변경된 부분의 명령부터 이후의 모든 명령을 다시 실행

질문2. Multi-Stage Build에서 중간결과물을 사용해 특정 레이어의 결과물을 이전 단계(builder 단계)에서 가져와서 현재 이미지로 복사하는데, 일반 Docker 빌드와 레이어시에도 동일한가?

  • 일반 Docker빌드시 각 명령어들이 순서대로 실행 → 새로운 레이어가 이전 레이어 위에 순차적으로 쌓임 → 최종 이미지 생성
  • 일반 Docker빌드시 각 단계에서 생성된 레이어들을 건너뛰거나,선택적으로 사용하는게 아닌 모든 레이어가 최종 이미지에 포함됨

Multi-Stage Build

  • FROM문을 사용해 단계별로 이미지를 구성하고, 마지막 단계에서만 필요한 파일을 가져온다.
  • 그 결과, 최종 이미지를 크게 줄이고, 필요한 부분만 포함시킬 수 있다.

 

 

참고문서

https://benriemer.medium.com/speed-up-docker-desktop-d676c9fe82de

따라하며 배우는 도커와 CI환경 강의

https://blog.annotation-ai.com/python-docker-img-optimization/

반응형

'DOCKER' 카테고리의 다른 글

[DOCKER] Docker Compose  (0) 2024.01.08
[Docker] Docker Volume  (2) 2024.01.02
[Docker] Docker Container2 - 코드로 보는 개념  (0) 2023.12.26
[Docker] Docker Image  (0) 2023.12.26
[Docker] Docker Container1 - 기본 개념  (0) 2023.12.21
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함