Linux 컨테이너 내 /bin의 바이너리를 바꾸는 방법

Linux 컨테이너 내 /bin의 바이너리를 바꾸는 방법

나는 이상한 컨테이너를 만들고 싶은데 내 작업 중 하나는 uname바이너리를 교체하는 것입니다.
나는 그것을 코딩했다 :

#include <stdio.h>
int main() {
   printf("This is a fake uname\n");
   return 0;
}  

컴파일:

gcc uname.c -o uname

우분투에서 실행하면 잘 작동합니다.

하나를 만들어 Dockerfile이미지에 복사했습니다.

cat > Dockerfile <<EOF
FROM alpine:latest
RUN rm /bin/uname
COPY uname /bin/
ENTRYPOINT sh;
WORKDIR /home
EOF  

그리고 그것을 구축docker build -t myimage -f Dockerfile .

이미지를 실행하면:

docker run -it --rm myimage  

파일이 존재하지만 실행하려고 하면 존재하지 않는다고 기록됩니다.

/home # uname
sh: uname: not found
/home # ls -lia uname
ls: uname: No such file or directory
/home # ls -lia /bin/uname
     35 -rwxr-xr-x    1 root     root          8600 Jan 29 13:27 /bin/uname

답변1

문제는 (Ubuntu를 사용하고 있기 때문에) 바이너리를 빌드한 다음 Alpine(Ubuntu를 사용하고 있기 때문에 ) glibc에서 실행한다는 것입니다.musl

바이너리를 빌드하면 일부 메타데이터가 하드코딩됩니다.통역사(동적 링커) 위치. 바이너리 실행 중에 커널은 이 인터프리터를 사용하여 바이너리와 모든 런타임 종속성을 로드합니다. 인터프리터 에 대해 빌드할 때 glibc바이너리는 다음을 요청합니다 ( 와 유사 ).glibc/lib64/ld-linux-x86-64.so.2

$ readelf -l /bin/uname
<...>
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

하지만 알파인에는 그런 통역사가 없습니다. 대신, /lib/ld-musl-x86_64.so.1Alpine의 모든 바이너리는 이 인터프리터를 요청하도록 구축되었습니다.

# readelf -l /bin/busybox
<...>
      [Requesting program interpreter: /lib/ld-musl-x86_64.so.1]

따라서 unameUbuntu에서 빌드를 빌드한 다음 Alpine에서 실행하면 커널이 인터프리터를 찾을 수 없으므로 "찾을 수 없음".

흥미롭게도 통역사를 수동으로 호출할 수 있는데, 이것이 문제인 경우 작동할 수 있습니다.오직통역사에서:

# /lib/ld-musl-x86_64.so.1 /bin/uname 
This is a fake uname

그러나 이는 물론 문제 해결 목적일 뿐입니다.

그래서 뭐 할까?

옵션은 바이너리를 정적으로 연결하는 것입니다(이렇게 하면 커널이 자체적으로 바이너리를 로드하고 실행할 수 있도록 인터프리터와 모든 공유 라이브러리가 필요하지 않습니다).

gcc uname.c -static -o uname

아니면 Alpine에서 빌드하세요(아마도 Docker의다단계 구축? 이 경우에는 의도를 명확하게 명시하고 원래 Alpine의 바이너리를 대체하기 위해 Alpine에서 사용자 정의 바이너리를 구축하고 있음을 나타내기 때문에 이 접근 방식을 권장합니다.

관련 정보