Docker
如何替換 Linux 容器內 /bin 中的二進製文件
我想做一些奇怪的容器,我的任務之一是替換
uname
二進製文件。我編碼它:
#include <stdio.h> int main() { printf("This is a fake uname\n"); return 0; }
編譯它:
gcc uname.c -o uname
當我在我的 ubuntu 上執行它時它工作正常。
我創建了一個
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
問題是你建構你的二進製文件
glibc
(因為你使用的是 Ubuntu),然後在 Alpine 上執行它(而musl
不是使用它)。當您建構二進製文件時,一些元數據被硬編碼到其中,特別是解釋器(動態連結器)位置。在二進制執行期間,核心使用此解釋器來載入二進制及其所有執行時依賴項。當您針對 建構時
glibc
,二進製文件會請求 的glibc
解釋器(類似於/lib64/ld-linux-x86-64.so.2
):$ readelf -l /bin/uname <...> [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
但是Alpine下沒有這樣的解釋器。相反, Alpine 有
/lib/ld-musl-x86_64.so.1
,並且它的所有二進製文件都被建構,以便它們完全請求這個解釋器:# readelf -l /bin/busybox <...> [Requesting program interpreter: /lib/ld-musl-x86_64.so.1]
因此,當您
uname
在 Ubuntu 中建構然後在 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 的二進製文件)。