原標題:5種常見的Docker Compose錯誤

作者 | Ethan Jackson

譯者 | 張健欣

策劃 | 萬佳

在構建一個容器化應用程序時,開發人員需要一種方法來引導他們正在使用的容器去測試其代碼。雖然有幾種方法可以做到這一點,但 Docker Compose 是最流行的選擇之一。它讓你可以輕鬆指定開發期間要引導的容器,其次建立一個快速的“編碼 - 測試 - 調試”開發循環。

願景是,一個人編寫一個 docker-compose.yml 文件,指定了開發中所需的一切,並將它提交到代碼倉庫。然後,每一個開發者只需運行 docker-compose up ,即可啓動測試其代碼需要的所有容器。

然而,要使 docker-compose 設置達到最高性能,需要大量工作。我們見過最好的團隊在不到一分鐘的時間內啓動他們的開發環境,並在幾秒中內測試每個更改。考慮到每個開發人員每天花在測試代碼上的時間,小的改進會對開發人員的生產力產生巨大影響。

源自 XKCD

1錯誤:頻繁的容器重建

docker build 需要很長時間。如果每次想要測試一個代碼更改時都要重新構建你的容器,那麼你就有很大潛力來加速你的開發循環。

處理非容器化應用程序的傳統工作流如下:

  • 編碼
  • 構建
  • 運行

這些年來,通過使用針對編譯型語言的增量構建和熱加載之類的技巧,這個過程得到高度優化。它變得非常快。

當人們第一次採用容器時,他們傾向於採用現有的工作流程,只添加一個 docker build 步驟。他們的工作流如下:

  • 編碼
  • 構建
  • 容器構建
  • 運行

如果做的不好, docker build 步驟會使所有優化都白費。另外,它還增加了一堆額外的耗時工作,例如使用 apt-get 重新安裝依賴。所有這些加起來,會使得我們的測試過程比使用 Docker 之前要慢得多。

解決方案:在 Docker 外運行你的代碼

一種方案是在 Docker Compose 中啓動所有依賴項,但在本地運行你正在積極處理的代碼。這模仿了開發非容器化應用程序的工作流。

只需要在 localhost 上暴露你的依賴,並將你正在使用的服務指向 localhost:<port> 地址。

然而,這並不總是可行的,尤其是如果你正在處理的代碼依賴容器鏡像內置的東西,而這些東西不容易從你的筆記本電腦訪問。

解決方案:最大化緩存來優化 Dockerfile

如果必須構建 Docker 鏡像,那麼編寫 Dockerfile 時,最大化緩存能將一個 10 分鐘的 Docker 構建變爲 1 分鐘。

生產環境的 Dockerfile 文件的典型模式是通過將單個命令鏈接到一個 RUN 語句中來減少層數。然而,鏡像大小在開發過程中並不重要。在開發過程中,你想要儘可能多的層數。

你的生產環境 Dockerfile 文件可能如下所示:

RUN\ go get -d -v \ && go install -v \ && go build

這對開發來說很糟糕,因爲每次重新運行該命令時,Docker 都會重新下載所有的依賴並重新安裝它們。增量構建更加有效。

相反,你應該有一個專門用於開發環境的的 Dockerfile 文件。將每件事都分解成非常小的步驟,規劃好你的 Dockerfile 文件,這樣基於經常變化的代碼的步驟最後執行。

最不頻繁更改的內容,例如拉取依賴,應該放在第一位。這樣,在重建 Dockerfile 時就不必構建整個項目。你只需要構建你剛剛修改的一小部分。

關於這個的例子,請看下面我們用於 Blimp 開發環境的 Dockerfile。它遵循上面所述的技術,將繁重的構建過程縮短到幾秒鐘。

https://blimpup.io/

FROM golang:1.13-alpine as builder

RUN apk add busybox-static

WORKDIR /go/src/github.com/kelda-inc/blimp

ADD ./go.mod ./go.modADD ./go.sum ./go.sumADD ./pkg ./pkg

