輕鬆的玩轉Docker的15個小技巧
Tip1
獲取最近執行容器的id 這是我們經常會用到的一個操作,按照官方示例,你可以這樣做(環境ubuntu):
這種方式在編寫指令碼的時候很有用,比如你想在指令碼中批量獲取id,然後進一步操作。但是這種方式要求你必須給ID賦值,如果是直接敲命令,這樣做就不太方便了。 這時,你可以換一種方式:
docker ps -l -q命令將返回最近執行的容器的id,通過設定別名(alias),dl命令就是獲取最近容器的id。這樣,就無需再輸入冗長的docker ps -l -q命令了。通過兩個斜引號“,可以獲取dl命令的值,也就是最近執行的容器的id。
Tip2
儘量在Dockerfile中指定要安裝的軟體,而不用Docker容器的shell直接安裝軟體。 說實話,我有時候也喜歡在shell中安裝軟體,也許你也一樣,喜歡在shell中把所有軟體安裝都搞定。但是,搞來搞去,最後還是發現,你還是需要在Doockerfile中指定安裝檔案。在shell中安裝軟體,你要這樣做:
然後輸入下面的命令來安裝檔案:
然後再呼叫exit:
退出docker容器,再給docker commit命令傳遞一個複雜的JSON字串來提交新的映象:
太麻煩了,不是嗎?還是在Dockerfile中指定安裝檔案吧,只要兩個步驟:
1.在一個小巧的Dockerfile中,指定當前操作的映象為FROM命令的引數
2.然後在Dockerfile中指定一些docker的命令,如CMD, ENTERPOINT, VOLUME等等來指定安裝的軟體
Tip3
超-超-超級使用者
你可能需要一直用超級使用者來操作docker,就像早期示例裡一直提示的:
Wow!連續三個sudo!三次化身“超級使用者”,真可謂是“超-超-超級使用者”啊!別擔心,設定完畢,以後你就再也不用打那麼多sudo了!
Tip4
清理垃圾
如果你想刪除所有停止執行的容器,用這個命令:
順便說一句,docker ps命令很慢,不知道為啥這麼慢,按理說Go語言是很快的啊。docker ps -a -q命令列出所有容器的id,然後根據id刪除容器。docker rm命令遇到正在執行的容器就會失效,所以這個命令完美的刪除了所有沒在執行的容器。
Tip5
docker inspect輸出結果的解析利器:jq 要對docker inspect的輸出結果進行過濾,一般情況下,用grep命令,你需要這樣操作:
哦!看上去很複雜,用jq吧,專業解析docker inspect輸出結果,具有更強的可讀性,方便易用:
其中第一個’.’代表所有的結果。’[0]’代表陣列的第一個元素。就像JavaScript訪問一個JSON物件一樣,簡單方便。
Tip6
映象有哪些環境變數? 有時候,你需要知道自己建立的映象有哪些環境變數。簡單!只要這樣:
輸出結果如下:
呼叫env檢視環境變數,對於後面要講到的“連結”(-link)很有用,在連線兩個容器時候需要用到這些環境變數,具體請看最後一個要點“連結”。
Tip7
RUN命令 vs CMD命令
Docker的新手使用者比較容易混淆RUN和CMD這兩個命令。 RUN命令在構建(Build)Docker時執行,這時CMD命令不執行。CMD命令在RUN命令執行時才執行。我們來理清關係,假設Dockerfile內容如下:
我們要向系統中安裝一些軟體,那麼:
Build時執行RUN,RUN時執行CMD,也就是說,CMD才是映象最終執行的命令。
Tip8
CMD命令 vs ENTRYPOINT命令
又是兩條容易混淆的命令!具體細節我們就不說了,舉個例子,假設一個容器的Dockerfile指定CMD命令,如下:
另一個容器的Dockerfile指定ENTRYPOINT命令,如下:
執行第一個容器:
得到的結果:
執行第二個容器:
得到的結果:
看到不同了吧?實際上,CMD命令是可覆蓋的,docker run後面輸入的命令與CMD指定的命令匹配時,會把CMD指定的命令替換成docker run中帶的命令。而ENTRYPOINT指定的命令只是一個“入口”,docker run後面的內容會全部傳給這個“入口”,而不是進行命令的替換,所以得到的結果就是“echo hello”。
Tip9
Docker容器有自己的IP地址嗎?
剛接觸Docker的人或許會有這樣的疑問:Docker容器有自己的IP地址嗎?Docker容器是一個程序?還是一個虛擬機器?嗯…也許兩者兼具?哈哈,其實,Docker容器確實有自己的IP,就像一個具有IP的程序。只要分別在主機和Docker容器中執行檢視ip的命令就知道了。
檢視主機的ip:
得到結果:
檢視Docker容器的ip:
得到結果:
兩者並不相同,說明Docker容器有自己的ip。
Tip10
基於命令列的瘦客戶端,使用UNIX Socket和Docker後臺服務的REST介面進行通訊。Docker預設是用UNIX socket通訊的,一直到大概0.5、0.6的版本還是用埠來通訊,但現在則改成UNIX socket,所以從外部無法控制Docker容器的內部細節。下面我們來搞點有趣的事情,從主機連結到docker的UNIX socket:
連線成功後,輸入:
輸入後連敲兩個回車,第二個回車表示輸入結束。然後,得到的結果應該是:
有一天,我不小心把提交的名稱打錯了,名字開頭打成”-xxx”(我把命令和選項的順序搞混了),所以當我刪除的時候出了問題,docker rm -xxx,會把-xxx當成引數而不是映象的名稱。所以我只得通過socket直接連到容器來呼叫REST Server把錯誤的東西刪掉。
Tip11
把映象的依賴關係繪製成圖
docker images命令有一個很拉風的選項:-viz,可以把映象的依賴關係繪製成圖並通過管道符號儲存到圖片檔案:
這樣,主機的當前路徑下就生成了一張png圖,然後,用python開啟一個微型的HTTP伺服器:
然後在別的機器上用瀏覽器開啟:
OK,依賴關係一目瞭然!
(譯者注:要使用dot命令,主機要安裝graphviz包。另外,如果主機ip沒有繫結域名,machinename換成主機的ip即可。)
Tip12
Docker把東西都存到哪裡去了? Docker實際上把所有東西都放到/var/lib/docker路徑下了。切換成super使用者,到/var/lib/docker下看看,你能學到很多有趣的東西。執行下面的命令:
可以看到不少目錄,containers目錄當然就是存放容器(container)了,graph目錄存放映象,檔案層(file system layer)存放在graph/imageid/layer路徑下,這樣你就可以看看檔案層裡到底有哪些東西,利用這種層級結構可以清楚的看到檔案層是如 何一層一層疊加起來的。
Tip13
Docker原始碼:Go, Go, Go, Golang! Docker的原始碼全部是用Go語言寫的。Go是一門非常酷的語言。其實,不只是Docker,很多優秀的軟體都是用Go寫的。對我來說,Docker原始檔中,有4個是我非常喜歡閱讀的:
commands.go docker的命令列介面,是對REST API的一個輕量級封裝。Docker團隊不希望在命令中出現邏輯,因此commands.go只是向REST API傳送指令,確保其較小的顆粒性。
api.go REST API的路由(接受commands.go中的請求,轉發到server.go)
server.go 大部分REST API的實現
buildfile.go Dockerfile的解析器
有的夥計驚歎”Wow!Docker是怎麼實現的?!我無法理解!”沒關係,Docker是開源軟體,去看它的原始碼就可以了。如果你不太清楚Dockerfile中的命令是怎麼回事,直接去看buildfile.go就明白了。
Tip14
執行幾個Docker後臺程式,再退出容器,會發生什麼? OK,倒數第二個要點。如果在Docker中執行幾個後臺程式,再退出Docker容器,會發生什麼?答案是:不要這麼做!因為這樣做後臺程式就全丟了。
Dockerfile中用RUN命令去開啟一個後臺程式,如:
這樣的話,RUN命令開啟的後臺程式就會丟失。呼叫容器的bash連到容器的shell:
然後呼叫 ps aux檢視程序,你會發現postgres的程序並沒有跑起來。 RUN命令會影響檔案系統。因此,不要再Dockerfile中用啟動後臺程式,要把後臺程式啟動成前臺程序。或者,像一些高手提議的那樣,寫一個啟動腳 本,在指令碼中啟動這些後臺程式或程序。
Tip15
容器之間進行友好溝通:連結
這是最拉風的功能!我把它留到最後壓軸!這是0.6.5中最重要的新功能,我們前面已經提過兩次了。執行一個容器,給它一個名稱,在下面的例子中,我們通過-name引數給容器指定名稱”loldb”:
再執行另一個容器,加上-link引數來連線到第一個容器(別名為loldb),並給第二個容器也指定一個別名(這裡用的是cheez):
順便得到cheez的環境變數:
這樣,我們就在兩個容器間建立起一個網路通道(bridge),基於此,我們可以建立一個類似rails的程式:一個容器可以訪問資料庫容器而不對外暴露其他介面。非常酷!資料庫容器只需要知道第一個容器的別名(在本例中為cheez)和要開啟的埠號。所以資料庫容器也可以env命令來檢視這個埠是否開啟。