Docker Hardened Image 만들고 빌드하기
Docker Hardened Image 만들고 빌드하기
선언적 YAML 정의 파일로 Docker Hardened Image(DHI)를 만들고, 로컬에서 빌드하며, 고급 패턴(빌드 단계, 서드파티 저장소, 파일 경로, dev 변형)을 사용하는 방법을 알아봐요.
출처: 문서
본문
Docker Hardened Images(DHI)는 전통적인 Dockerfile 대신 선언적 YAML 정의 파일로 빌드돼요. 단일 YAML 파일이 이미지에 들어가는 것(패키지, 사용자, 환경 변수, 엔트리포인트, 메타데이터)을 정확히 설명해요. DHI 빌드 시스템은 필요한 패키지만 포함된 서명된 이미지를 SBOM(소프트웨어 자재명세서)과 SLSA Build Level 3 provenance과 함께 생성해요.
이 페이지는 DHI 정의 파일을 작성하고, 이미지를 로컬에서 빌드하며, 빌드 단계, 서드파티 저장소, 파일 경로, dev 변형 같은 고급 패턴을 사용하는 방법을 설명해요.
[!IMPORTANT]
DHI 빌드 시스템은
dhi.io에서 베이스 이미지와 빌드 도구를 풀하므로, 정의 파일을 빌드하기 전에 해당 레지스트리에 인증해야 해요. 로그인할 때 Docker ID 자격 증명(Docker Hub에 사용하는 동일한 사용자 이름과 비밀번호)을 사용해요.인증하려면
docker login dhi.io를 실행해요.
DHI 빌드가 Dockerfile과 어떻게 다른가요?
Dockerfile은 명령형 명령(RUN, COPY, FROM)의 시퀀스예요. DHI 정의 파일은 선언적 명세예요. 이미지의 원하는 상태를 설명하면 빌드 시스템이 그것을 만들어내는 방법을 알아서 처리해요.
모든 DHI 정의는 BuildKit이 사용할 DHI 빌드 프론트엔드를 알려주는 syntax 지시문으로 시작해요. 프론트엔드는 기본 Dockerfile 파서 대신 YAML 정의를 파싱·처리하는 구성 요소예요.
# syntax=dhi.io/build:2-alpine3.23
프론트엔드 버전은 베이스 배포판에 대응돼요.
| 배포판 | Syntax 지시문 |
|---|---|
| Alpine 3.22 | # syntax=dhi.io/build:2-alpine3.22 |
| Alpine 3.23 | # syntax=dhi.io/build:2-alpine3.23 |
| Alpine 3.24 | # syntax=dhi.io/build:2-alpine3.24 |
| Debian 12 (Bookworm) | # syntax=dhi.io/build:2-debian12 |
| Debian 13 (Trixie) | # syntax=dhi.io/build:2-debian13 |
DHI 빌드 시스템은 YAML을 읽고, 지정된 저장소에서 패키지를 해결하며, 파일시스템을 조립하고, 사용자 계정을 만들고, 메타데이터를 설정한 다음 서명된 OCI 이미지를 생성해요.
참조용 카탈로그 살펴보기
DHI 카탈로그 저장소는 Apache 2.0으로 오픈소스이며 모든 공식 이미지 정의를 담고 있어요. 기존 정의를 공부하는 것이 다양한 이미지 유형의 YAML 패턴을 배우는 최고의 방법이에요.
카탈로그는 다음과 같은 디렉터리 구조를 따라요.
catalog/
├── image/
│ ├── alpine-base/
│ │ ├── alpine-3.23/
│ │ │ ├── 3.23.yaml # runtime variant
│ │ │ └── 3.23-dev.yaml # dev variant
│ │ ├── guides.md
│ │ ├── info.yaml
│ │ ├── logo.svg
│ │ └── overview.md
│ ├── nginx/
│ │ ├── alpine-3.22/
│ │ ├── alpine-3.23/
│ │ │ ├── mainline.yaml
│ │ │ ├── mainline-dev.yaml
│ │ │ ├── stable.yaml
│ │ │ └── stable-dev.yaml
│ │ ├── debian-12/
│ │ ├── debian-13/
│ │ ├── bin/
│ │ ├── guides.md
│ │ ├── info.yaml
│ │ ├── logo.svg
│ │ └── overview.md
│ └── redis/
│ ├── debian-13/
│ │ ├── 8.0.yaml # runtime
│ │ ├── 8.0-dev.yaml # dev
│ │ ├── 8.0-compat.yaml # compat runtime
│ │ └── 8.0-compat-dev.yaml # compat dev
│ ├── guides.md
│ ├── info.yaml
│ ├── logo.svg
│ └── overview.md
├── chart/
└── package/
각 이미지는 배포판별로 변형을 구성해요. 이미지는 여러 변형 유형을 지원해요.
runtime변형은 최소화되어 있으며 일반적으로 non-root 사용자로 실행돼요.dev변형은 셸, 패키지 매니저, 개발 도구를 추가해요.- 호환성(compatibility) 변형은 기존 워크플로에서 사용할
bash,coreutils,grep,sed같은 일반적인 셸 유틸리티를 추가해요. 호환성 이미지는runtime또는dev변형과 함께flavor: compat필드를 사용해요. - 호환성-dev 변형은 호환성 패키지와 dev 도구를 결합해요.
일부 이미지는 sfw(software framework) 변형 같은 추가 flavor도 지원해요. 각 이미지의 사용 가능한 변형 전체 목록은 카탈로그를 참조해요.
해보기: 카탈로그 이미지 빌드
자체 정의를 작성하기 전에 GitHub에서 기존 카탈로그 이미지를 직접 빌드해 봐요.
$ docker buildx build \
https://raw.githubusercontent.com/docker-hardened-images/catalog/refs/heads/main/image/alpine-base/alpine-3.23/3.23.yaml \
--sbom=generator=dhi.io/scout-sbom-indexer:1 \
--provenance=1 \
--tag my-alpine-base:3.23 \
--load
이 명령은 GitHub에서 정의 파일을 직접 다운로드해 로컬에서 빌드해요. 빌드가 완료된 후 이미지를 확인해요.
$ docker images my-alpine-base
이미지를 수정하려면 카탈로그를 클론하고 YAML 파일을 로컬에서 편집해요.
$ git clone https://github.com/docker-hardened-images/catalog.git
$ cd catalog
YAML 스키마 참조
다음 섹션은 DHI 정의 파일에서 사용할 수 있는 필드를 설명해요.
필수 필드
모든 정의에는 다음 최상위 필드가 포함되어야 해요.
| 필드 | 설명 |
|---|---|
name |
이미지의 사람이 읽을 수 있는 이름. |
image |
전체 레지스트리 경로(예: dhi.io/my-image). |
variant |
이미지 변형 유형: runtime 또는 dev. |
tags |
이미지 태그 목록. |
platforms |
대상 아키텍처(예: linux/amd64와 linux/arm64). |
contents |
설치할 패키지 저장소와 패키지. |
이미지 메타데이터
이 필드들은 이미지에 메타데이터를 추가해요.
| 필드 | 설명 |
|---|---|
os-release |
이미지 내부의 /etc/os-release 내용을 정의. |
annotations |
설명과 라이선스 같은 OCI 이미지 annotations. |
dates |
릴리스 날짜와 수명 종료 날짜. |
vars |
템플릿용 빌드 시간 변수. |
flavor |
호환성 이미지용 compat 같은 이미지 flavor 수정자. |
컨테이너 구성
이 필드들은 컨테이너가 실행되는 방식을 제어해요.
| 필드 | 설명 |
|---|---|
accounts |
사용자, 그룹, run-as 사용자. |
environment |
환경 변수. |
entrypoint |
컨테이너 엔트리포인트 명령. |
cmd |
기본 명령 인자. |
work-dir |
컨테이너 내부의 작업 디렉터리. |
volumes |
볼륨 마운트 지점. |
ports |
노출된 네트워크 포트. |
paths |
만들 디렉터리, 파일, 심볼릭 링크. |
고급 필드
이 필드들은 더 복잡한 빌드 패턴을 지원해요.
| 필드 | 설명 |
|---|---|
contents.builds |
셸 파이프라인이 있는 빌드 단계. |
contents.keyring |
서드파티 패키지 저장소용 서명 키. |
contents.artifacts |
포함할 미리 빌드된 OCI 아티팩트. |
contents.mappings |
SBOM 정확성을 위한 Package URL(purl) 매핑. |
contents.files |
체크섬과 함께 Git URL에서 가져온 소스 파일. |
최소 이미지 만들기
가장 단순한 정의인 non-root 사용자가 있는 Alpine 베이스 이미지로 시작해요.
프로젝트용 디렉터리를 만들고 base.yaml 파일을 추가해요.
# syntax=dhi.io/build:2-alpine3.23
name: My Base Image
image: my-registry/my-base
variant: runtime
tags:
- "1.0.0"
- "1.0"
platforms:
- linux/amd64
- linux/arm64
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.23/main
- https://dl-cdn.alpinelinux.org/alpine/v3.23/community
packages:
- alpine-baselayout-data
- busybox
- ca-certificates-bundle
accounts:
run-as: nonroot
users:
- name: nonroot
uid: 65532
gid: 65532
groups:
- name: nonroot
gid: 65532
members:
- nonroot
os-release:
name: Docker Hardened Images (Alpine)
id: alpine
version-id: "3.23"
pretty-name: My Hardened Image
home-url: https://docker.com/products/hardened-images/
bug-report-url: https://docker.com/support/
environment:
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
annotations:
org.opencontainers.image.description: A minimal Alpine base image
cmd:
- /bin/sh
이 정의에서:
contents.repositories는 Alpine 패키지 미러의 전체 URL을 사용해요.contents.packages는 정확한 Alpine 패키지 이름을 나열해요.accounts블록은nonroot사용자(UID 65532)를 만들고 컨테이너의 기본 사용자로 설정해요.os-release블록은/etc/os-release에 나타나는 내용을 정의해요. 항상home-url과 함께bug-report-url을 포함하세요.annotations블록은 레지스트리와 Docker Scout 보고서에 보이는 OCI 메타데이터를 추가해요.
이미지를 빌드해요.
$ docker buildx build . -f base.yaml \
--sbom=generator=dhi.io/scout-sbom-indexer:1 \
--provenance=1 \
--tag my-base:latest \
--load
[!NOTE]
스펙 파일의
tags필드는 이미지 메타데이터(이미지 매니페스트에 포함된 변형 및 버전 레이블)를 정의해요. CLI의--tag플래그는 이미지를 푸시하거나 로드하는 데 사용되는 OCI 이미지 참조를 설정해요. 이 둘은 다른 목적을 갖습니다 — 스펙 파일 태그는 이미지가 무엇인지를 설명하고, CLI 태그는 어디에 저장되는지를 결정해요.
서드파티 저장소가 있는 Debian 베이스 사용
Debian 패키지나 서드파티 APT 저장소가 필요한 애플리케이션에는 Debian syntax 지시문을 사용해요. 다음 예시는 공식 Redis APT 저장소에서 Redis 이미지를 빌드해요.
redis.yaml 파일을 만들어요.
# syntax=dhi.io/build:2-debian13
name: Redis 8.0.x
image: my-registry/my-redis
variant: runtime
tags:
- "8.0"
- "8.0.5"
platforms:
- linux/amd64
- linux/arm64
contents:
repositories:
- deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb trixie main
keyring:
- https://packages.redis.io/gpg
packages:
- '!libelogind0'
- '!mawk'
- '!original-awk'
- base-files
- libpcre2-8-0
- libssl3t64
- libstdc++6
- libsystemd0
- redis=6:8.0.5-1rl1~trixie1
- redis-server=6:8.0.5-1rl1~trixie1
- redis-tools=6:8.0.5-1rl1~trixie1
- tini
mappings:
redis: pkg:deb/redis/redis@6:8.0.5-1rl1~trixie1?os_name=debian&os_version=13
redis-server: pkg:deb/redis/redis-server@6:8.0.5-1rl1~trixie1?os_name=debian&os_version=13
redis-tools: pkg:deb/redis/redis-tools@6:8.0.5-1rl1~trixie1?os_name=debian&os_version=13
accounts:
run-as: nonroot
users:
- name: nonroot
uid: 65532
gid: 65532
groups:
- name: nonroot
gid: 65532
members:
- nonroot
os-release:
name: Docker Hardened Images (Debian)
id: debian
version-id: "13"
version-codename: trixie
pretty-name: Docker Hardened Images/Debian GNU/Linux 13 (trixie)
home-url: https://docker.com/products/hardened-images/
bug-report-url: https://docker.com/support/
work-dir: /data
environment:
REDIS_VERSION: 8.0.5
annotations:
org.opencontainers.image.description: A minimal Redis image
org.opencontainers.image.licenses: AGPL-3.0-only
entrypoint:
- /usr/bin/tini
- --
cmd:
- redis-server
- /etc/redis/redis.conf
- --include
- /etc/redis/conf.d/*.conf
이 예시는 여러 패턴을 소개해요.
- 서드파티 저장소:
repositories필드는 APT 소스용 Debiandeb [signed-by=...] URL suite component형식을 사용해요. - Keyring:
keyring필드는 서드파티 저장소의 패키지를 검증하는 데 사용되는 GPG 키를 다운로드해요. - 패키지 제외:
!를 패키지 이름 앞에 붙여 명시적으로 제외해요. 이렇게 하면 원치 않는 의존성이 설치되지 않아요. 이 경우!libelogind0,!mawk,!original-awk가 제외돼요. - Debian 버전 고정: 전체 epoch 형식
redis-server=6:8.0.5-1rl1~trixie1을 사용해 정확한 패키지 버전을 고정해요. - SBOM 매핑:
mappings필드는 Package URL(purl) 메타데이터를 제공해 Docker Scout가 SBOM에서 소프트웨어를 정확히 식별할 수 있게 해줘요. - Init 프로세스:
entrypoint는 PID 1에서 신호 전달과 좀비 프로세스 재생을 처리하는 경량 init 프로세스로tini를 사용해요. - 구성 포함:
cmd는--include /etc/redis/conf.d/*.conf를 사용해paths섹션에서 만든 구성 파일이 시작 시 로드되도록 해요.
경로 만들기
paths 필드를 사용해 이미지 내부에 디렉터리, 인라인 콘텐츠가 있는 파일, 심볼릭 링크를 만들어요. 다음 예시는 운영에 필요한 경로로 Redis 정의를 확장해요.
paths:
- type: directory
path: /var/lib/redis
uid: 65532
gid: 65532
mode: "0755"
- type: directory
path: /var/log/redis
uid: 65532
gid: 65532
mode: "0755"
- type: directory
path: /run/redis/
uid: 65532
gid: 65532
mode: "0755"
- type: directory
path: /data
uid: 65532
gid: 65532
mode: "0755"
- type: file
path: /etc/redis/conf.d/docker.conf
content: |
daemonize no
bind 0.0.0.0 -::1
logfile ""
uid: 0
gid: 0
mode: "0555"
- type: symlink
path: /usr/bin/redis-sentinel
uid: 0
gid: 0
source: /usr/bin/redis-check-rdb
세 가지 경로 유형을 사용할 수 있어요.
| 유형 | 필수 필드 | 설명 |
|---|---|---|
directory |
path, uid, gid, mode |
빈 디렉터리를 만듦. |
file |
path, content, uid, gid, mode |
인라인 콘텐츠로 파일을 만듦. |
symlink |
path, source, uid, gid |
심볼릭 링크를 만듦. |
mode 필드는 소유자에게 읽기-쓰기-실행을 뜻하는 "0755" 또는 모두에게 읽기-실행을 뜻하는 "0555" 같은 8진수 권한 비트의 문자열 표현을 사용해요. file 유형은 YAML 여러 줄 문자열을 사용한 인라인 content를 지원한다는 점을 유의하세요.
빌드 단계 추가
파일 구성, 심볼릭 링크 생성, 권한 조정처럼 빌드 중 셸 명령을 실행해야 하는 이미지에는 contents.builds 필드를 사용해요. 각 빌드 단계는 자체 패키지, 이름 있는 단계의 파이프라인, 출력 매핑을 가져요.
다음 예시는 빌드 중 Nginx를 권한 없는 포트에서 실행하도록 구성하고 server tokens를 비활성화해요.
# syntax=dhi.io/build:2-alpine3.23
name: Nginx mainline
image: my-registry/my-nginx
variant: runtime
tags:
- "1.29"
platforms:
- linux/amd64
- linux/arm64
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.23/main
- https://dl-cdn.alpinelinux.org/alpine/v3.23/community
- http://nginx.org/packages/mainline/alpine/v3.23/main
keyring:
- https://nginx.org/keys/nginx_signing.rsa.pub
packages:
- alpine-baselayout-data
- busybox
- musl-utils
- nginx=1.29.5-r1
builds:
- name: nginx
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.23/main
- https://dl-cdn.alpinelinux.org/alpine/v3.23/community
- http://nginx.org/packages/mainline/alpine/v3.23/main
keyring:
- https://nginx.org/keys/nginx_signing.rsa.pub
packages:
- alpine-baselayout-data
- bash
- musl-utils
- nginx=1.29.5-r1
pipeline:
- name: install
runs: |
set -eux -o pipefail
ln -sf /dev/stdout /var/log/nginx/access.log
ln -sf /dev/stderr /var/log/nginx/error.log
sed -i "s,listen 80;,listen 8080;," /etc/nginx/conf.d/default.conf
sed -i "/user nginx;/d" /etc/nginx/nginx.conf
sed -i "s,pid /run/nginx.pid;,pid /var/run/nginx.pid;," /etc/nginx/nginx.conf
sed -i '/^http {$/a\ server_tokens off;' /etc/nginx/nginx.conf
chown -R 65532:65532 /var/cache/nginx
chmod -R g+w /var/cache/nginx
chown -R 65532:65532 /etc/nginx
chmod -R g+w /etc/nginx
chown -R 65532:65532 /run
chown -R 65532:65532 /run/lock
chown -R 65532:65532 /var/run
chown -R 65532:65532 /var/log/nginx
outputs:
- source: /
target: /
uid: 0
gid: 0
diff: true
accounts:
run-as: nginx
users:
- name: nginx
uid: 65532
gid: 65532
groups:
- name: nginx
gid: 65532
members:
- nginx
- name: www-data
gid: 82
os-release:
name: Docker Hardened Images (Alpine)
id: alpine
version-id: "3.23"
pretty-name: Docker Hardened Images/Alpine Linux v3.23
home-url: https://docker.com/products/hardened-images/
bug-report-url: https://docker.com/support/
environment:
NGINX_VERSION: 1.29.5-r1
annotations:
org.opencontainers.image.description: A minimal Nginx image
org.opencontainers.image.licenses: BSD-2-Clause
entrypoint:
- nginx
cmd:
- -g
- daemon off;
ports:
- 8080/tcp
이 정의의 핵심 패턴:
| 요소 | 설명 |
|---|---|
contents |
각 빌드 단계는 자체 contents 섹션을 가짐. bash처럼 빌드 중에만 필요한 패키지를 포함. |
pipeline |
셸 명령을 실행하는 이름 있는 단계를 포함. 항상 set -eux -o pipefail로 스크립트를 시작. |
outputs |
빌드 단계의 결과를 최종 이미지로 복사. diff: true를 설정하면 변경된 파일만 복사해 이미지를 최소화. |
accounts |
Nginx는 nonroot 대신 전용 nginx 사용자(UID 65532)를 사용. 웹 서버 호환성을 위해 www-data 그룹(GID 82)도 생성. |
musl-utils |
Alpine 기반 Nginx 이미지의 메인 패키지와 빌드 패키지 모두에 필요. |
OCI 아티팩트를 패키지 소스로 사용
Alpine 또는 Debian 저장소에서 패키지를 설치하는 대신 DHI 패키지 아티팩트에서 미리 빌드된 바이너리를 풀할 수 있어요. 이것이 카탈로그가 Python과 Node.js 같은 이미지를 빌드하는 방식이에요 — 런타임을 별도로 컴파일해 OCI 아티팩트로 게시한 다음, 이미지 정의에서 digest로 참조해요.
contents 아래에 artifacts 필드를 추가해요.
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.23/main
- https://dl-cdn.alpinelinux.org/alpine/v3.23/community
packages:
- alpine-baselayout-data
- bzip2
- ca-certificates-bundle
- expat
- gdbm
- libffi
- mpdecimal
- musl
- ncurses
- openssl
- readline
- sqlite-libs
- tzdata
- zlib
artifacts:
- name: dhi.io/pkg-python:3.13.12-alpine3.23@sha256:052b3b915055006a27c42470eed5c65d7ee92d2c3de47ecaedcc6bbd36077b95
includes:
- opt/**
uid: 0
gid: 0
| 필드 | 설명 |
|---|---|
name |
digest 핀이 있는 전체 OCI 참조. 재현성을 위해 항상 @sha256:을 사용. |
includes |
아티팩트에서 추출할 파일의 Glob 패턴. 경로는 파일시스템 루트에서 해석되며, opt/**는 /opt 경로 아래의 모든 것을 포함. |
excludes |
건너뛸 파일의 Glob 패턴. 헤더, 문서, 사용하지 않는 바이너리를 제거하는 데 유용. |
uid, gid |
추출된 파일의 소유권. |
사용 가능한 DHI 패키지는 카탈로그 저장소의 package/ 디렉터리에 있어요.
dev 변형 만들기
이미지의 dev 변형은 셸, 패키지 매니저, 개발 도구를 추가해요. 디버깅과 멀티 스테이지 워크플로의 빌드 단계로 사용하는 데 유용해요.
dev 변형을 만들려면 variant 필드를 변경하고 root 접근을 활성화해요.
# syntax=dhi.io/build:2-alpine3.23
name: Alpine 3.23 Base (dev)
image: my-registry/my-base
variant: dev
tags:
- "1.0-dev"
platforms:
- linux/amd64
- linux/arm64
contents:
repositories:
- https://dl-cdn.alpinelinux.org/alpine/v3.23/main
- https://dl-cdn.alpinelinux.org/alpine/v3.23/community
packages:
- alpine-baselayout-data
- apk-tools
- busybox
- ca-certificates-bundle
accounts:
root: true
run-as: root
users:
- name: nonroot
uid: 65532
gid: 65532
groups:
- name: nonroot
gid: 65532
members:
- nonroot
os-release:
name: Docker Hardened Images (Alpine)
id: alpine
version-id: "3.23"
pretty-name: Docker Hardened Images/Alpine Linux v3.23
home-url: https://docker.com/products/hardened-images/
bug-report-url: https://docker.com/support/
environment:
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
annotations:
org.opencontainers.image.description: A minimal Alpine base image
cmd:
- /bin/sh
runtime 변형과의 핵심 차이:
variant: runtime대신variant: dev.accounts.root: true가 root 계정을 활성화.run-as: root가 root를 기본 사용자로 설정.- 패키지에
apk-tools를 추가해 이미지에 패키지 매니저를 부여. nonroot사용자는 여전히 정의되어 있어 애플리케이션이 런타임에서 권한 없는 사용자로 전환할 수 있음.
Debian 기반 dev 변형의 경우 apk-tools 대신 apt를 추가하고 DEBIAN_FRONTEND: noninteractive 환경 변수를 포함하세요.
호환성 변형 만들기
호환성 변형은 표준 Linux 사용자 영역을 기대하는 스크립트와 자동화 도구와 함께 사용하기 위한 일반적인 셸 유틸리티를 포함해요. 호환성 이미지는 flavor 필드를 사용해요.
variant: runtime
flavor: compat
호환성 변형은 애플리케이션 패키지와 함께 bash, coreutils, findutils, grep, hostname, openssl, procps, sed 같은 패키지를 추가해요. 호환성-dev 변형은 호환성 패키지와 dev 도구를 모두 결합해요.
variant: dev
flavor: compat
호환성 패턴의 완전한 예시는 카탈로그의 Redis 호환성 이미지를 참조해요.
포트와 볼륨 설정
ports 필드를 사용해 컨테이너가 노출하는 포트를 선언해요. 컨테이너가 non-root 사용자로 실행될 때는 항상 권한 없는 포트(1024보다 높은)를 사용하세요.
ports:
- 8080/tcp
volumes 필드를 사용해 볼륨 마운트 지점을 선언해요.
volumes:
- /data
Annotations 설정
OCI annotations는 이미지에 기계가 읽을 수 있는 메타데이터를 추가해요. annotations 필드를 사용해요.
annotations:
org.opencontainers.image.description: A minimal hardened application image
org.opencontainers.image.licenses: Apache-2.0
이 annotations는 Docker Scout 보고서와 컨테이너 레지스트리 인터페이스에 나타나요.
빌드와 검증
이미지 빌드
로컬 테스트용 단일 플랫폼 이미지를 빌드해요.
$ docker buildx build . -f my-image.yaml \
--sbom=generator=dhi.io/scout-sbom-indexer:1 \
--provenance=1 \
--tag my-image:latest \
--load
SBOM 검사
생성된 소프트웨어 자재명세서를 봐요.
$ docker scout sbom my-image:latest
취약점 스캔
알려진 CVE 데이터베이스에 대해 이미지를 확인해요.
$ docker scout cves my-image:latest
비강화 이미지와 비교
동등한 비강화 이미지에 대한 보안 개선을 측정해요.
$ docker scout compare my-image:latest \
--to <non-hardened-equivalent>:<tag> \
--platform linux/amd64
<non-hardened-equivalent>를 비교하려는 Docker Official Image 또는 커뮤니티 이미지로 바꿔요.
Docker Debug로 검사
os-release와 엔트리포인트 구성을 검증해요.
$ docker debug my-image:latest
출력은 os-release 구성에서 감지한 배포판 이름을 보여주고 엔트리포인트 lint 검사를 실행해요.
레지스트리로 푸시
이미지를 컨테이너 레지스트리에 태그·푸시해요.
$ docker tag my-image:latest <your-namespace>/my-image:latest
$ docker push <your-namespace>/my-image:latest
<your-namespace>를 Docker Hub 사용자 이름 또는 조직 네임스페이스로 바꿔요.
카탈로그에 기여하기
Docker Hardened Images는 오픈소스 프로젝트예요. 카탈로그 저장소에 pull request를 제출해 새 이미지 정의를 기여하거나 기존 것을 개선할 수 있어요.
새 이미지를 기여하려면:
- 카탈로그 저장소를 포크해요.
image/<image-name>/<distribution>/명명 규칙에 따라image/아래에 디렉터리를 만들어요.- YAML 정의 파일을 추가해요(변형당 하나).
- 표시 이름, 설명, 카테고리가 있는
info.yaml을 추가해요. - 이미지를 설명하는
overview.md를 추가해요. - 이미지 아이콘용
logo.svg를 추가해요. - 사용 문서가 있는
guides.md를 추가해요. main브랜치에 대한 pull request를 열어요.
자세한 내용은 카탈로그 저장소의 contributing guide를 읽어보세요.