ARG COMPILE_FLAGS

RUN CGO_ENABLED=0 go install-i -ldflags "${COMPILE_FLAGS}"./pkg/...

ADD./login-proxy ./login-proxy RUN CGO_ENABLED= 0goinstall-i -ldflags "${COMPILE_FLAGS}"./login-proxy/...

ADD./registry ./registry RUN CGO_ENABLED= 0goinstall-i -ldflags "${COMPILE_FLAGS}"./registry/...

ADD./sandbox ./sandbox RUN CGO_ENABLED= 0goinstall-i -ldflags "${COMPILE_FLAGS}"./sandbox/...

ADD./cluster-controller ./cluster-controller RUN CGO_ENABLED= 0goinstall-i -ldflags "${COMPILE_FLAGS}"./cluster-controller/...

RUN mkdir /gobinRUN cp / go/ bin/cluster-controller /gobin/blimp-cluster-controller RUN cp / go/ bin/syncthing /gobin/blimp-syncthing RUN cp / go/ bin/init /gobin/blimp-init RUN cp / go/ bin/sbctl /gobin/blimp-sbctl RUN cp / go/ bin/registry /gobin/blimp-auth RUN cp / go/ bin/vcp /gobin/blimp-vcp RUN cp / go/ bin/login-proxy /gobin/login-proxy

FROMalpine

