일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- kotlin
- kotlin spring
- Elasticsearch
- 정보처리기사 실기 기출문제
- APM
- kotlin coroutine
- aws
- 공부
- Pinpoint
- 기록으로 실력을 쌓자
- 코틀린 코루틴의 정석
- kotlin querydsl
- 정보처리기사실기 기출문제
- AWS EKS
- 오블완
- CKA
- minikube
- IntelliJ
- CloudWatch
- CKA 기출문제
- MySQL
- 티스토리챌린지
- PETERICA
- 정보처리기사 실기
- Linux
- Java
- Kubernetes
- AI
- Spring
- mysql 튜닝
- Today
- Total
피터의 개발이야기
[Docker] Distroless 이미지란? 본문
ㅁ 들어가며
Docker의 경량 이미지는 alpine만 알고 있었다. 보안 취약점과 이미지의 용량에 대해서 고민하면서 회사 크루의 소개로 Distroless를 알게 되었다. 도커 이미지계의 미니멀리즘이라, 해커가 들어와도 할 수 있는게 없었다. 그래서 보안점검, 이미지 경량화에 우수하지만 디버깅은 어렵다.
ㅁ Distroless 이미지란?
Distroless 이미지는 Kubernetes 워크로드를 위해 설계된 최소화된 컨테이너 이미지이다. 이 이미지는 애플리케이션과 그 런타임 의존성만을 포함하며, 불필요한 요소들을 과감히 제거하였다.
ㅁ Distroless의 특징
ㅇ 최소한의 구성: 애플리케이션에 필요한 최소한의 바이너리 코드와 직접적인 종속성만 포함
ㅇ 불필요한 요소 제거: 패키지 관리자, shell, 기타 프로그램 등 표준 Linux 배포판에 있지만 애플리케이션에 불필요한 요소들을 제거
ㅇ 보안성 강화: 불필요한 프로그램들이 없어 보안적으로 장점
ㅁ Distroless vs 기존 이미지
Distroless 이미지는 기존의 Alpine 이미지보다 더 경량화되었다. 예를 들어, httpd:alpine
베이스 이미지에서는 취약점이 0개로 나타나는 반면, 일반 httpd
이미지에서는 최소 124개의 알려진 취약점이 발견된다.
ㅁ Distroless 이미지 사용하기
Distroless 이미지를 효과적으로 사용하기 위해서는 Docker의 Multi Stage 빌드를 활용하는 것이 좋다.
표준 Linux 배포판에는 있지만 애플리케이션에 불필요한 패키지 관리자를 분리하여 이미지를 구성할 수 있다.
간단히 hello world app을 만들어 보자.
// app.go
package main
import "fmt"
func main() {
fmt.Println("Hello World")
}
# 빌드 단계
FROM golang:1.7.3 AS builder
WORKDIR /go/src/app
COPY app.go .
RUN go build -o app
# 실행 단계
FROM gcr.io/distroless/static-debian11
WORKDIR /root/
COPY --from=builder /go/src/app/app .
CMD ["./app"]
이 방식을 사용하면 빌드 단계와 실행 단계를 분리하여 최종 이미지의 크기를 최소화할 수 있다.
# 빌드
$ docker build --platform linux/amd64 -t myapp -f Dockerfile .
# 실행
$ docker run -itd --platform linux/amd64 --name=test2 myapp
# 로그확인
$ docker logs test2
Hello, World!
ㅁ Distroless와 alpine 이미지 크기 비교
# 빌드 단계
FROM golang:1.7.3 AS builder
WORKDIR /go/src/app
COPY app.go .
RUN go build -o app
# 실행 단계(alpine 수정)
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /go/src/app/app .
CMD ["./app"]
# myapp-alpine 이미지 생성
docker build --platform linux/amd64 -t myapp-alpine -f Dockerfile .
# 이미지 조회
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp latest 158014cd1279 43 minutes ago 4.2MB
myapp-alpine latest 20fa627e3fd2 46 minutes ago 9.47MB
ㅇ 이미지 크기가 1/2 크기로 감소하였다.
ㅇ alpine도 경량화되었는데, 더 경량화된 상태를 확인할 수 있다.
# 실행
$ docker run -it --rm --platform linux/amd64 --name=alpine myapp-alpine:latest /bin/sh
ㅇ sh를 통해 컨테이너 안에서 실행할 수 있었다.
ㅁ trivy를 이용한 보안점검
# Distroless 취약점 점검
$ trivy image myapp
myapp (debian 11.10)
Total: 1 (UNKNOWN: 1, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
# Alpine 취약점 점검
$ trivy image myapp-alpine
myapp-alpine (alpine 3.21.0)
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
ㅇ 경량화된 두 이미지 보안 취약점에서는 큰 문제가 없었다.
비교를 위해 로컬에 생성한 다른 이미지와 비교를 해 보았다.
$ trivy image ffmpeg5-golang:1.20.10-rockylinux9
usr/local/go/bin/go (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/bin/gofmt (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/addr2line (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/asm (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/buildid (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/cgo (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/compile (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/covdata (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/cover (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/dist (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/doc (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/fix (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/link (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/nm (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/objdump (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/pack (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/pprof (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/test2json (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/trace (gobinary)\
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
usr/local/go/pkg/tool/linux_amd64/vet (gobinary)
Total: 15 (UNKNOWN: 0, LOW: 0, MEDIUM: 11, HIGH: 3, CRITICAL: 1)
ㅇ 이런 보안 취약점은 Multi stage로 최대한 보안해야 한다.
ㅁ GoogleContainerTools
GoogleContainerTools에 가면 Distroless 이미지들과 example들을 확인할 수 있다.
ㅇ Debian 12
Image | Tags |
Architecture Suffixes
|
gcr.io/distroless/static-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, arm, s390x, ppc64le
|
gcr.io/distroless/base-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, arm, s390x, ppc64le
|
gcr.io/distroless/base-nossl-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, arm, s390x, ppc64le
|
gcr.io/distroless/cc-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, arm, s390x, ppc64le
|
gcr.io/distroless/python3-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64
|
gcr.io/distroless/java-base-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, s390x, ppc64le
|
gcr.io/distroless/java17-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, s390x, ppc64le
|
gcr.io/distroless/java21-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, ppc64le
|
gcr.io/distroless/nodejs18-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, arm, s390x, ppc64le
|
gcr.io/distroless/nodejs20-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, arm, s390x, ppc64le
|
gcr.io/distroless/nodejs22-debian12 | latest, nonroot, debug, debug-nonroot |
amd64, arm64, arm, s390x, ppc64le
|
ㅇ debian12 기준으로 python, java, nodejs 등 언어 별로 이미지를 제공한다.
ㅁ Distroless 장점
ㅇ 이미지 크기 감소
ㄴ 불필요한 구성 요소를 제거하여 이미지 크기를 크게 줄일 수 있다.
ㄴ 일반 이미지에 비해 약 1/10 크기로 용량이 감소할 수 있다.
ㅇ 보안성 향상
ㄴ 운영체제, 패키지 관리자, 쉘 등을 제거함으로써 공격 표면을 줄이고 보안 취약점을 최소화한다.
ㅇ 성능 최적화
ㄴ 필요한 구성 요소만 포함하므로 컨테이너 시작 시간이 단축되고 리소스 사용이 효율적이다.
ㅇ 의존성 관리 용이
ㄴ 애플리케이션과 직접적인 종속성만 포함되어 있어 의존성 관리가 간단해진다.
ㅁ Distroless 단점
ㅇ 디버깅 어려움
ㄴ 쉘이나 추가 도구가 없어 컨테이너 내부에서 직접적인 디버깅이 어렵다.
ㅇ 추가 소프트웨어 설치 불가
ㄴ 패키지 관리자가 없어 런타임에 추가 소프트웨어를 설치할 수 없다.
ㄴ Distroless 이미지에 소프트웨어를 추가하려면
- 멀티 스테이지 빌드를 활용하여 필요한 소프트웨어를 먼저 설치하고 빌드
- 빌드된 결과물만 Distroless 이미지로 복사ㄷ
ㅇ 호환성 문제
ㄴ 일부 환경에서 호환성 문제가 발생할 수 있다.
ㄴ 예를 들어, AWS에서는 모든 컨테이너에 쉘이 있다고 가정하기 때문에 상태 체크를 통과하지 못할 수 있다.
ㅇ 빌드 프로세스 복잡성
ㄴ 멀티 스테이지 빌드를 사용해야 하므로 Dockerfile 작성이 더 복잡해질 수 있다.
ㅁ 마무리
Distroless 이미지는 컨테이너 이미지의 새로운 패러다임을 제시한다. 최소한의 구성(최소한의 Binary code, 직접적인 종속 라이브러리, 최소한의 런타임)으로 보안성을 강화하고 이미지 크기를 줄여 효율적인 컨테이너 운영을 가능하게 한다. 특히 Kubernetes 환경에서 더욱 가치있어서, 앞으로 컨테이너 기반 애플리케이션 개발 및 배포 방식에 적극 활용해 볼 생각이다.
ㅁ 함께 보면 좋은 사이트
ㅇ Distroless 베이스 이미지를 활용해 초경량 이미지 빌드하기
ㅇ [Supply Chain Security] Minimize base image footprint
ㄴ CI/CD 파이프라인에 이미지 스캔, 라이브러리 취약점스캔
'DevOps > Docker' 카테고리의 다른 글
[Docker] MacOS에서 Docker 로그 파일 위치 찾기 (0) | 2025.01.07 |
---|---|
Trivy: 컨테이너 이미지 취약점 스캐너 사용법 (1) | 2025.01.04 |
[Docker] 멀티 플랫폼, --platform 옵션 사용법 (1) | 2025.01.03 |
[Docker] Docker 컨테이너에서 한국 시간(KST)을 적용하는 방법 (0) | 2024.12.13 |
[Docker] Multi-stage 빌드로 Go 애플리케이션 최적화하기 (0) | 2024.12.03 |