1. 程式人生 > >Docker祕籍 之 建立史上最小Docker容器

Docker祕籍 之 建立史上最小Docker容器

注:此文參加Docker中文社群譯文懸賞活動,Docker中文社群轉載時可適當修改本譯文。

【轉載必須在正文中標註並保留原文連結、譯文連結、作者、譯者資訊、以及本條要求。】

譯者按:

  史上最小Docker容器長什麼樣子?怎麼建立的?做什麼用?來看此文!

scratch映象

  如果你使用Docker,你很快就會發現,當你採用預配置的容器時,需要下載的內容很大。一個簡單的Ubuntu容器很容易就超過了200MB,而且當其中附帶安裝好的軟體時,這個體積還要增加。在某些情況下,你其實不需要Ubuntu所帶的所有東西。例如,假如你想執行一個用Go語言編寫的簡單web server(經過權衡,server

一詞不翻譯更自然——譯者),那麼就不需要任何工具。

  我搜索了用來作為起始點的最小容器,發現了這個:docker pull scratch

  這個scratch(有草稿的意思,這裡可以看做專用詞彙,不翻譯)映象是極好的,幾乎完美!簡潔,體積小,速度快。它沒有bug、安全漏洞、遲鈍的程式碼、或者技術過失。這是因為它基本上是空的,除了一點兒由Docker新增的元資料。實際上,你可以用下面的命令(參見:Docker文件)自己建立這個scratch映象:

tar cv --files-from /dev/null | docker import - scratch

  所以,這就是體積最小的Docker

映象了。此文結束!嗯……或者還有些什麼東西可說的?比如,怎麼使用這個scratch映象?這還真有點兒挑戰性。

scratch映象新增內容

  在這個“空”映象上能執行什麼?回答是:沒有依賴的可執行程式。這樣的程式你有嗎?

  我經常用PythonJavaJavaScript寫程式。這些語言/平臺都需要安裝執行時。最近,我開始探索Go(或者你可以稱為GoLang)平臺。這平臺看上去是靜態連結的。我編譯了一個簡單的Hello World web server,並用scratch容器執行。下面列出了程式碼:

packagemain

import(

"fmt"

"net/http"

)

func

helloHandler(whttp.ResponseWriter,r*http.Request){

fmt.Fprintln(w,"Hello World from Go in minimal Docker container")

}

funcmain(){

http.HandleFunc("/",helloHandler)

fmt.Println("Started, serving at 8080")

err:=http.ListenAndServe(":8080",nil)

iferr!=nil{

panic("ListenAndServe: "+err.Error())

}

}

  很顯然,由於scratch容器裡不包括Go編譯器,我無法在裡面編譯我的webserver。我在Mac上工作,我也無法編譯一個linux可執行碼(實際上,是可以跨平臺編譯Go語言原始碼到另外平臺的,但這將是另外一篇博文的素材)。

  所以我需要一個包含Go編譯器的Docker容器。讓我們這樣做:

docker run -ti google/golang /bin/bash

  用這個容器編譯Go web server,並提交到GitHub庫:

go get github.com/adriaandejonge/helloworld

  go get命令是go build命令的變體,可以取來遠端依賴包並構建。通過下面命令執行構建結果:

$GOPATH/bin/helloworld

  這能正常工作。但這還不是我們想要的。我們想讓hello world容器在scratch容器裡執行。所以,實際上我們需要一個帶有如下內容的Dockerfile

FROM scratch

ADD bin/helloworld /helloworld

CMD["/helloworld"]

  然後執行它。不幸的是,我們執行google/golang容器的方式無法構建這個Dockerfile。所以,我們需要一個在這個容器裡面訪問Docker的方法。

Docker裡面調Docker

  當你使用Docker時,遲早會需要在Docker裡面控制Docker。有許多方法可以做到。你可以使用遞迴和Docker裡面執行Docker然而,這看上去太複雜了,而且會導致容器體積變大。你還可以通過下面附加的命令列選項來在Docker伺服器例項的外面訪問它:

docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) -ti google/golang /bin/bash

  在繼續之前,請先回到Go編譯器,因為Docker重啟之後已經忘了之前的編譯:

