LabHub

컨테이너 내부 원리 · OCI 이미지 포맷 · 실습

이미지를 해체해서 들여다보기

LabHub 에서 이어서 보기

이 실습은 진짜 VM 에서 돕니다

이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이
따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라
진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가
되고 docker execdocker logs 도 그대로 동작합니다.

예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라
컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어
보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.

알아 둘 것이 둘 있습니다.

목표

이미지를 아카이브로 내보내 안에 든 매니페스트와 config 블롭을 직접 열어 보고,
RootFS.Layers(diff_id)와 매니페스트의 레이어 다이제스트가 어떻게 다른 층의
값인지 손으로 확인합니다. 마지막에는 같은 베이스에서 만든 두 이미지가 첫 레이어를
공유한다는 것을 다이제스트 비교로 증명합니다.

왜 중요한가

이미지 관련 사고는 대부분 "태그를 믿었기 때문"에 일어납니다. 태그는 사람이 붙인
이름이고 언제든 다른 다이제스트를 가리키도록 바뀝니다. 반대로 다이제스트는 내용의
해시라서 같은 해시면 같은 내용이 보장됩니다. 이 구분을 손으로 확인해 두면 배포
파이프라인에서 무엇을 고정해야 하는지가 분명해집니다. 또 하나, 매니페스트에 적힌
레이어 다이제스트는 압축본의 해시이고 config 의 rootfs.diff_ids 는 **압축을 푼
tar 의 해시**입니다. 둘이 다르다는 것을 모르면 "같은 이미지인데 해시가 다르다"는
착각에서 시간을 오래 씁니다. 그래서 이 실습은 두 값을 각각 다른 파일에서 꺼내
봅니다.

단계

1. /root/int3 디렉터리를 만들고, alpine:3.20 이미지의 ID(콘텐츠 해시)를 /root/int3/image-id.txt 에 저장합니다. 64자리 16진수여야 하며 sha256: 접두어는 붙어 있어도 됩니다.
2. nginx:1.27-alpine 의 rootfs 레이어 다이제스트를 한 줄에 하나씩 /root/int3/layers.txt 에 저장합니다. 모든 줄이 sha256: 로 시작하는 64자리여야 하고 줄 수가 실제 레이어 개수와 같아야 합니다.
3. alpine:3.20 이미지를 아카이브로 내보내 /root/int3/alpine.tar 로 저장합니다. docker save 는 이 상자에서 동작하지 않으므로 skopeo copy oci-archive:/opt/images/alpine_3.20.tar oci-archive:/root/int3/alpine.tar:alpine:3.20 을 씁니다 — 하는 일은 같습니다. 안에 manifest.json(또는 index.json/oci-layout)과 해시 이름의 블롭이 들어 있어야 합니다.
4. 그 아카이브에서 manifest.json 을 꺼내 /root/int3/manifest.json 으로 저장합니다. 유효한 JSON 이어야 하고 레이어 목록이 1개 이상 들어 있어야 합니다.
5. 4단계 매니페스트의 Config 필드가 가리키는 config 블롭 파일을 아카이브에서 꺼내 /root/int3/config.json 으로 저장합니다. 이 파일에는 architecture 가 있어야 하고 rootfs.typelayers, rootfs.diff_ids 가 1개 이상이어야 합니다.
6. skopeo 로 로컬 이미지 저장소의 nginx:1.27-alpine 정보를 조회해 /root/int3/skopeo.json 에 저장합니다. 저장소가 비어 있으므로 먼저 skopeo copy --insecure-policy oci-archive:/opt/images/nginx_1.27-alpine.tar containers-storage:docker.io/library/nginx:1.27-alpine 으로 넣으세요 — podman load 는 이 상자에서 막혀 있지만 skopeo 는 네임스페이스를 만들지 않아 그대로 됩니다. Name 에 nginx 가 들어가고 Digestsha256: 형식이며 Layers 가 1개 이상이어야 합니다.
7. alpine:3.20 을 베이스로 쓰되 서로 다른 내용을 한 겹 얹은 이미지 두 개를 labhub/oci:alabhub/oci:b 태그로 빌드합니다. 두 이미지의 첫 레이어 다이제스트는 같고 마지막 레이어 다이제스트는 달라야 합니다.
8. /root/int3/oci.md 에 다음 네 줄을 정확히 이 형식으로 적습니다. 모두 nginx:1.27-alpine 기준입니다.

참고

단계 8개

  1. 이미지 ID 는 곧 콘텐츠 해시
  2. 레이어 다이제스트 목록 뽑기
  3. 이미지를 아카이브로 내보내기
  4. 아카이브 안의 매니페스트 열기
  5. config 블롭과 diff_ids
  6. 레지스트리 도구로 매니페스트 조회
  7. 베이스 레이어 공유 증명하기
  8. OCI 메타데이터 요약