17 Docker 이미지 최적화
앱을 컨테이너화하고 클러스터에서 잘 작동하면 프로덕션으로 이동하는 것이 좋다고 생각할 수 있지만 여전히 시간을 투자해야 하는 몇 가지 모범 사례가 있습니다. Docker 이미지 최적화는 가장 중요한 것 중 하나입니다. , 빌드 및 배포가 빨라야 하고 애플리케이션 콘텐츠가 안전해야 하며 저녁 시간에 자유롭게 통화할 수 있어야 하기 때문에 서버의 디스크 공간이 부족할 때 오전 2시에 호출되는 것을 원하지 않습니다. Dockerfile 구문은 작고 직관적이지만 이미지 빌드를 최대한 활용하기 위해 이해해야 하는 몇 가지 복잡성을 숨깁니다.
17.1 Docker 이미지를 최적화
Docker 이미지 형식이 크게 최적화되었습니다. 레이어는 가능한 한 이미지 간에 공유되므로 빌드 시간, 네트워크 트래픽 및 디스크 사용량이 줄어듭니다. 그러나 Docker는 데이터에 대해 보수적인 접근 방식을 사용하며 가져온 이미지를 자동으로 제거하지 않습니다. 이는 명시적으로 수행해야 하는 작업입니다. 따라서 애플리케이션을 업데이트하기 위해 컨테이너를 교체하면 Docker는 새 이미지 계층을 다운로드하지만 이전 이미지 계층은 제거하지 않습니다. 디스크는 특히 정기적으로 업데이트되는 개발 또는 테스트 시스템에서 많은 오래된 이미지 레이어로 삼키기 쉽습니다.
docker system df
Docker 엔진에서 오래된 이미지를 삭제한 적이 없다면 결과에 놀랄 것입니다. 컨테이너를 실행하지 않더라도 총 7.5GB의 스토리지에 185개의 이미지가 있음을 알 수 있습니다.
첫 번째는 필요한 경우가 아니면 이미지에 파일을 포함하지 않는 것입니다. 당연하게 들리겠지만 폴더에 런타임에 필요하지 않은 문서나 이미지 또는 기타 바이너리가 포함되어 있다는 사실을 깨닫지 못한 채 전체 폴더 구조에 복사하는 Dockerfile을 작성하는 경우가 많습니다. 복사한 파일에 대해 명시하는 것은 첫 번째로 큰 절약이 될 수 있습니다. 목록 17.1의 Dockerfiles를 비교하세요. 첫 번째 예제는 전체 폴더에 있는 복사본인 반면, 두 번째 예제는 복사본에 몇 가지 추가 파일이 추가되고 이를 삭제하는 새로운 단계가 포함되어 있다는 것을 알 수 있습니다.
목록 17.1 파일을 제거하여 Dockerfile 최적화 시도
# Dockerfile v1 - copies in the whole directory structure:
FROM diamol/base
CMD echo app- && ls app && echo docs- && ls docs
COPY . .
# Dockerfile v2 - adds a new step to delete unused files
FROM diamol/base
CMD echo app- && ls app && echo docs- && ls docs
COPY . .
RUN rm -rf docs
v2 Dockerfile에서는 추가 문서 폴더를 삭제하기 때문에 이미지 크기가 더 작아질 것이라고 생각할 수 있지만 이미지 레이어가 작동하는 방식은 그렇지 않습니다. 이미지는 모든 레이어의 병합이므로 파일은 여전히 COPY 레이어에서 존재합니다. 삭제 레이어에 숨겨져 전체 이미지 크기가 줄어들지 않습니다.
Try ch17/exercises/build-context에서 두 가지 예를 모두 작성하고 크기를 비교하십시오
docker image build -t diamol/ch17-build-context:v1 .
docker image build -t diamol/ch17-build-context:v2 -f ./Dockerfile.v2 .
docker image ls -f reference= diamol/ch17*
diamol/ch17-build-context v2 3cf323a1394b 9.25MB
diamol/ch17-build-context v1 53a536f0ec11 9.25MB
폴더를 삭제하는 rm
명령이 전혀 실행되지 않은 것처럼 v2 이미지가 v1 이미지와 정확히 같은 크기임을 알 수 있습니다. 그림 17.2에서 내 출력을 볼 수 있습니다. 저는 Linux 컨테이너를 사용하고 있으므로 크기는 작지만 크기의 거의 절반은 docs 폴더의 불필요한 파일에서 나온 것입니다.
Dockerfile의 각 명령은 이미지 레이어를 생성하고 레이어는 함께 병합되어 전체 이미지를 형성합니다. 레이어에 파일을 작성하면 해당 파일이 영구적으로 존재합니다. 후속 계층에서 삭제하면 Docker가 하는 일은 파일 시스템에서 숨기는 것뿐입니다. 이것은 이미지 최적화와 관련하여 이해해야 하는 기본 사항입니다. 나중 레이어에서 팽창을 제거하려고 하는 것은 좋지 않습니다. 모든 레이어를 최적화해야 합니다. 삭제가 발생하기 전에 이전 레이어의 이미지를 실행하여 삭제 레이어가 파일을 숨기는 것을 쉽게 알 수 있습니다.
TRY 해당 레이어가 캐시에 있는 경우 모든 이미지 레이어에서 컨테이너를 실행할 수 있습니다. 최종 이미지를 이전 이미지 레이어와 비교합니다.
# run a container from the finished image:
docker container run diamol/ch17-build-context:v2
# check the image history to find the previous layer ID:
docker history diamol/ch17-build-context:v2
# run a container from that previous layer:
docker container run <previous-layer-id>
이것이 최적화에 대한 첫 번째 요점입니다.
앱을 실행하는 데 필요하지 않은 이미지를 복사하지 마십시오. 이후 지침에서 삭제를 시도하더라도 디스크 공간을 차지하는 이미지 스택의 어딘가에 여전히 있을 것입니다. 원하는 파일만 이미지로 가져오려면 COPY
지침을 정확하게 따르는 것이 훨씬 좋습니다. 따라서 이미지 크기가 더 작아지고 Dockerfile에 더 명확하게 문서화된 설치가 가능합니다. 목록 17.2는 이 간단한 앱에 최적화된 v3 Dockerfile을 보여줍니다. v1에서 변경된 유일한 점은 디렉토리 전체가 아닌 앱 하위 폴더를 복사한다는 것입니다.
목록 17.2 필요한 파일만 복사하는 최적화된 Dockerfile
FROM diamol/base
CMD echo app- && ls app && echo docs- && ls docs
COPY ./app ./app
이것을 만들 때 이미지 크기가 더 작아지는 것을 볼 수 있지만 여기에서도 할 수 있는 또 다른 최적화가 있습니다. Docker는 빌드 컨텍스트(빌드를 실행하는 폴더)를 압축하고 빌드를 실행할 때 Dockerfile과 함께 엔진에 보냅니다. 이것이 로컬 시스템의 파일에서 원격 엔진의 이미지를 빌드하는 방법입니다. 빌드 컨텍스트에는 필요하지 않은 파일이 있는 경우가 많으므로 .dockerignore
라는 파일에 파일 경로나 와일드카드를 나열하여 빌드 컨텍스트에서 파일을 제외할 수 있습니다.
TRY 최적화된 Docker 이미지를 빌드한 다음 .dockerignore 파일로 다시 빌드하여 컨텍스트 크기를 줄이십시오.
- Build the optimized image; this adds unused files to the context:
docker image build -t diamol/ch17-build-context:v3 -f ./Dockerfile.v3 .
- Rename the already prepared ignore file and check the contents:
mv rename.dockerignore .dockerignore
cat .dockerignore
- Run the same build command again:
docker image build -t diamol/ch17-build-context:v3 -f ./Dockerfile.v3 .
첫 번째 빌드 명령에서 Docker가 엔진에 2MB의 빌드 컨텍스트를 보내는 것을 볼 수 있습니다. 압축되지 않았으므로 해당 폴더에 있는 파일의 전체 크기입니다. 대부분이 2MB의 고래 사진입니다. 두 번째 빌드에는 Docker에게 docs 폴더와 Dockerfiles를 제외하도록 지시하는 .dockerignore 파일이 현재 디렉토리에 있으므로 빌드 컨텍스트는 4KB에 불과합니다.
.dockerignore 파일은 빌드 컨텍스트에서 사용하지 않는 데이터를 보내는 비용을 계산할 때 많은 시간을 절약할 수 있으며 Dockerfile에서 명시적 경로를 사용하는 경우에도 공간을 절약할 수 있습니다. 코드를 로컬로 빌드하고 Docker에서 컴파일하기 위해 다단계 빌드를 사용할 수도 있습니다. .dockerignore 파일에 빌드 바이너리를 지정하고 이미지에 복사되지 않도록 할 수 있습니다. 파일 형식은 Git의 .gitignore 파일과 동일하며 GitHub의 앱 플랫폼용 템플릿을 좋은 출발점으로 사용할 수 있습니다(Dockerfile이 저장소의 루트에 있는 경우 Git 기록 폴더 .git도 포함해야 합니다. ).
Docker 이미지에 포함할 파일을 관리하는 것의 중요성을 확인했으므로 이제 한 걸음 물러나서 기본으로 사용 중인 이미지를 살펴보겠습니다.
17.2 올바른 기본 이미지 선택
기본 이미지 크기 선택은 디스크 공간 및 네트워크 전송 시간만큼 보안에 관한 것입니다. 기본 OS 이미지가 큰 경우 실제 시스템에서는 유용할 수 있지만 컨테이너의 보안 구멍이 될 수 있는 모든 종류의 도구가 있을 수 있습니다. OS 기본 이미지에 curl이 설치되어 있는 경우 공격자는 이를 사용하여 악성코드를 다운로드하거나 데이터를 서버에 업로드할 수 있습니다.
이는 애플리케이션 플랫폼 기반 이미지의 경우에도 마찬가지입니다. Java 앱을 실행하는 경우 OpenJDK 공식 이미지가 좋은 기반이지만 Java 런타임(JRE)과 개발자 SDK(JDK)의 구성이 서로 다른 많은 태그가 있습니다.
Linux 사용자는 296MB 대신 69MB 기본 이미지를 사용할 수 있고 Windows 사용자는 Docker Hub에서 변형을 확인하고 가장 작은 OS 이미지와 가장 작은 Java 설치가 있는 것을 선택하기만 하면 2.4GB 대신 277MB를 사용할 수 있습니다. OpenJDK 팀은 다중 아키텍처 이미지에 신중합니다. 그들은 가장 광범위한 호환성을 가진 이미지를 선택하지만 더 작은 변형으로 앱을 시험해 보는 것은 간단합니다. 모든 앱이 더 작은 변형에서 작동하는 것은 아니지만 FROM
라인의 이미지를 전환하고 테스트하는 것은 쉽습니다.
크기는 디스크 공간에 관한 것이 아니라 공간을 사용하는 항목에 관한 것이기도 합니다. 가장 큰 OpenJDK 이미지에는 전체 Java SDK가 포함되어 있으므로 누군가 컨테이너를 손상시킬 수 있는 경우 좋은 공격 벡터가 있습니다. 일부 Java 소스 코드 파일을 컨테이너의 디스크에 작성하고 SDK로 컴파일하고 애플리케이션 컨테이너의 보안 컨텍스트에서 원하는 모든 작업을 수행하는 앱을 실행할 수 있습니다.
TRY ch17/exercises/truth-app는 기본 JDK 이미지를 사용하는 Java 앱이 있습니다. 항상 true 값을 반환하는 매우 간단한 REST API를 실행합니다.
- build the image - the base image uses the :11-jdk tag:
docker image build -t diamol/ch17-truth-app .
- run the app and try it out:
docker container run -d -p 8010:80 --name truth diamol/ch17-truth-app
- 확인
curl "http://localhost:8010/truth"
실행 중인 컨테이너에는 이미지에서 컴파일되는 Java REST API가 있지만 다른 Java 앱을 컴파일하기 위한 모든 도구도 있습니다. 공격자가 앱에서 벗어나 컨테이너에서 임의의 명령을 실행하면 자신의 코드를 실행하여 원하는 대로 수행할 수 있습니다. 이 이미지에는 "실수로" 테스트 코드 파일이 포함되어 있으며 악의적인 사용자가 이를 찾아 실행하여 앱의 동작을 변경할 수 있습니다.
TRY API 컨테이너의 셸에 연결하여 컨테이너 브레이크아웃을 시뮬레이션하십시오. 그런 다음 JDK를 사용하여 테스트 코드를 컴파일 및 실행하고 나중에 앱을 다시 확인합니다.
- connect to the API container - for Linux containers:
docker container exec -it truth sh
- inside the container compile and run the test Java file:
javac FileUpdateTest.java
java FileUpdateTest
exit
- back on your machine, try the API again:
curl http://localhost:8010/truth
앱의 동작이 변경되었음을 알 수 있습니다. 테스트 픽스처는 응답을 true 대신 false로 설정합니다. 그림 17.5의 출력은 원래 응답과 "해킹" 후 변경된 응답을 보여줍니다.
이것은 작업을 쉽게 하기 위해 이미지 주변에 편리한 테스트 파일이 있는 약간 인위적인 예이지만 컨테이너 브레이크아웃이 가능하며 흥미로운 공격 옵션을 보여줍니다. 네트워크 액세스를 방지하기 위해 플랫폼에서 컨테이너를 잠글 수 있으며 이 공격은 계속 작동합니다. 교훈은 기본 이미지에 앱을 실행하는 데 필요한 모든 것이 있어야 하지만 앱 빌드를 위한 추가 도구는 없다는 것입니다.
golden 이미지는 이 문제를 해결하는 한 가지 방법입니다. 올바른 기본 이미지를 선택하고 조직에 대한 자체 버전을 빌드하는 팀이 있습니다. 내 자바 앱은 diamol/openjdk
에서 빌드된다. 이것은 각 OS에 대해 가장 작은 변형을 사용하는 다중 아키텍처 이미지이다. golden 이미지가 업데이트되는 빈도를 제어할 수 있으며 golden 이미지 빌드 후 애플리케이션 이미지 빌드를 트리거할 수 있습니다. 고유한 golden 이미지를 구축할 때의 또 다른 이점은 Anchore와 같은 타사 도구를 사용하여 구축 프로세스의 기본 계층에 대한 추가 보안 검사를 통합할 수 있다는 것입니다.
TRY ch17/exercises/anchore는 Docker 이미지를 분석하기 위한 오픈 소스 프로젝트입니다. 분석기 구성 요소는 Docker 컨테이너에서 실행되지만 불행히도 다중 아키텍처를 지원하지 않습니다. Intel(Docker Desktop 또는 Community Engine 포함)에서 Linux 컨테이너를 실행하는 경우 지원됩니다. 그렇지 않으면 이 연습을 위해 PWD 세션을 시작하고 책의 GitHub 리포지토리를 복제할 수 있습니다.
- Start all the Anchore components:
docker-compose up -d
- Wait for Anchore to download its database - this can take 15 minutes, so you might want to open a new terminal window for this command:
docker exec anchore_engine-api_1 anchore-cli system wait
- Copy the Dockerfile for my Java golden image into the container:
docker container cp "$(pwd)/../../../images/openjdk/Dockerfile" anchore_engine-api_1:/Dockerfile
- Add the image and the Dockerfile for Anchore to analyze:
docker container exec anchore_engine-api_1 anchore-cli image add diamol/openjdk --dockerfile /Dockerfile
- Wait for the analysis to complete:
docker container exec anchore_engine-api_1 anchore-cli image wait diamol/openjdk
Anchore는 처음 실행할 때 알려진 보안 문제의 데이터베이스를 다운로드하기 때문에 완전히 시작하는 데 시간이 걸립니다. 일반적으로 Anchore를 CI/CD 프로세스에 통합하므로 처음 배포할 때만 적중이 발생합니다. 대기 명령은 앵커가 준비될 때까지 세션을 차단된 상태로 유지합니다. 그림 17.6에서 스캔을 위해 OpenJDK 이미지를 추가했지만 아직 분석되지 않은 것을 볼 수 있습니다.
Anchore가 분석을 완료하면 이미지의 모든 구성 요소에서 사용하는 오픈 소스 라이선스, 운영 체제 및 애플리케이션 플랫폼 세부 정보, 이미지의 바이너리에 대한 보안 문제를 포함하여 이미지에 대해 많은 것을 알게 됩니다. 이러한 결과는 모두 업데이트된 기본 이미지를 수락하기 위한 품질 게이트의 일부일 수 있습니다. 새 버전이 조직에서 금지하는 OSS 라이선스를 사용하거나 심각한 보안 취약점이 포함된 경우 해당 업데이트를 건너뛸 수 있습니다. Anchore에는 Jenkins와 같은 CI/CD 도구용 플러그인이 있으므로 이러한 정책을 파이프라인에 자동으로 적용할 수 있으며 Anchore API 컨테이너를 사용하여 직접 결과를 쿼리할 수도 있습니다.
TRY 이전 연습의 대기 명령이 완료되면 이미지가 분석된 것입니다. Anchore가 애플리케이션 플랫폼 및 이미지의 보안 문제에 대해 발견한 사항을 확인하십시오.
# check what Java components Anchore has found in the image:
docker container exec anchore_engine-api_1 anchore-cli image content diamol/openjdk java
# and check for known security issues:
docker container exec anchore_engine-api_1 anchore-cli image vuln diamol/openjdk all
Author And Source
이 문제에 관하여(17 Docker 이미지 최적화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@springer/17-Docker-이미지-최적화저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)