使用Travis在Docker Hub上管理開源Docker映象

weixin_33843409發表於2018-08-05

我最近正在開展一個小專案,學習使用Docker工具鏈,並瞭解其工作原理。 我決定構建(又一個)etcd的開源Docker映象並將其釋出在Docker Hub上。 像所有開源專案一樣,構建一個有效的Docker映象非常簡單,並沒有花太多時間。反而是如何映象讓更多人覺得有用會花費更多的時間。 在這個過程中,我學到了一些關於使用DockerDocker HubTravis的內容,但仍然有一些問題沒有得到解答。 因此,我認為值得記錄我的發現和未解答的問題,以獲得反饋並提高我的理解。

\\

首先闡述我的要求:

\\
  1. 支援最新版本和一些舊版本的應用程式似乎是Docker社群中的常見做法。 支援常見發行版及其精簡版本似乎也是一種常見做法。 因此,該映象應該遵循社群的做法。\\t
  2. Github上的README自動同步到Docker Hub上的描述是必須的。 我的假設是開發人員通常會在Docker Hub上發現Docker映象。 所以只在Github上有一個很好的自述檔案是不夠的。 該文件也必須存在於Docker Hub上。 我不想在我的git倉庫中編寫文件,然後每次有更改時手動將其複製到Docker Hub。\\t
  3. 映象本身應該非常易於使用,既可以作為伺服器執行etcd,也可以作為CLI客戶端執行。 我不打算討論這個,因為這與本章主題有點無關。 但是如果你感興趣的話,請檢視README並在Github專案中開啟一個issue來反饋問題。\

第一個挑戰是建立一個自動化流程來構建新映象並將其上傳到Docker Hub。 有一篇很好的部落格記錄瞭如何使用Travis實現這一目標,以便每次推送到程式碼庫都會構建一個新映象像,並且當合並分支時,構建的新映象將被推送到Docker Hub。 設定很簡單。

\\

儘管簡單,但它不符合我的任何要求。 下面嘗試逐一解決。

\\

問題#1 - 自動同步README

\\

雖然上面提及到的Travis構建過程可以構建並將映象推送到Docker Hub,但它無法同步README。 Docker CLI也不支援更新Docker Hub上的描述。

\\

Docker Hub有一個名為Automated Builds的東西,通過配置,可以達到Travis作業產生的結果。 並且,它還可以從Github倉庫中提取README的內容,以便在Docker Hub上作為描述使用。 因此,我們可以使用Automated Builds來構建映象並從Github同步README。 這解決了我們的一項要求。

\\

06b75f836e302b6c3c14b4865015dd48.png

\\

Automated Builds的工作原理是我們必須在Docker Hub上配置它以指定Docker上下文的路徑(即Dockerfile的路徑),從該Dockerfile構建映象並打上自己的標記。 這意味著從倉庫構建的每個映象,必須在唯一路徑上存在Dockerfile。 這很快就會成為一個問題。

\\

問題#2 - 執行測試

\\

雖然Docker Hub上的Automated Builds解決了README同步問題,但自動構建還存在一些其他問題。 你無法將其用作執行測試和獲取PR測試狀態的工具。 根本沒有辦法從Docker Hub的構建系統獲得反饋給Github。 這對於開發流程至關重要

\\

所以有一件事是肯定的 - 如果構建失敗,我們需要回到Travis執行測試並阻止PR。

\\

問題#3 - 為多個版本構建映象

\\

我不僅想構建etcd的最新版本,也包括之前的一些版本,並構建這些版本的發行版本。

\\

支援的版本:

\\

3.3(最新)

\\

3.2

\\

支援的發行版本:

\\

Debian:stretch-slim

\\

alpine

\\

因此,我們必須構建總共4個映象 - 每個發行版本對應一個版本。

\\

首先看看多版本問題。

\\

我開始檢視其他開源Docker映象是如何實現這一點的。 結果並不令人滿意。

\\

看了MySQL映象的Dockerfiles。 每個版本的MySQL的docker映象都在倉庫的“版本目錄”中有一個專用的Dockerfile。 他們幾乎沒有區別。 請參閱這兩個MySQL的Dockerfiles - MySQL 5.6MySQL 5.7的Dockerfile。 在76行程式碼中,它們只有一行不同,並且只是版本不同。

\\

我在Nginx的Dockerfiles中觀察到了一個非常相似的模式。 平均而言,每個Dockerfile中每個基本映象的每個版本只有5-10%的行不同。

