[Docker] 5. 명령어 분류와 Dockerfile 최적화
1. Docker 명령어 분류
1.1 Docker 이미지 생성 및 관리
1. docker build :: Dockerfile을 기반으로 이미지를 빌드
docker build -t my-image .
2. docker images :: 로컬에 저장된 Docker 이미지를 확인
docker images
3. docker rmi :: Docker 이미지를 삭제
docker rmi my-image
4. docker pull :: Docker Hub나 다른 레지스트리에서 이미지를 다운로드
docker pull ubuntu:latest
5. docker push :: 이미지를 Docker Hub나 다른 레지스트리로 푸시
docker push my-image
1.2 Docker 컨테이너 실행 및 관리
1. docker run :: 새로운 컨테이너를 실행
docker run -d -p 5000:5000 my-image
2. docker ps :: 실행 중인 컨테이너를 확인
docker ps
3. docker stop :: 실행 중인 컨테이너를 중지
docker stop container_id
4. docker start :: 정지된 Container 구동
docker start <container_name_or_id>
5. docker restart :: 구동중인 Container 재가동
docker restart <container_name_or_id>
6. docker rm :: 중지된 컨테이너를 삭제
docker rm container_id
7. docker logs :: 실행 중인 컨테이너의 로그를 확인
docker logs container_id
2. Dockerfile 최적화 전에 알아둬야 할 것.
2.1 기본적인 유의점
1. Local 에서 Docker Image 빌드 X, Remote 에서 Docker Image 빌드
- CI/CD 자동화 취지에 맞게, 로컬에서는 개발만
- 모든 CI/CD 절차는 로컬 외 서버에 위임(git Actions, Jenkins ..)
2. Remote 중 생성 주체와 실행 주체 분리
- 생성 주체와 실행 주체가 같다면,
해당 서버에 Network Overhead + 불필요한 메모리 사용 초래
3. Docker 이미지의 목적은 실행, 컴파일이나 빌드 관련 명령어는 제외
- Docker이미지에서는 실행에 필요한 파일만 포함하여 이미지의 크기와 빌드 시간 최적화
- 이미 프로그램 빌드가 마쳐진 결과물만을 가지고(COPY) 도커 이미지 생성
2.2 이미지 생성 과정에서 스냅샷
- Dockerfile에서는
1. 기반 OS 이미지 위에 Script 를 통해 Volumn 을 설정
2. 로컬에 있는 디렉토리들을 Image 에 적재
3. 원하는 프로그램들을 설치 - Dockerfile의 각 명령어가 레이어를 형성하는 방법
:: Docker Image == 레이어별로 층층히 쌓여진 결과물
:: Dockerfile은 이미지를 생성하기 위한 스크립트
:: Dockerfile의 여러 명령어들이 라인 단위로 순차적 실행 -> 점진적인 업데이트를 통한 최종 결과물 형성
:: 각 명령어는 실행될 때마다 새로운 이미지 스냅샷을 생성
=> So, Dockerfile 은 Image 빌드 중에 문제 발생 시, 이전 스냅샷으로 롤백 가능
=> 해당 성질을 이용하여 최적화에 이용 가능
3. Dockerfile 최적화 방법
Dockerfile best practices
몇 가지 효율적인 Dockerfile 작성 방법들
velog.io
:: 위의 내용 중 일부를 정리
3.1 베이스 이미지 경량화
- base image
:: Docker 이미지의 시작점
:: 경량화된 이미지를 통 이미지 빌드 시간이 단축 및 컨테이너의 리소스 절약 가능 - 리눅스 이미지의 종류
항목 | Alpine | Bullseye | Ubuntu |
최대 크기 | 2.7MB | 약 50MB | 약 25MB |
Shell | /bin/sh | /bin/bash | /bin/bash |
Package Manager |
apk | apt | apt |
기반 OS | 경량화된 리눅스 (musl libc + Busybox) |
Debian 11 (glibc와 coreutils 사용) |
Debian 기반 (glibc와 coreutils 사용) |
장점 | - 매우 작은 크기 - 빠른 빌드 시간 |
- 많은 라이브러리와 툴 기본 제공 - Debian 기반의 안정성 |
- 라이브러리와 패키지 사용 용이 |
단점 | - 기본 쉘 /bin/sh만 제공 - 일부 앱이나 라이브러리에서 의존성 문제 발생 가능 |
- 크기 크고, 빌드 시간 및 용량이 커짐 | - 크기 및 빌드 시간이 길다 |
3.2 Dockerfile 뒤쪽에 자주 변경되는 부분 배치
- Docker 이미지 생성 시,
1. 각각의 명령어가 레벨로 처리
2. 변경된 부분은 해당 레벨 이후부터 새로 빌드
=> So, 빈번하게 변경되는 명령어는 Dockerfile 뒤쪽에 배치
=> 캐시 활용을 통해 빌드 시간이 단축
- 무거운 작업
:: 변경이 거의 없고 실행 비용이 큰 작업 (npm install, apt-get install)
:: Dockerfile에서 앞쪽에 배치 - 가벼운 작업
:: 변경될 가능성이 높은 작업 (애플리케이션 소스 코드 복사나 설정 파일 변경 등)
:: 뒤쪽에 배치 -> 변경된 부분만 다시 빌드하도록 설정
3.3 .dockerignore 파일 활용
:: 불필요한 파일들이 Docker 이미지 빌드 시에 포함되는 것을 방지
:: .dockerignore에 Dockerfile 넣을것, Dockerfile 내 COPY 명령어 있으면, 매번 Dockefile 바뀔때마다 Rebuild
3.4 Multi-Stage Build
:: Docker 이미지 빌드 과정에서 여러 단계로 나누어 작업을 진행 -> 최종 이미지 최적화
:: 불필요한 파일과 의존성을 제거를 통한 최적화
:: 빌드 컨테이너를 이용하여 빌드용과 실행용을 구분하여 실행용 이미지를 최적화
- Ex
# 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"]
Docker 이미지
-실행 가능한 상태 -> 최종 이미지에는 실행 환경만 제공
- 컴파일이나 빌드와 관련된 명령어는 최종 이미지에 X
빌드 컨테이너
:: 실제 애플리케이션의 빌드 과정을 처리하는 임시 환경
:: 컴파일하거나, 의존성을 설치하는 등의 작업을 수행
:: 이후 결과물로 "실행에 필요한 파일"의 이미지를 반환
-> 헷갈려서 글+ 그림으로 정리하면
1. 개발 = 내 로컬
2. 컴파일 및 빌드 실행 - 담당 서버(git Actions, jenkens) / 근데 없다면 내 로컬에서...ㅠㅠ
=> (2)과정 산출물은 - 실행에 필요한 것만 가진 경량화된 파일
3. (2)의 결과물을 EC2와 같은 서버에 배포(실행 이미지에 대한 최적화)
출처
ASAC 수업자료
[Docker] Dockerfile - Multi-stage build(멀티스테이지 빌드)
애플리케이션 개발 시에 개발 환경에서 사용한 라이브러리나 개발 지원 툴이 제품 환경에서 반드시 사용되는 것은 아닙니다. 제품 환경에는 애플리케이션을 실행하기 위해 최소한으로 필요한
kimjingo.tistory.com