이미지를 최적화하는 것은 이미지의 용량을 최소화하고 보안 위험을 줄일 수 있다.
1. 이미지 최적화
이미지를 확인하고 관리하는 명령어로 다음 명령어들을 사용할 수 있다.
docker system df # 현재 환경에서의 이미지, 컨테이너, 볼륨, 캐시 등 상황 표시
docker system prune # 현재 사용하지 않는 이미지 레이어나 빌드 캐시를 비워줌
컨테이너나 이미지 레이어는 잠시 사용 중이지 않을 수도 있으므로 docker system prune 명령어는 신중히 사용해야한다.
rm 명령어는 용량을 줄여주지 않는다.
FROM diamol/base
CMD echo app- && ls app && echo docs- && ls docs
COPY . .
RUN rm -rf docs
이미지에서 불필요한 docs라는 파일을 rm -rf 명령으로 제거한다고해도 이미지 크기가 작아지지 않는다. 이미지는 이미지 레이어가 쌓여 만들어지는데 실제 docs 디렉터리는 COPY 인스트럭션으로 만든 레이어에 그대로 남아 있다. 삭제를 하는 레이어가 추가로 생성되어 보이지 않게될 뿐이다. 다음 레이어에서 불필요한 파일을 제거해도 소용이 없으며, 각 레이어마다 따로 최적화를 해야한다.
.dockerignore
불필요한 파일을 제외해야한다면, 필요한 파일의 디렉터리만 Dockerfile의 명령어 상에 지정하면된다. 만약 제외할 파일의 디렉터리를 규칙화하고 싶다면 .dockerignore 파일에 디렉터리를 추가하면된다.
2. 기반 이미지 최적화
도커허브에서 윈도우, 자바11 버전 등 공식 이미지들의 변종 이미지들을 확인해볼 수 있다. 자바 11 버전의 11-jdk는 리눅스용으로는 296 MB인데 최소 기능만 포함된 11-jre-slim은 69 MB이다.
이런식으로 윈도우-> 나노서버, 리눅스 -> 알파인 리눅스 or 데비안 슬림 등 공식 이미지들은 꼭 필요한 기능만 포함된 최소 사양 이미지가 있는 것이 보통이다. 이것은 용량만 관계된 것이 아니라 보안 및 디스크 활용에도 영향을 미친다.
cd ch17/exercises/truth-app
docker image build -t diamol/ch17-truth-app .
docker container run -d -p 8010:80 --name truth diamol/ch17-truth-app
curl http://localhost:8010/truth
이 앱은 기반 이미지로 11-jdk 태그를 사용한다. 즉 curl 등 부가적인 도구를 포함하고 있기 때문에 디스크의 용량을 많이 사용한다. curl 요청을 날려보면 true를 반환한다.
docker container exec -it truth sh # 셸로 API 컨테이너 접속
javac FileUpdateTest.java
java FileUpdateTest
exit
curl http://localhost:8010/truth
컨테이너 내부에서 자바 테스트 코드가 컴파일되고 실행된 뒤, 강제로 에러만 내뱉도록 설계된 이미지이다. 조금 다른 형태이긴 하지만 이런 방식으로 다른 사람이 컨테이너에 접근하여 에러를 일으킬 수 있는 파일을 공격자가 실행시켜버리면 컨테이너의 동작에 이상이 생길 수 있다. curl 요청을 다시해보면 false를 리턴한다.
기반 이미지는 최소화된 형태로 사용하고, 골든 이미지를 사용하자
애플리케이션을 구동하는데 불필요한 기능들이 포함된 기반이미지를 사용하는 것은 좋지 않다. 각 공식이미지 등에서 제공하는 최소화된 기반 이미지 형태를 사용하는 것이 기본 원칙이다. 이런 최소화된 형태의 이미지로 애플리케이션을 구동해보고, 필요한 서드파티 도구나 설정 등을 추가하여 골든 이미지로 만들어 사용하는 것이 추천된다.
Anchore
엔코어라는 이미지 분석 도구가 있다. 이 서드파티 도구는 리눅스 컨테이너에서만 지원된다. 앤코어를 통해 이미지를 분석하면 운영체제, 애플리케이션 플랫폼 정보, 바이너리 단위의 보안 문제까지 많은 부분들을 검사해준다.
교재에서 anchore 설치 명령어를 작성해놓았으나 교재 작성 시점으로부터 시간이 지나서 사용 범위나 방법이 애매하다. 공식 사이트에서도 보니 Demo 버전을 따로 신청해야하는 것 같다. 나중에 필요할 때 직접 앤코어 사이트에 가서 방법을 알아보고 실행해보는 것이 좋을 것 같다.
3. 멀티 스테이지 빌드 활용
최적화를 위해서는 인스트럭션을 최소화하여 이미지 레이어 수를 줄여야한다. 이를 위해 가장 적절한 방법이 멀티스테이지이다.
FROM diamol/base AS download
ARG DATABASE_URL=https://archive.cis.uci.eud/.../url_svlight.tar.gz
RUN wget -0 dataset.tar.gz ${DATASET_URL}
FROM diamol/base AS expand
COPY --from=download dataset.tar.gz .
RUN tar xczf dataset.tar.gz
FROM diamol/base
WORKDIR /dataset/url_svmlight
COPY --from=expand url_svmlight/Day1.svm .
각 단계에서 이전 단계에서 명시적으로 복사해온 파일만이 포함되기 때문에 인스트럭션을 줄일 필요없이 이미지가 최적화되며 디버깅 편의성을 유지할 수 있다. 멀티 스테이지 Dockerfile 스크립트도 원하는 지점까지만 이미지를 빌드할 수 있어서 파일의 중간 상태를 체크할 수 있다.
cd ch17/exercises/ml-dataset
docker image build -t diamol/ch17-ml-dataset:v3 -f Dockerfile.v3 . #끝까지 빌드
docker image build -t diamol/ch17-ml-dataset:v3-download -f Dockefile.v3 --target download . # 'download' 스테이지 까지만 빌드
docker image build -t diamol/ch17-ml-dataset:v3-expand -f Dockerfile.v3 --target expand . # 'expand' 스테이지 까지만 빌드
docker image ls -f reference=diamol/ch17-ml-dataset:v3*
download 단계의 이미지는 251MB, expand 단계의 이미지는 2.46GB인데 최종 빌드 이미지는 24MB이다. 저자가 이렇게 설계한 부분도 있겠지만 어쨌든 이런식으로 빌드하여 필요한 파일만 최종 단계로 넘겼기 때문에 이미지의 크기를 줄일 수 있었다.
3. 다단 애플리케이션 설정
지금부터는 애플리케이션의 환경에 따른 설정값을 변경하는 전략에 대해서 살펴본다. 교재에서 언급하는 설정의 종류는 3가지이다.
- 버전에 따라 달라지는 설정: 모든 환경에서 동일하지만 버전별로 달라지는 설정
- 환경에 따라 달라지는 설정: 환경별로 달라지는 설정
- 기능 설정: 버전별로 애플리케이션의 동작을 다르게하기 위한 설정
보통 버전에 따라 달라지는 설정은 버전별로 업데이트를 하고 기본 설정으로 만든다. 그리고 운영, 테스트, 로컬 환경 등 환경별로 달라지는 설정을 기본 설정을 Override하는 방식을 취한다. 또한 특별한 기능이 필요한 경우 On/off를 하는 형태로 환경 변수를 도커 이미지를 빌드할 때 넣어주어서 설정한다. 이런 방식은 'The Twelve-Factor App(https://12factor.net)에서 권장하는 현대적 방식의 앱 설계 방향이라고 한다.
예를 들어 Node.js로 실행하는 애플리케이션을 아래 예시처럼 환경별, 기능별로 달리 실행할 수 있다.
cd ch18/exercises/access-log
docker container run -d -p 8080:80 diamol/ch18-access-log # 이미지에 포함된 기본 설정으로 실행
docker container run -d -p 8081:80 -v "$(pwd)/config/dev:/app/configoverride" diamol/ch18-access-log # 오버라이드
docker container run -d -p 8082:80 -v "$(pwd)/config/dev:/app/configoverride" -e NODE_CONFIG='{\"metrics\": {\"enabled\":\"true\"}}' diamol/ch18-access-log # 오버라이드 + 환경변수 설정
기본 설정은 이미지에 포함시키되, 오버라이드 하는 설정 파일을 로컬파일의 $(pwd)/config/dev 경로에서 /app/configoverride 쪽으로 볼륨 마운트하여 환경별 설정 내용을 바꾼다. 애플리케이션에서 이 path를 바라보게 하여 설정을 참조하도록 하는 것이다. 마지막으로 -e NODE_CONFIG라는 변수에 json 값을 통해서 환경 변수 설정을 추가하였다.
기본적으로 애플리케이션의 종류와 상관없이 이런 패턴을 적용한다! 다만 위 예제에서 json 방식으로 환경 변수를 설정한 것은 Node.js 기반이기 때문이다.
이런 패턴으로 한다는 것만 기억하면 될 것 같다. 교재에서는 위에서 살펴본 Node.js로 구성한 accesslog 애플리케이션 외에 각각 java, golang으로 구성한 iotd, image-gallery 애플리케이션에 대한 도커 컴포즈 파일의 설정을 살펴보며 마무리한다. 컨테이너를 실행할 때 설정해주는 설정 내용을 도커 컴포즈에서 참조하도록 지정해주었다.
golang 기반에서는 환경 변수를 구분하기 위해서 "IG_" 라는 prefix를 환경 변수 앞에 붙여준다고 한다. 그리고 자바는 .properties 파일을, golang은 .toml 파일을 사용한다고 하니 참고하면 좋을 것 같다!
version: "3.7"
services:
accesslog:
image: diamol/ch18-access-log
ports:
- "8030:80"
environment:
NODE_CONFIG: '{"metrics": {"enabled":"true"}}'
secrets:
- source: access-log-config
target: /app/config-override/local.json
networks:
- iotd-net
iotd:
image: diamol/ch18-image-of-the-day
ports:
- "8020:80"
environment:
CONFIG_SOURCE_PATH: "/config-override/application.properties"
IOTD_MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,prometheus"
secrets:
- source: iotd-config
target: /config-override/application.properties
networks:
- iotd-net
image-gallery:
image: diamol/ch18-image-gallery
ports:
- "8010:80"
environment:
IG_METRICS.ENABLED: "TRUE"
secrets:
- source: image-gallery-config
target: /app/config-override/config.toml
depends_on:
- accesslog
- iotd
networks:
- iotd-net
networks:
iotd-net:
--이하 생략
'Programming-[Infra] > Docker' 카테고리의 다른 글
도커 교과서(엘튼 스톤맨, 심효섭) - 18. 리버스 프록시-1: nginx, 로드밸런싱, 라우팅과 SSL, Traefik (0) | 2023.05.21 |
---|---|
도커 교과서(엘튼 스톤맨, 심효섭) - 17. 로그 설정: fluentd, elasticsearch, kibana (0) | 2023.05.20 |
도커 교과서(엘튼 스톤맨, 심효섭) - 15. 다중 아키텍처 빌드 (0) | 2023.05.07 |
도커 교과서(엘튼 스톤맨, 심효섭) - 14. 도커 원격 접속, 보안과 context (0) | 2023.05.06 |
[에러] 도커 교과서(엘튼 스톤맨, 심효섭) - 13. 지속적 통합(CI) 파이프라인 구축. Gogs, 젠킨스 (0) | 2023.05.05 |