\\

這看起來像是大量的重複程式碼。 我不想這樣,因為每個映象中唯一不同的是版本和包管理器(Alpine和Debian使用不同的包管理器)。

\\

Dockerfile的ARG可用於簡化此操作。 通過將值傳遞給docker build命令,ARG可用於定義在構建映象時可以設定的變數。 這非常方便。 由於每個版本的Dockerfiles唯一不同的是URL中的版本本身,我可以輕鬆地使用它並使用相同的Dockerfile和以下命令為不同版本構建docker映象:

\\
\docker build . -t \"3.3\" --build-arg version=3.3\docker build . -t \"3.2\" --build-arg version=3.2
\\

在帶有環境矩陣的Travis中使用它,我們可以自動為多個版本構建Docker映象。 請參閱.travis.yml中為每個版本構建多個映象的程式碼段:

\\
\language: bash\services: docker\env:\  matrix:\  - VERSION=3.3.1\  - VERSION=3.2.19\script:\  - docker build . -t \"$VERSION\" --build-arg version=$VERSION
\\

這解決了多版本的多映象問題。 但我們仍然需要解決為每個版本的每個基本映象構建映象的問題。

\\

問題#4 - 為多個基本映象進行構建

\\

除了為多個版本構建映象,我們還存在為多個基本映象構建映象的問題。 現在僅僅使用ARG是不夠的。

\\

在這種情況下,挑戰變成了每個發行版可能都有自己的具體操作方式,例如包管理。 Alpine和Debian使用不同的包管理器。 因此僅使用ARG不足以處理所有更改。 還需要管理如何進行包的安裝。 在這種情況下,我們需要一個安裝包,但不需要執行etcd。 如果我們能夠以某種方式獲得沒有任何額外的etcd二進位制檔案,我們就可以製作出完美的Docker映象。

\\

多階段Docker構建和構建器模式

\\

Docker的Multi-Stage Builds是一種乾淨利落地構建Docker映象的方式。 通常在構建過程中,我們會安裝大量隨機包。 成功構建應用程式後,清理可能會很麻煩。

\\

構建Docker映象時,映象總是越小越好。 有些人使用bash指令碼編寫清理指令。 有些人將開發和生產Dockerfiles分開,開發Dockerfile會產生一個較大包體的映象,其包含開發所需的一切,生產Dockerfile會生成一個僅具有開發Docker映象所必需工件的docker映象。 但這兩種方法都有各自的不足。

\\

多階段構建簡化了其中的一部分,並提供了更好地組織Dockerfiles的方法。 你可以在Dockerfiles中新增多個階段,為每個階段構建一個完全獨立的Docker映象,並使用其他構造可以輕鬆地將工件從一個階段複製到另一個階段。 從Docker Hub上的文件中檢視此示例:

