
- 指定在開發過程中需要啟動的容器。
- 設置一套快速的代碼測試調試(code-test-debug),以方便開發循環。
錯誤1:頻繁地進行容器重建
Docker的構建往往比較耗時,特別是每次針對代碼的變更開展測試的時候。如果能夠節省此方面的時間,那么對于加快開發周期來說是十分有益的。過去,對于非容器化的應用,我們通常會采取如下傳統的工作流程:- 編寫代碼
- 構建
- 運行
- 編寫代碼
- 構建
- Docker構建
- 運行
RUN \
go get -d -v \
&& go install -v \
&& go build
不過,該命令在每次被重新運行時,Docker都會重新下載所有的依賴項,并重新安裝它們。我們可以通過增量構建(incremental build)來提供效率。同時,您可以將開發專用的Dockerfile其分成幾個短小的步驟,從而使得那些經常更改的代碼步驟被排到最后,而將鮮少更改的步驟(例如拉式依賴關系)被放在首位。因此,在重建Dockerfile時,您不必構建整個項目,而只需構建那些被已更改的少量末尾塊即可。有關此方面的案例,您可以參閱以下用于Blimp(請參見--https://kelda.io/blimp)開發的Dockerfile。通過遵循上述方法,您可以將繁瑣的構建過程縮減到了幾秒鐘之內完成。FROM golang:1.13-alpine as builder
RUN apk add busybox-static
WORKDIR /go/src/github.com/kelda-inc/blimp
ADD ./go.mod ./go.mod
ADD ./go.sum ./go.sum
ADD ./pkg ./pkg
ARG COMPILE_FLAGS
RUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./pkg/...
ADD ./login-proxy ./login-proxy
RUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./login-proxy/...
ADD ./registry ./registry
RUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./registry/...
ADD ./sandbox ./sandbox
RUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./sandbox/...
ADD ./cluster-controller ./cluster-controller
RUN CGO_ENABLED=0 go install -i -ldflags "${COMPILE_FLAGS}" ./cluster-controller/...
RUN mkdir /gobin
RUN 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
FROM alpine
COPY --from=builder /bin/busybox.static /bin/busybox.static
COPY --from=builder /gobin/* /bin/
最后值得一提的是:隨著多階段構建(multi-stage builds,請參見--https://docs.docker.com/develop/develop-images/multistmuage-build/)的引入,我們如今可以創建各種具有良好分層和較小鏡像的Dockerfile。不過,我們在此并不會展開詳細的討論。解決方案:使用主機卷(host volumes)大多數語言都會提供一種方法來監視程序代碼,并在代碼發生更改時自動重新運行。例如,nodemon就是JavaScript語言的一種Node自動重啟工具(請參見--https://www.npmjs.com/package/nodemon)。由于主機卷可以將您電腦上的目錄,鏡像到正在運行的容器之中,因此您在使用文本編輯器來編輯文件時,各種更改將會被自動同步到容器中,并在容器內被立即執行。最初,您可能需要花點時間進行前期準備,之后在Docker中,您可以在1-2秒內馬上看到代碼的更改結果。因此,我們會選擇使用主機卷將代碼直接掛載到容器中,以便以原生的方式,在包含其了運行時依賴項的Docker容器中運行自己的代碼。錯誤2:緩慢的主機卷
如果您使用過主機卷,那么是否已經注意到:在Windows和Mac上讀寫文件的速度可能會非常緩慢?其實,對于諸如Node.js和具有復雜依賴性的PHP應用程序之類,需要讀寫大量文件的命令而言,這是一個已知的問題。其背后的原因是:Docker主要運行在Windows和Mac上的VM中。而我們在進行主機卷的掛載時,它必須經過大量的轉換,才能使文件夾進入容器,這有點類似于網絡文件系統。而此類額外的開銷,在Linux本地運行Docker時,則不會出現。解決方案:放寬強一致性該問題的一個關鍵原因是:文件系統在默認掛載時,需要保持強一致性。也就是說:所有特定文件的讀寫進程都必須統一對于文件修改的順序,以便讓文件的內容達成最終的一致。可是,強一致性的代價非常昂貴,它需要所有文件的寫入進程之間持續保持協調,以確保它們不會干擾或破壞彼此的更改。雖然在生產環境中的數據庫需要保持強一致性。但是在開發過程中,由于寫入進程就是代碼文件本身,目標就是我們的存儲庫,因此強一致性就不那么必需了。那么,我們就可以考慮Docker在掛載卷時,放寬強一致性。例如:在Docker Compose中,我們可以簡單地將此cached關鍵字添加到卷掛載中,以獲得顯著的性能保證。對應的代碼如下:volumes:
- "./app:/usr/src/app/app:cached"
注意:此舉僅適合開發環境,不適合生產環境。解決方案:代碼同步另一種處置方法是設置代碼的同步。您可以使用工具偵測主機和容器之間的變化,通過復制文件來解決差異(類似于rsync),而不是掛載卷。Docker在最新的版本中內置了用來替代卷的緩存模式--Mutagen(請參見--https://mutagen.io/)。此外,上文提到的Blimp則使用Syncthing(請參見--https://http//syncthing.net/)實現了類似的功能。解決方案:不要掛載軟件包Node之類的語言通常會把大部分文件操作放在packages目錄中(如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://kelda.io/blimp/docs/examples/#nodejs。錯誤3:脆弱的配置
如果您曾深入研究過代碼,您可能會發現Docker Compose中也充斥著各種大量復制和粘貼而來的代碼。顯然,我們需要干凈整潔的Docker Compose文件,以方便輕松地按需做出修改。解決方案:使用各種env文件Env文件能夠將環境變量與Docker Compose主配置分開,以實現:- 避免將代碼泄露到git的歷史記錄中。
- 開發人員都能按需自定義設置。例如,每個開發人員都可以持有一個唯一的訪問密鑰。他們通過將配置保存在.env文件中,以實現不必修改已提交的docker-compose.yml文件,也不必在文件更新時處理各種沖突問題。
解決方案:使用替代文件
替換文件(請參見--https://docs.docker.com/compose/extends/)可以方便您在具有基本配置的基礎上,在其他文件中指定各項修改。該功能非常適合Docker Swarm及其YAML文件。您可以將生產環境的配置存儲在docker-compose.yml中,然后在替代文件中,指定開發所需的任何修改(例如:使用主機卷)。解決方案:使用extends如果您使用的是Docker Compose v2,那么就可以使用extends關鍵字,在多個位置導入YAML片段。例如,您可能會定義:公司里所有的服務都需要在開發的Docker Compose文件中帶有某五個特定的配置。然后您可以使用extends關鍵字將其放置到任何需要的地方,以實現模塊化。當然,如果僅在YAML中執行此項操作可能比較繁瑣,我們完全可以通過編程來實現。雖然Compose v3刪除了對于extends關鍵字的支持。但是,您仍然可以使用YAML anchors(請參見--https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors/)來實現類似的結果。錯誤4:亂序啟動(Flaky Boots)
如果docker-compose出現了崩潰,我們能夠僅使用docker-compose restart來重啟服務嗎?其實此類問題主要與服務錯誤的啟動順序有關。例如,您的Web應用可能依賴于數據庫,那么在Web應用啟動時,如果數據庫尚未準備就緒,就會出現崩潰。解決方案:使用depends_ondepends_on使您可以控制啟動的順序。默認情況下,depends_on僅判斷依賴項是否已經創建,而不會判斷依賴項是否“健康”。雖然Docker Compose v2能夠支持將depends_on與運行狀況的檢查相結合。不過,該功能也在Docker Compose v3中被去除了。當然,您可以使用諸如wait-for-it.sh之類的腳本,來手動實現類似的功能。和上面提到的放寬強一致性相同,雖然Docker文檔不建議在生產環境中使用depends_on和wait-for-it.sh,來為容器指定特定的啟動順序。但是對于開發而言,我們完全可以用到depends_on。錯誤5:資源管理不善
如果您碰到開發流程受阻,Docker無法全速運行,或是無法平穩地獲取運行所需的資源,那么您可以考慮以下幾個方面:解決方案:更改Docker Desktop的分配Docker Desktop需要大量的RAM和CPU,尤其是在Mac和Windows的VM上。Docker Desktop的默認配置往往不會分配足夠的RAM和CPU,因此我們通常需要調整相關的設置。在開發時,我經驗是:為Docker分配大約8GB的RAM和4個CPU,并且在不使用Docker Desktop時,及時關閉之。解決方案:刪除未使用的資源人們在使用Docker時經常會出現數百個卷與舊的容器鏡像。這在無形中浪費了各種資源。為了釋放這些資源,我們建議通過間或運行docker system prune的方式,以刪除當前未使用到的所有卷、容器和網絡。總結
總的說來,為了改善開發人員在使用Docker Compose時的體驗,我建議您做到如下五點:- 最小化容器的重建。
- 使用主機卷。
- 像對待代碼那樣,認真配置文件,以便于維護。
- 讓啟動更加可靠。
- 認真分配管理資源。
Java幫幫
非盈利學習社區
官網:www.javahelp.com.cn

職涯寶
幫助職業者成功
分享優質內容
官網:zhiya360.com

九點編程
深夜學習,未來可期
