LabHub
배우기 러닝패스 코스

イメージのビルド

キャッシュが壊れる場所を見つける

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

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

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

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

알아 둘 것이 둘 있습니다.

목표

캐시가 어디서 깨지는지 이미지 ID 로 증명하고, 유니온 파일시스템에서 삭제가 크기를 줄이지 못한다는 것을 두 이미지의 크기 차이로 확인합니다.

왜 중요한가

"빌드가 느려요"라는 문제는 캐시 설정을 켜고 끄는 문제가 아니라 순서 문제인 경우가 압도적으로 많습니다. 캐시 키가 부모 다이제스트에 연쇄로 묶여 있다는 사실 하나만 이해하면, 고칠 지점은 언제나 "캐시가 처음으로 깨지는 그 한 줄"이라는 것이 자명해집니다. 그 위는 건드릴 필요가 없고 그 아래는 손댈 방법이 없습니다. 크기도 마찬가지입니다. 레이어는 되감기지 않으므로, 최종 이미지에 들어가면 안 되는 것은 애초에 그 레이어에서 만들지 않아야 합니다.

단계

  1. /root/build2 를 만들고, nginx:1.27-alpine 의 레이어 개수를 세어 /root/build2/nginx-layers.txt 에 숫자만 적습니다.
  2. alpine:3.20 의 레이어별 히스토리를 잘리지 않게 /root/build2/alpine-history.txt 로 저장합니다. docker history 가 보여 주는 표는 이미지 config 블롭의 history 배열 그 자체입니다. 이 상자에서는 skopeo inspect --config oci-archive:/opt/images/alpine_3.20.tar | jq -r '.history[]' 로 같은 값을 읽습니다.
  3. /root/build2deps.txtsrc/main.txt 를 만들고, 두 파일을 각각 COPY 하는 Dockerfile 을 작성합니다. labhub/cache:v1 로 빌드한 뒤 이미지 ID 를 /root/build2/id1.txt 에 적고, 아무것도 바꾸지 않고 다시 빌드해 ID 를 /root/build2/id2.txt 에 적습니다. 두 값은 같아야 합니다.
  4. deps.txt 의 내용을 바꾸고 labhub/cache:v2 로 빌드해 ID 를 /root/build2/id3.txt 에 적습니다. id1.txt 와 달라야 합니다.
  5. /root/build2/ordered.Dockerfile 을 만들어 COPY deps.txtCOPY src/ 보다 먼저 나오게 하고 labhub/cache:v3 로 빌드합니다.
  6. /root/build2/big.log 를 만들고, /root/build2/.dockerignore 에 로그 파일 제외 규칙을 넣습니다. 컨텍스트 전체를 /ctx 로 복사하는 Dockerfile 로 labhub/cache:v4 를 빌드하면 이미지 안에 /ctx/deps.txt 는 있고 /ctx/big.log 는 없어야 합니다.
  7. 20MB 파일을 만든 뒤 다음 RUN 에서 지우는 이미지를 labhub/fat:bad 로, 같은 RUN 안에서 지우는 이미지를 labhub/fat:good 으로 빌드합니다.
  8. /root/build2/cache.md 에 아래 세 줄을 실제 값으로 적습니다.
bad_mb=<labhub/fat:bad 크기(MB)>
good_mb=<labhub/fat:good 크기(MB)>
diff_mb=<두 값의 차>

참고

이미지의 레이어 수 세기

/root/build2 를 만들고, nginx:1.27-alpine 의 레이어 개수를 세어 /root/build2/nginx-layers.txt 에 숫자만 적습니다.

inspect 결과의 RootFS.Layers 는 배열입니다. jq 로 길이를 구할 수 있습니다.

레이어별 명령 들여다보기

alpine:3.20 의 레이어별 히스토리를 잘리지 않게 /root/build2/alpine-history.txt 로 저장합니다. docker history 가 보여 주는 표는 이미지 config 블롭의 history 배열 그 자체입니다. 이 상자에서는 skopeo inspect --config oci-archive:/opt/images/alpine_3.20.tar | jq -r '.history[]' 로 같은 값을 읽습니다.

각 레이어가 어떤 명령으로 만들어졌고 얼마를 차지하는지 보여 주는 하위 명령이 있습니다. 잘리지 않게 저장하세요.

바뀐 게 없으면 같은 이미지

/root/build2deps.txtsrc/main.txt 를 만들고, 두 파일을 각각 COPY 하는 Dockerfile 을 작성합니다. labhub/cache:v1 로 빌드한 뒤 이미지 ID 를 /root/build2/id1.txt 에 적고, 아무것도 바꾸지 않고 다시 빌드해 ID 를 /root/build2/id2.txt 에 적습니다. 두 값은 같아야 합니다.

빌드를 두 번 하고 각각의 이미지 ID 를 파일에 남기세요. 모든 레이어가 캐시에 적중하면 결과 이미지도 동일합니다.

앞 레이어를 깨뜨려 보기

deps.txt 의 내용을 바꾸고 labhub/cache:v2 로 빌드해 ID 를 /root/build2/id3.txt 에 적습니다. id1.txt 와 달라야 합니다.

가장 위에서 COPY 하는 파일을 고치면 그 아래가 전부 다시 실행됩니다. 새 태그로 빌드하고 ID 를 비교하세요.

변경 빈도 순으로 배치하기

/root/build2/ordered.Dockerfile 을 만들어 COPY deps.txtCOPY src/ 보다 먼저 나오게 하고 labhub/cache:v3 로 빌드합니다.

거의 안 바뀌는 것(의존성 목록)을 위에, 자주 바뀌는 것(소스)을 아래에 둡니다. 채점은 두 COPY 의 행 번호를 비교합니다.

빌드 컨텍스트에서 빼기

/root/build2/big.log 를 만들고, /root/build2/.dockerignore 에 로그 파일 제외 규칙을 넣습니다. 컨텍스트 전체를 /ctx 로 복사하는 Dockerfile 로 labhub/cache:v4 를 빌드하면 이미지 안에 /ctx/deps.txt 는 있고 /ctx/big.log 는 없어야 합니다.

제외 규칙 파일은 빌드 컨텍스트 루트에 둡니다. 필요한 파일까지 빼면 빌드가 깨집니다.

삭제해도 줄지 않는다는 증거

20MB 파일을 만든 뒤 다음 RUN 에서 지우는 이미지를 labhub/fat:bad 로, 같은 RUN 안에서 지우는 이미지를 labhub/fat:good 으로 빌드합니다.

만들고 다음 RUN 에서 지운 이미지와, 만들고 같은 RUN 안에서 지운 이미지를 각각 빌드해 크기를 비교하세요.

측정값 정리

/root/build2/cache.md 에 아래 세 줄을 실제 값으로 적습니다.

세 값 모두 실제 이미지 크기에서 계산합니다. 1MB = 1048576 바이트 기준으로 정수 MB 를 적으세요.