go get github.com/adriaandejonge/helloworld

  當啟動容器時,-v標誌會在Docker容器裡建立一個卷,並允許你在Docker虛擬機器裡提供一個檔案作為輸入。/var/run/docker.sock是個Unix套接字,能用來訪問Docker伺服器。用$(which docker)是個聰明的做法,它提供docker可執行程式碼在容器裡的路徑,而不需要硬編碼。然而,當你在蘋果系統上使用boot2docker時,要小心使用這個命令。如果docker可執行碼並沒有安裝在boot2docker虛擬機器而是其他地方時,這個命令是找不到docker可執行碼的。找到的是被新增到容器裡的在boot2docker虛擬機器中的可執行碼。所以,你可以用/usr/local/bin/docker硬編碼代替$(which docker)。類似地,你如果執行不同的系統,你有機會用/var/run/docker.sock來調整到不同的位置。

  現在你能在$GOPATH目錄(在這個例子裡指向/gopath)下,使用google/golang容器裡的Dockerfile了。我把這個Dockerfile 傳到了GitHub上,這樣你可以使用下面的程式碼將其從Go構建目錄拷貝到希望的位置:

cp $GOPATH/src/github.com/adriaandejonge/helloworld/Dockerfile $GOPATH

  你需要拷貝這個,因為編譯好的二進位制碼位於$GOPATH/bin,並且也不可能在構建Dockerfile的時候包括父目錄裡的檔案。所以下面執行這個:

docker build -t adejonge/helloworld $GOPATH

  如果都正常執行了,Docker會返回類似下面的資訊: 

Successfully built 6ff3fd5a381d

  這可以讓你執行容器了: 

docker run -ti --name hellobroken adejonge/helloworld

  但不幸的是,現在Docker返回這個: 

2014/07/02 17:06:48 no such file or directory

  怎麼辦?我們在scratch容器裡有個靜態連結的可執行碼,有什麼不對頭的嗎?

結果是Go沒有靜態連結庫,或者至少不是所有庫。在Linux下,我們可以用ldd 命令看到可執行碼的動態連線庫:

ldd $GOPATH/bin/helloworld 

  命令返回如下內容: 

linux-vdso.so.1 => (0x00007fff039fe000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f61df30f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f61def84000)
/lib64/ld-linux-x86-64.so.2 (0x00007f61df530000)

  所以,為了在web伺服器李允星Hello World,我們需要告訴Go編譯器執行靜態連結。

Go裡建立靜態連結的可執行碼

  為了建立靜態連結可執行碼,我們需要告訴Gocgo編譯器而非go編譯器。命令如下:

CGO_ENABLED=0 go get -a -ldflags '-s' github.com/adriaandejonge/helloworld

CGO_ENABLED環境變數告訴Go使用cgo編譯器而非go編譯器。-a標記告訴Go重新構建所有依賴。否則,你讓然會使用動態連結依賴庫。並且-ldflags '-s' 標記尤其漂亮,它大體上能縮減可執行檔案50%的體積。你不用cgo編譯器也可以做到這點,縮減的體積源於去掉了除錯資訊。

  保險起見,重新執行一下ldd命令:

ldd $GOPATH/bin/helloworld 

  應該返回:

not a dynamic executable

  你還可以重新執行從scratch建立帶可執行碼的Docker容器的步驟:

docker build -t adejonge/helloworld $GOPATH

  如果一切正常,應該返回類似下面的內容: 

Successfully built 6ff3fd5a381d

  你可以執行容器了:

docker run -ti --name helloworld adejonge/helloworld

  這次應該返回這個了: 

Started, serving at 8080

  目前為止,有太多手動步驟以及太多的坑了。我們從google/golang容器退出,從宿主機繼續:

<Press Ctrl-C>

exit

  你可以通過下面命令檢查容器和映象是否存在: 

docker ps -a

docker images -a

  你還可以做些清除工作: 

docker rm -f helloworld

docker rmi -f adejonge/helloworld

建立一個能建立Docker容器的Docker容器

  目前為止的這些步驟,也可以記錄在Dockerfile裡,然後讓Docker替我們做這些工作:

FROM google/golang

RUN CGO_ENABLED=0 go get -a -ldflags '-s' github.com/adriaandejonge/helloworld

RUN cp /gopath/src/github.com/adriaandejonge/helloworld/Dockerfile /gopath

CMD docker build -t adejonge/helloworld gopath

docker build -t adejonge/hellobuild github.com/adriaandejonge/hellobuild

  -t flag指定映象名稱為adejonge/hellobuild 並且隱含標記為最新。這些名字能讓你將來刪除他們的時候容易一點兒。下一步,你可以用這個映象建立容器,記得要使用本文之前提過的標記:

