빌드가 캐시 키를 계산하는 단계에서 멈춘다.
ERROR: failed to solve: failed to compute cache key:
failed to calculate checksum of ref ...: "/bin/bash": not found
... : "/bin/sh": not found
두 메시지는 다른 상황을 가리킨다.
Alpine 계열에는 bash 가 들어 있지 않고 BusyBox 의 sh 만 있다. RUN 은 기본적으로 /bin/sh -c 로 실행되므로 평소에는 문제가 없지만, SHELL ["/bin/bash", "-c"] 를 지정했거나 ENTRYPOINT ["/bin/bash", ...] 를 쓰면 거기서 걸린다.
bash 문법(배열, [[ ]], pipefail)을 쓰지 않는다면 sh 로 바꾸는 것이 가볍다. 필요하면 설치한다.
RUN apk add --no-cache bash # Alpine
RUN apt-get update && apt-get install -y bash \
&& rm -rf /var/lib/apt/lists/* # 데비안 계열
BusyBox 의 sh 는 POSIX 범위를 넘는 문법을 모른다. 스크립트를 그대로 가져왔다가 조용히 다르게 동작하는 경우가 있으므로, 옮길 때는 set -eu 를 붙이고 결과를 확인한다.
scratch 나 distroless 계열, 그리고 그와 비슷하게 만든 제품 이미지는 셸을 담고 있지 않다. 이런 이미지에서는 RUN 이 아예 동작하지 않는다. RUN 은 이미지 안의 셸을 빌려 쓰기 때문이다.
이런 이미지를 베이스로 무언가를 설치하려는 시도 자체가 방향이 틀렸다. 선택지는 셋이다.
셸이 있는 변형 태그를 쓴다. 많은 제품이 Alpine 기반 태그를 함께 배포한다. 관리나 디버깅 목적이라면 이 방법이 가장 단순하다.
멀티스테이지로 필요한 것만 넣는다. 빌드는 도구가 갖춰진 이미지에서 하고, 최종 단계에는 실행 파일만 복사한다.
FROM debian:bookworm-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends gcc \
&& rm -rf /var/lib/apt/lists/*
COPY hello.c .
RUN gcc -static -o /hello hello.c
FROM gcr.io/distroless/static
COPY --from=builder /hello /hello
ENTRYPOINT ["/hello"]
셸을 억지로 넣지 않는다. 다른 이미지에서 /bin/sh 와 /lib 를 복사해 오는 방법이 돌아다니지만, Alpine 은 musl 이고 데비안은 glibc 라 라이브러리 체계가 달라 대부분 깨진다. 우연히 도는 것처럼 보여도 재현하기 어려운 오류를 낳는다.
셸이 없는 컨테이너를 들여다봐야 한다면 이미지를 바꾸는 대신 임시 컨테이너를 붙인다. 방법은 셸 없는 컨테이너 디버깅과 파드 종료 원인 추적 에 있다.
패키지 이름이 데비안 계열과 다른 것들이 있다.
| 필요한 것 | Alpine 패키지 |
|---|---|
telnet, tftp 등 |
busybox-extras |
tput, clear |
ncurses |
| ncurses 라이브러리만 | ncurses-libs |
| 빌드용 헤더 | ncurses-dev |
ps, top 확장판 |
procps |
dig, nslookup |
bind-tools |
curl |
curl (BusyBox 의 wget 은 기본 포함) |
RUN apk add --no-cache bash busybox-extras bind-tools
--no-cache 를 붙이면 인덱스가 이미지에 남지 않아 별도의 정리 단계가 필요 없다.