COPY --from=builder /bin/busybox.static /bin/busybox.staticCOPY --from=builder /gobin/* /bin/

最後一點:通過最近引入的 多階段構建,現在可以創建具有良好分層且鏡像很小的 Dockerfile。我們在本文中對這一點不會過多討論,只能說上面顯示的 Dockerfile 就是這樣做的,因此它既用於 Blimp 開發環境也用於生產環境。

解決方案:使用主機卷

通常,最好的選擇是使用一個主機捲來直接將你的代碼加載到容器上。這使你能夠以本機速度運行代碼,同時仍然在包含運行時依賴項的 Docker 容器中運行。

主機卷將你筆記本電腦上的一個目錄鏡像到一個正在運行的容器中。當你在文本編輯器中編輯一個文件時,更改會自動同步到容器中,然後能立即在容器中執行。

大多數語言都有一種方法來監視你的代碼,並在代碼更改時自動重新運行。例如,nodemon 是 Java 中的監視代碼的方法。請查看這篇關於如何設置這一點的文章教程。

https://www.npmjs.com/package/nodemon

https://blimpup.io/blog/docker-volumes-for-development/

它最初需要一些工作,但結果是,你可以在 1-2 秒內看到你的代碼更改的結果,而一次 Docker 構建可能需要幾分鐘。

2錯誤:主機卷速度慢

如果使用了主機卷,你可能已經注意到,在 Windows 和 Mac 上讀寫文件的速度非常慢。對於讀寫大量文件的命令來說,這是一個已知的問題,例如具有複雜依賴的 Node.js 和 PHP 應用程序。

這是因爲 Docker 是運行在 Windows 和 Mac 的一個虛擬機上。在進行主機卷加載時,必須經過大量的轉換才能將筆記本電腦上的文件夾加載到容器中,這有點兒類似網絡文件系統。這會增加大量負載,而在 Linux 本機上運行 Docker 時不會出現這些情況。

解決方案:放鬆強一致性

其中一個關鍵問題是,默認情況下,文件系統加載會保持強一致性。一致性是一個廣泛的話題,可以濃墨重彩地大書特書,但是簡而言之,它意味着所有特定文件的讀取者和寫入者都同意任何文件修改發生的順序,從而(最終,某種程度上)同意該文件的內容。

問題是,強制實現強一致性是相當昂貴的,需要所有文件寫入者確保他們不會不恰當地破壞彼此的更改。

雖然強一致性有時特別重要,例如,當在生產環境運行數據庫時。好消息是,在開發環境,它不是必需的。你的代碼文件只會有單個寫入者(你自己),和單個信源(你的代碼庫)。因此,衝突並不像在生產中那麼需要擔心。

正是由於這個原因,Docker 實現了在加載卷時放鬆一致性保證的功能。在 Docker Compose 中,你只需將 cached 關鍵詞添加到卷加載中即可獲得顯著的性能保證。(不要在生產環境這麼做...)

volumes:- "./app:/usr/src/app/app:cached"

解決方案:代碼同步

另一種方案是設置代碼同步。你可以用一個工具來通知你的筆記本電腦和容器之間的更改,並複製文件來解決差異(類似於 rsync),而不是加載一個卷。

Docker 的下一個版本內置了 Mutagen,作爲卷的緩存模式的一種替代。如果你感興趣,就等 Docker 發佈下一個版本再試試,不過你也可以直接下載 Mutagen 項目,不用等就可以直接使用。

https://mutagen.io/

解決方案:不要加載包

對於 Node 這樣的語言,大部分文件操作往往位於包目錄(例如 node_modules )。因此,從卷中排除這些目錄會顯著提高性能。

在下面的例子中,我們有一個卷將代碼加載到一個容器中。然後用它自己乾淨的專用卷覆蓋了 node_modules 目錄。

volumes:- ".:/usr/src/app"- "/usr/src/app/node_modules"

這個額外的卷加載告訴 Docker 爲 node_modules 目錄使用一個標準卷,這樣當 npm install 運行時,它不會使用比較慢的主機加載。爲了使之生效,當容器首次啓動時,我們在 entrypoint 運行 npm install 來安裝我們的依賴並填充 node_modules 目錄。像這樣:

entrypoint:- "sh"- "-c"- "npm install && ./node_modules/.bin/nodemon server.js"

克隆和下載上述示例代碼的完整說明,請參考此處。

https://blimpup.io/docs/#/getting-started

3錯誤:脆弱的配置

大多數 Docker Compose 文件都是有組織地演化的。我們通常會看到大量的複製粘貼代碼,這使得代碼修改非常困難。一個乾淨的 Docker Compose 文件可以更容易地在生產環境變化時進行定期更新。

解決方案:使用 env 文件

env 文件將環境變量從主 Docker Compose 配置中分離出來。這有助於:

  • 使密鑰不會保存在 git 歷史中
  • 使每個開發者擁有稍微不同的設置變得容易。例如,每個開發者可能有一個唯一的 access 密鑰。將配置保存在一個 .env 文件中意味着他們不必修改提交的 docker-compose.yml 文件,並在這個文件更新時處理衝突。

要使用 env 文件,只需增加一個 .env 文件,或者使用 env_file 字段顯式設置路徑。

解決方案:使用 override 文件

Override 文件讓你有一個基本配置,然後在不同文件中指定修改。如果你使用 Docker Swarm,並且有一個生產環境的 YAML 文件,這將非常有用。你可以在 docker-compose.yml 中存儲自己的生產環境配置,然後在一個 override 文件中指定開發環境所需的任何更改,例如使用主機卷。

https://docs.docker.com/compose/extends/

解決方案:使用 extends

如果你正在用 Docker Compose v2,你可以使用 extends 關鍵字在多個地方導入 YAML 片段。例如,你可能有一個定義,你公司的所有服務在開發環境的 Docker Compose 文件中都有這 5 個特定的配置項。你可以定義一次,然後使用 extends 關鍵詞來將它放到任何需要的地方,這就提供了一些模塊化特性。我們不得不在 YAML 中這樣做是很痛苦的,但我們能夠少寫一個程序來生成它還是最好的。

Compose v3 移除了對 extends 關鍵詞的支持。然而,你可以使用 YAML anchors 獲得類似的結果。

https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/

解決方案:程序生成 Compose 文件

我們已經和一些使用 Blimp 的工程團隊一起工作過,他們在開發環境的 Docker Compose 文件中有上百個容器。如果他們使用單個巨大的 Docker Compose 文件,就需要數千行無法維護的 YAML 代碼。

https://blimpup.io/

隨着擴展,可以編寫一個腳本,來基於一些高級別的規範生成 Docker Compose 文件。這對於具有非常大的開發環境的工程團隊來說是很常見的。

4錯誤:脆弱的引導

docker-compose up 是不是隻有一半時間工作?你是不是不得不使用 docker-compose restart 來啓動崩潰的服務?

大多數開發者都想要寫代碼,不想做 DevOps 工作。調試一個壞的開發環境是非常令人沮喪的。

docker-compose up 應該每一次都好好工作。

這裏的大多數問題都與服務啓動順序錯誤有關。例如,你的 Web 應用可能依賴一個數據庫,如果 Web 應用啓動時數據庫還沒有就緒,那麼它就會崩潰。

解決方案:使用 depends_on

depends_on 使你能控制啓動順序。默認地, depends_on 會等待依賴被創建,而不等待處於“healthy”狀態的依賴。然而,Docker Compose v2 支持將 depends_on 與健康狀態檢查結合起來。(不幸的是,這個功能在 Docker Compose v3 中被移除了。你可以使用一個類似 wait-for-it.sh 的腳本來手動實現類似功能)

https://github.com/vishnubob/wait-for-it

Docker 文檔建議不要使用類似 depends_on 和 wait-for-it.sh 之類的方案。而且,我們同意,在生產環境,要求爲容器指定特定的引導順序是脆弱架構的一種標誌。然而,作爲一名試圖完成工作的開發人員,修復整個工程組織中的每一個容器可能是不可行的。因此,對於開發環境,我們認爲這是可以的。

5錯誤:資源管理不善

要確保 Docker 擁有它流暢運行所需的資源,而不會完全超出你的筆記本電腦負擔,可能是比較棘手的。如果你覺得自己的開發工作流由於 Docker 沒有以峯值容量運行而變得遲緩,那麼可以參考以下幾點。

解決方案:修改 Docker Desktop 分配

Docker Desktop 需要大量 RAM 和 CPU,尤其是在 Mac 和 Windows 上它是一個虛擬機時。默認的 Docker Desktop 配置往往沒有分配足夠的 RAM 和 CPU,因此我們一般建議調整設置到過度分配。我傾向於在開發時給 Docker 分配大約 8GB RAM 和 4 CPU(在不使用 Docker Desktop 時,我會將它關掉,來使之可行)

解決方案:刪除未使用的資源

人們在使用 Docker 時,經常會無意識地泄漏資源。人們擁有成百上千的卷、舊的容器鏡像以及如果不小心有時還會運行的容器,這並不少見。這就是爲什麼我們推薦偶爾運行 docker system prune ,刪除當前沒有使用的所有卷、容器和網絡。這會釋放大量資源。

解決方案:在雲上運行

最後,在某些情況下,即使有上述提示,也不可能在你的筆記本上運行所需的所有容器。如果是這樣的話,可以看看 Blimp,這是一種在雲上運行 Docker Compose 文件的簡單方法。

6你應該做什麼?

爲了提升 Docker Compose 上的開發者體驗,我鼓勵你

  • 最小化容器重新構建
  • 使用主機卷
  • 力求可維護的 compose 文件,就像代碼一樣。
  • 使你的引導可靠
  • 用心管理資源

原文鏈接:

https://blimpup.io/blog/common-docker-compose-mistakes/

Elasticsearch被迫修改開源協議,CEO:全賴AWS!

特朗普最後一擊:再打華爲,8份許可被撤銷,150份申請大部分被拒絕

抖音因涉黃受行政處罰;應屆生拒絕996被申通辭退;拼多多23歲員工猝死引發廣泛關注 | Q資訊

InfoQ 寫作平臺歡迎所有熱愛技術、熱愛創作、熱愛分享的內容創作者入駐!

還有更多 超值活動等你來!

填寫申請,成爲作者

開啓你的創作之路吧~

點個在看少個 bug👇

相關文章