docker run -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) -ti --name hellobuild adejonge/hellobuild

  提供--name hellobuild 標記可以讓你在執行容器之後容易地刪除它。實際上,你可以馬上就做這件事,因為在你運行了這個命令之後,你已經建立了adejonge/helloworld映象:

docker rm -f hellobuild

docker rmi -f adejonge/hellobuild

  現在,你可以啟動一個叫做helloworld新容器,基於adejonge/helloworld映象的,跟前面的做法一樣:

docker run -ti --name helloworld adejonge/helloworld

  由於所有步驟都在同樣的命令列下執行,並沒開啟Docker容器裡面的bash shell,所以你可以吧這些步驟新增到bash腳本里並自動執行它。為了方便,我把這些bash指令碼新增到了hellobuild GitHub

  同樣,如果你想越過這些步驟直接嘗試這個最小的Docker容器,你可以用這個構建好的映象,我把它放在了Docker Hub庫:

docker pull adejonge/helloworld

  用docker images -a命令將顯示大小為3.6MB。當然,如果你建立比我這個Go web server更小的可執行碼,你的容器可以做得更小。用C或者彙編你可以做到這一點。然而,你無法比scratch映象更小了。

  喜歡此文?不喜歡此文?敬請留言,歡迎轉發,多多交流。——譯者

相關推薦

Docker祕籍 建立Docker容器

注:此文參加Docker中文社群譯文懸賞活動,Docker中文社群轉載時可適當修改本譯文。 【轉載必須在正文中標註並保留原文連結、譯文連結、作者、譯者資訊、以及本條要求。】 譯者按:   史上最小Docker容器長什麼樣子?怎麼建立的?做什麼用?來看此文! scr

iOS學習Swift全第三方輪子大全

@SwiftLanguage 更新至 2016-2-1,最近新收錄 Graph, Localize-Swift, Cuckoo, Gecco, AudioKit, vapor, Every.swift 等 7 個,合計已收錄 297 個。詳見本文件。 工具類 專案 開

Docker環境安裝指南-讓安裝docker簡單到爆

一、思考❓❔ 1.什麼是Docker? 裝應用的容器 開發、測試、運維都偏愛的容器化技術 輕量級 擴充套件性 一次構建、多次分享、隨處執行 2.安裝Docker難不難? So easy! 此文看過之後,讀者一定會有一個docker環境 二、Windows上安裝Docker

Intellij idea簡單的教程Linux下安裝與破解Intellij idea2017

成功 zxvf java 新建 pre form 旗艦版 lan intel 一、前言 這一節我們介紹在Linux下如何安裝與破解Intellij idea2017。現在有很多公司開發環境都是Linux,所以掌握在Linux環境下使用Idea辦公也是咱們必須得掌握的技能。

簡單的SpringCloud教程 | 第十一篇: docker部署spring cloud項目

大數 imageview 建議 chapter 環境 多次 pan mas 存儲 Docker是一個開源的引擎,可以輕松的為任何應用創建一個輕量級的、可移植的、自給自足的容器。開發者在筆記本上編譯測試通過的容器可以批量地在生產環境中部署,包括VMs(虛擬機)、bare m

Java學習吐血整理Java技術書從入門到進階全50+本(書籍推薦珍藏版)

前言: 技術書閱讀方法論 一.速讀一遍(最好在1~2天內完成) 人的大腦記憶力有限,在一天內快速看完一本書會在大腦裡留下深刻印象,對於之後複習以及總結都會有特別好的作用。 對於每一章的知識,先閱讀標題,弄懂大概講的是什麼主題,再去快速看一遍,不懂也沒有關係,但是一定要在不懂的地方做個記

[轉] webpack前端效能優化(全,不斷更新中。。。)

最近在用webpack優化首屏載入效能,通過幾種外掛之後我們上線前後的速度快了一倍,在此就簡單的分享下吧,先上個優化前後首屏渲染的對比圖。 可以看到總下載時間從3800ms縮短到1600ms。 我們在用webpack時一般都會選擇多入口檔案吧,為的就是將自己的原始碼跟第三方庫程式碼分離。這是之前的程式

全MapReduce檔案優化策略

小檔案的優化無非以下幾種方式:   在資料採集的時候,就將小檔案或小批資料合成大檔案再上傳 HDFS 在業務處理之前,在 HDFS 上使用 mapreduce 程式對小檔案進行合併 在 mapreduce 處理時,可採用 CombineTextInputForma

