[Docker] 用 Dockerfile 建立新的 base image
最近讀書會在讀 Debug Hacks 這本書,
裡面有非常多要動手實作的部分,
想說就利用 docker 來弄一個 Linux,實驗就在上面操作~
不過裝好 gdb 跟一些套件之後,exit 後再 run 一次,剛剛裝的東西都不見了~
這才記起來 docker 每次 run 都是開始一個新的 container,
想要東西長久保存的話,有一種是用 docker commit,
不過大家比較推薦的是使用 Dockerfile 的方式,就來試一下吧~
參考資料:
stackoverflow: I lose my data when the container exits
stackoverflow: How can I inspect the file system of a failed `docker build`?
1. 建立 Dockerfile
先建立一個目錄,在裡面放一個叫做 Dockerfile 的文字檔,內容如下:
FROM ubuntu RUN apt-get install -y g++ gdb
這個 Dockerfile 裡面其實有個問題…
不過我們先不管,先往下一步前進吧~
2. 建立新的 base image
在剛剛 Dockerfile 的同一個目錄下,執行 docker build 指令:
testuser@localhost ~/Docker/debug_hacks $ docker build -t testuser/debug_hacks . Sending build context to Docker daemon 2.048 kB Step 0 : FROM ubuntu ---> 91e54dfb1179 Step 1 : RUN apt-get install g++ gdb ---> Running in 55a74cb5ac0b Reading package lists... Building dependency tree... Reading state information... Some packages could not be installed. This may mean that you have requested an impossible situation or if you are using the unstable distribution that some required packages have not yet been created or been moved out of Incoming. The following information may help to resolve the situation: The following packages have unmet dependencies: g++ : Depends: cpp (>= 4:4.8.2-1ubuntu6) but it is not going to be installed Depends: gcc (>= 4:4.8.2-1ubuntu6) but it is not going to be installed Depends: g++-4.8 (>= 4.8.2-5~) but it is not going to be installed Depends: gcc-4.8 (>= 4.8.2-5~) but it is not going to be installed gdb : Depends: libpython3.4 (>= 3.4~b1) but it is not going to be installed Recommends: libc-dbg E: Unable to correct problems, you have held broken packages. The command '/bin/sh -c apt-get install g++ gdb' returned a non-zero code: 100
一執行就失敗了… 看起來是安裝 g++ 和 gdb 時出了問題…
因為第一個 RUN 指令就失敗,所以沒有任何的中間暫存 container 被建立出來,
要找問題的話,直接 docker run 跑一個 container 起來:
docker run -it ubuntu /bin/bash
進到裡面,試試看執行 apt-get install g++ gdb,也是一樣的錯誤訊息~
仔細看一下,看起來是有些相關的模組版本太舊,
先執行 apt-get update 後,果然 apt-get install g++ gdb 就能成功了~
於是回頭來改 Dockerfile 變成下面這樣:
FROM ubuntu RUN apt-get update RUN apt-get upgrade -y RUN apt-get install -y g++ gdb
再重新執行一次 docker build 指令,就沒問題囉:
testuser@localhost ~/Docker/debug_hacks $ docker build -t testuser/debug_hacks .
Sending build context to Docker daemon 2.048 kB
Step 0 : FROM ubuntu
---> 91e54dfb1179
Step 1 : RUN apt-get update
---> Running in fb140c0a4787
Reading package lists...
---> 5f2e28d38083
Removing intermediate container fb140c0a4787
Step 2 : RUN apt-get upgrade -y
---> Running in 4306816ac798
Reading package lists...
The following packages will be upgraded:
libexpat1 sudo
2 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Need to get 413 kB of archives.
Removing intermediate container 4306816ac798
Step 3 : RUN apt-get install -y g++ gdb
---> Running in ff0712ac8eac
The following extra packages will be installed:
binutils cpp cpp-4.8 g++-4.8 gcc gcc-4.8 libasan0 libatomic1 libc-dev-bin
libc6-dbg libc6-dev libcloog-isl4 libgcc-4.8-dev libgmp10 libgomp1 libisl10
libitm1 libmpc3 libmpfr4 libpython3.4 libquadmath0 libstdc++-4.8-dev
libtsan0 linux-libc-dev manpages manpages-dev
Suggested packages:
binutils-doc cpp-doc gcc-4.8-locales g++-multilib g++-4.8-multilib
gcc-4.8-doc libstdc++6-4.8-dbg gcc-multilib make autoconf automake1.9
libtool flex bison gcc-doc gcc-4.8-multilib libgcc1-dbg libgomp1-dbg
libitm1-dbg libatomic1-dbg libasan0-dbg libtsan0-dbg libquadmath0-dbg
gdb-doc gdbserver glibc-doc libstdc++-4.8-doc man-browser
Recommended packages:
libc-dbg
The following NEW packages will be installed:
binutils cpp cpp-4.8 g++ g++-4.8 gcc gcc-4.8 gdb libasan0 libatomic1
libc-dev-bin libc6-dbg libc6-dev libcloog-isl4 libgcc-4.8-dev libgmp10
libgomp1 libisl10 libitm1 libmpc3 libmpfr4 libpython3.4 libquadmath0
libstdc++-4.8-dev libtsan0 linux-libc-dev manpages manpages-dev
0 upgraded, 28 newly installed, 0 to remove and 0 not upgraded.
---> d4384bb7ee31
Removing intermediate container ff0712ac8eac
Successfully built d4384bb7ee31
從上面的訊息裡面可以看到,它是每一個 RUN 指令都會建立出一個暫時的 container,
因此假設某個 RUN 指令出了問題,導致 build 失敗的話,
還是可以連進去那個暫時的 container 看看問題出在哪裡~
舉例來說,假設最後一行 RUN 指令沒有加上 -y 的話,
平常 apt-get 會在那邊等你輸入 Y/N,
但在 docker 裡沒人可以給你輸入 Y/N 了,就會失敗:
testuser@localhost ~/Docker/debug_hacks $ docker build -t testuser/debug_hacks_bad . Step 0 : FROM ubuntu ---> 91e54dfb1179 Step 1 : RUN apt-get update ---> 5f2e28d38083 Step 2 : RUN apt-get upgrade -y ---> 6dedca5268bc Step 3 : RUN apt-get install g++ gdb ---> Running in ea300fada053 After this operation, 142 MB of additional disk space will be used. Do you want to continue? [Y/n] Abort. The command '/bin/sh -c apt-get install g++ gdb' returned a non-zero code: 1
這時你可以連進去倒數第二次的 container (本例中是 6dedca5268bc),
這是倒數第二個指令成功完成後,建立出來的 container。
可以以這個 container 為基底,執行複製出來的 container,
來看看出了什麼問題,或是試試看你的修正有沒有效果:
docker run -it 6dedca5268bc /bin/bash
在執行上述指令進到 container 後,再試著執行 apt-get install g++ gdb,
就可以發現指令停在要輸入 Y/N 的畫面,因此這個就是問題了~
3. 以新建立的 image 為基底,執行新的 container
當 base image 建立成功後,就可以用一般的 docker run 指令,
來執行這個 container 了:
docker run -it testuser/debug_hacks /bin/bash