\\
\FROM golang:1.7.3\WORKDIR /go/src/github.com/alexellis/href-counter/\RUN go get -d -v golang.org/x/net/html  \COPY app.go .\RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .\\FROM alpine:latest  \RUN apk --no-cache add ca-certificates\WORKDIR /root/\COPY --from=0 /go/src/github.com/alexellis/href-counter/app .\CMD [\"./app\"]
\\

首先,注意這個Dockerifle有多個FROM指令。 每條指令表示不同的構建階段,從而產生完全不同的映象。 COPY指令採用名為--from的引數,可用於從其他構建階段複製檔案。 使用此選項來複制你想要用來構建映象的檔案,並留下不需要的所有內容。

\\

多階段構建與ARG結合可以解決我們的問題。 現在可以同時使用version和base_image,並使用base_image和FROM指令在構建時選擇基本映象。

\\
\FROM debian:stretch-slim\...\\FROM \"$base_image\"\...\COPY --from=0 /path/to/etcd-binary /usr/local/bin\CMD [\"etcd\"]
\\

然後執行以下命令來構建所有的映象:

\\
\# Version 3.3 on debian:stretch-slim\docker build . -t \"3.3\" --build-arg version=3.3 \\\    --build-arg base_image=\"debian:stretch-slim\"\# Version 3.3 on alpine:latest\docker build . -t \"3.3:alpine\" --build-arg version=3.3 \\\    --build-arg base_image=\"alpine:latest\"\# Version 3.2 on debian:stretch-slim\docker build . -t \"3.2\" --build-arg version=3.2 \\\    --build-arg base_image=\"debian:stretch-slim\"\# Version 3.2 on alpine:latest\docker build . -t \"3.2:alpine\" --build-arg version=3.2 \\\    --build-arg base_image=\"alpine:latest\"
\\

在這裡檢視完整的Dockerfile

\\

這為我們解決為多版本構建映象和多個基本映象程式碼重複的問題。

\\

Multi-Stage Builds有一些很酷的功能。 我建議檢查一下,以獲得更好的Docker體驗。

\\

有了這個,可以通過環境構建矩陣提供的不同環境來構建在Travis上的所有映象,在每個環境合併程式碼到主分支,構建映象、執行測試、推送構建好的映象到Docker Hub。 這是一個示例.travis.yml檔案(上一個示例的擴充套件):

\\
\language: bash\services: docker\env:\  matrix:\  - VERSION=3.3.1 BASE_IMAGE=debian:stretch-slim\  - VERSION=3.2.19 BASE_IMAGE=debian:stretch-slim\  - VERSION=3.3.1 BASE_IMAGE=alpine\  - VERSION=3.2.19 BASE_IMAGE=alpine\script:\  - |\    docker build . -t \"$VERSION\" \\\      --build-arg version=$VERSION \\\      --build-arg base_image=\"$BASE_IMAGE\"
\\

但同步README的問題仍然存在,並且Travis似乎也無法做到。 下面看看如何解決。

\\

問題#5 - 捆綁在一起

\\

我們在這裡取得了以下進展:

\\
  1. 我們知道如何為每個版本和每個基本映象構建Docker映象。 可用一種可管理的方式執行此操作,而無需重複程式碼。 並且可以在Travis上進行構建。\\t
  2. 可以在Travis上構建映象並推送到Docker。 但是無法在Docker Hub上獲得README。\\t
  3. 可以使用Automated Builds在Docker Hub上構建映象,並將README同步到Docker Hub。 但是我們無法為Github上的每個pull請求構建映象,並且如果構建失敗則阻止合併pull請求。 並且不會給Github任何反饋。\

因此,雖然我們可以使用Travis構建所有映象併為每個PR執行測試,但無法使用它來推送映象。 而對於Docker Hub則相反。 實際上,最簡單形式的Docker Hub的Automated Build系統不能用於基於ARG的設定,因為Automated Builds可以使用Docker上下文,即在構建設定中為“每個指定的路徑”構建映象,並期望這些路徑中存在Dockerfile。 你無法在此類設定中傳遞CLI引數。 因此,我們的自定義docker構建命令不適用於Automated Builds。

\\

自定義構建階段鉤子,用於Docker Hub上的自動構建

\\

自定義構建階段鉤子允許在不同的構建階段執行自定義指令碼,從而對自動構建進行更高階的自定義。 例如,可以覆蓋構建階段以將額外引數傳遞給docker構建,或者可以覆蓋推送階段以推送到多個倉庫。 當然,你還可以做很多事情。

\\

這最終解決了我們所有的問題:

\\
  1. 我們可以使用Travis設定為每個PR構建和執行測試,但不能將映象推送到Docker Hub。\\t
  2. 只能在主分支上使用自動構建和自定義階段鉤子。 這將把我們的映象推送到Docker Hub並同步來自Github的README。\

4f98720f65a92cb804d5d25c325c2e76.jpeg8b816c5bba2c967a29b21fdda5fa5303.jpeg

\\

構建鉤子指令碼是很通用的。 我參考在Travis中構建映象的指令碼來編寫鉤子指令碼!

\\

在此處檢視整個設定:

\\
  1. Dockerfile\\t
  2. .travis.yml\\t
  3. 自定義構建鉤子\

結論

\\

沒有重複程式碼,易於構建的過程,良好的開發體驗和README的自動同步 - 這對我來說非常有用,我對最終的設定非常滿意。

\\

有人可能會說使用Docker Automated Build和Travis很難維護。 可以通過自定義構建鉤子將PR狀態推送到Github,但這不是我到目前為止所探討的,與在Docker Hub上維護Travis和Automated Builds相比,它看起來也需要更多精力。

\\

但有一件事不確定是使用ARG構建Docker映象的方法和多階段構建。 雖然它適合我,但我在這個領域的經驗是有限的,我想得到反饋。 你怎麼看? 還有構建和分發開源Docker映象更好的方法嗎? 請通過評論部分告訴我你的想法。

相關文章