全面的Docker構建工具教程

onf -name engine .json usr art 倉庫 version 地址 Docker 是一個開源的應用容器引擎,基於 Go 語言] 並遵從Apache2.0協議開源。Docker 可以讓開發者打包他們的應用以及依賴包到一個輕量級、可移植的容器中,然後發布到

簡單的SpringCloud教程 | 第十一篇: docker部署spring cloud專案

Docker是一個開源的引擎,可以輕鬆的為任何應用建立一個輕量級的、可移植的、自給自足的容器。開發者在筆記本上編譯測試通過的容器可以批量地在生產環境中部署,包括VMs(虛擬機器)、bare metal、OpenStack 叢集和其他的基礎應用平臺。 Docker通常用於如下場景:

全、詳細的Docker學習資料

一、Docker 簡介 Docker 兩個主要部件: Docker: 開源的容器虛擬化平臺 Docker Hub: 用於分享、管理 Docker 容器的 Docker SaaS 平臺 – Docker Hub Docker 使用客戶端-伺服器 (C/S) 架構

微信程式0基礎快速入門(全!!!)

產品定位及功能介紹 微信小程式是一種全新的連線使用者與服務的方式,它可以在微信內被便捷地獲取和傳播,同時具有出色的使用體驗。 小程式註冊 註冊小程式帳號 在微信公眾平臺官網首頁(mp.weixin.qq.com)點選右上角的“立即註冊”按鈕。 選擇註冊的帳號型別 選擇“

詳細、完全的ipython使用教程,Python使用者必備!——ipython系列

宣告:本文承接前面一篇文章,ipython系列之一;另外,本文所指的ipython不是ipython notebook,ipython notebook已經被jupyter notebook所取代,不再叫ipython notebook了。 前面講解了ipython裡面的一些核心

【Github教程】全github使用方法:github入門到精通

GitHub已經成為的一切開放原始碼軟體的基石。開發人員喜歡它,基於它進行協作,並不斷通過它開發令人驚歎的專案。除了​​程式碼託管,GitHub的主要吸引力是使用它作為一個協作開發工具。在本教程中,讓我們來看看一些最有用的GitHub的功能,特別是使團隊工作更有效率,更高生產力,非常重要的,好玩的那

【Github教程】全github使用方法:github入門到精通

註冊賬戶以及建立倉庫 要想使用github第一步當然是註冊github賬號了, github官網地址:https://github.com/。 之後就可以建立倉庫了(免費使用者只能建公共倉庫),Create a New Repository,填好名稱後Create,之後會出現一些倉庫的配置資訊,這

機器學習和python學習吐血整理機器學習python大資料技術書從入門到進階全本(書籍推薦珍藏版)

“機器學習/深度學習並不需要很多數學基礎!”也許你在不同的地方聽過不少類似這樣的說法。對於鼓勵數學基礎不好的同學入坑機器學習來說,這句話是挺不錯的。不過,機器學習理論是與統計學、概率論、電腦科學、演算法等方面交叉的領域,對這些技術有一個全面的數學理解對理解演算法的內部工作機

Java全的微信支付+退款實戰。

廢話不多說直接上程式碼: 呼叫類: public class WeixinPayClient{        Logger log = Logger.getLogger(WeixinPayClient.class);   private st

簡單MySQL教程詳解(基礎篇)多表聯合查詢

常用術語 內連線 外連線 左外連線 右外連線 注意事項: 自連線 子查詢 在上篇文章史上最簡單MySQL教程詳解(基礎篇)之資料庫設計正規化及應用舉例我們介紹過,在關係型資料庫中,我們通常為了減少資料的冗餘量將對資料表進行規範,將

後端---簡單細緻的Java接入微信支付介面(Native接入)

距離上一篇部落格 史上最全最小白最簡單最細緻的Java接入支付寶支付介面方法   https://blog.csdn.net/weixin_42504145/article/details/85077635 已經過去快三天了,終於在今天將微信整個電腦網頁支付整合在SSM框

後端---簡單細緻的Java接入支付寶支付介面方法

昨天Boss在講專案的業務需求的時候對我突然說讓我做一下支付寶微信支付介面這塊功能,我的心裡是充滿問號的????,但是我的嘴卻堅定的說出了沒問題!!!                     &n