Docker Compose的4種錯誤使用方式

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"本文最初發佈於Earthly博客,經原作者授權由InfoQ中文站翻譯並分享。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"告訴我,這是不是聽起來很熟悉?有人要把docker-compose介紹給你,你可能是自願的,也可能是被迫的。你用了一段時間,但發現它很不靈活。我要告訴你,你可能用錯了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這可能有點誇張。我不認爲存在100%正確或錯誤的使用方法:自主構建和開發設置往往有各種奇怪的要求,所以標準可能不符合需求。如果你的情況並不完全相符,請帶着適當的懷疑態度閱讀本文。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文介紹了我自己使用docker-compose犯的一些錯誤。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我將重點關注與集成測試相關的用例,以及使用docker-compose作爲開發環境。對於生產使用,我認爲docker-compose通常並不適合。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"問題1:使用主機網絡"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新用戶遇到的第一個麻煩是Docker網絡的使用。在你瞭解了"},{"type":"text","marks":[{"type":"strong"}],"text":"docker build"},{"type":"text","text":"和"},{"type":"text","marks":[{"type":"strong"}],"text":"docker run"},{"type":"text","text":"的基礎知識後,這是你需要學習的另一層知識……坦白說,爲什麼你還需要了解Docker網絡呢?通過主機網絡,一切工作正常,這對嗎?錯了!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用主機網絡意味着你必須爲使用的各種微服務保留特定端口。如果你碰巧打開兩個有端口衝突的棧,那你就倒黴了。如果你想打開同一個棧的兩個版本,那也會倒黴。當某個服務有多個副本時,你想測試它的行爲嗎?這會非常艱難……"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在默認情況下,docker-compose在一個名爲"},{"type":"text","marks":[{"type":"strong"}],"text":"_default"},{"type":"text","text":"的獨立網絡上啓動其容器(其中"},{"type":"text","marks":[{"type":"strong"}],"text":""},{"type":"text","text":"爲默認的目錄名)。所以,你無需做任何特殊的事情就可以利用Docker網絡。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個網絡立馬能給你帶來很多好處:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它是一個比主機網絡更加獨立的網絡——因此,系統環境的小問題就不太會導致compose設置的行爲差異。你可以訪問互聯網,但是你希望從主機訪問的任何端口都要使用端口綁定來聲明。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果一個服務開始監聽0.0.0.0(容器應該這樣),那麼主機網絡設置將在WLAN上打開那個端口。如果你使用Docker網絡,它只會將該端口暴露給該網絡。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以通過使用服務的compose name作爲主機名來實現服務之間的通信。因此,如果你有一個名爲"},{"type":"text","marks":[{"type":"strong"}],"text":"db"},{"type":"text","text":"的服務,在其內部有一個偵聽端口"},{"type":"text","marks":[{"type":"strong"}],"text":"5432"},{"type":"text","text":"的服務,那麼你可以從任何其他服務通過"},{"type":"text","marks":[{"type":"strong"}],"text":"db:5432"},{"type":"text","text":"訪問它。這通常比"},{"type":"text","marks":[{"type":"strong"}],"text":"localhost:5432"},{"type":"text","text":"更直觀。而且,由於不存在本地主機端口衝突的風險,因此,在跨不同項目使用時,它可能會更加一致。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數端口不需要向主機開放——這意味着,如果你需要通過"},{"type":"link","attrs":{"href":"https:\/\/docs.docker.com\/compose\/reference\/up\/","title":"","type":null},"content":[{"type":"text","text":"--scale"}]},{"type":"text","text":"增加副本的話,它們不會競爭全局資源。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"問題2:強端口綁定到主機0.0.0.0上"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種做法隨處可見,你肯定也見過很多,每個人都見過很多:將端口綁定爲"},{"type":"text","marks":[{"type":"strong"}],"text":"8080:8080"},{"type":"text","text":"。乍一看,這似乎沒什麼問題。但魔鬼在細節中。這種極其常見的端口綁定不僅僅是將一個容器端口轉發到本地主機——它還將其轉發到系統上的每個網絡接口上,包括用於連接互聯網的任何接口。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"換言之,你的開發容器很可能一直在監聽你的無線局域網——當你在家、在辦公室或在麥當勞時。它總是可以訪問。這可能很危險。不要這樣做。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"“但是Vlad,我用了"},{"type":"text","marks":[{"type":"strong"}],"text":"ufw"},{"type":"text","text":",我的端口默認是不能訪問的”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這也許沒錯——但如果你在團隊中使用這樣的docker-compose設置,你的隊友可能沒有在他們的筆記本電腦上安裝防火牆。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修復方法非常簡單:只需在前面添加"},{"type":"text","marks":[{"type":"strong"}],"text":"127.0.0.1:"},{"type":"text","text":",例如"},{"type":"text","marks":[{"type":"strong"}],"text":"127.0.0.1:8080:8080"},{"type":"text","text":"。這是告訴docker只向迴環網絡接口公開端口,不包括其他網絡接口。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"問題3:使用sleep來協調服務啓動"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我要坦白一件事。關於這一點,我是百分百有錯的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個問題之所以如此複雜,主要原因是Docker或Docker Compose沒有提供支持解決這個問題。Docker-compose文件格式的2.1版本中有一個名爲"},{"type":"text","marks":[{"type":"strong"}],"text":"condition"},{"type":"text","text":"的"},{"type":"text","marks":[{"type":"strong"}],"text":"depends_on"},{"type":"text","text":"選項,可以設置爲"},{"type":"text","marks":[{"type":"strong"}],"text":"service_healthy"},{"type":"text","text":"。而且,每個服務都可以有一個"},{"type":"text","marks":[{"type":"strong"}],"text":"healthcheck"},{"type":"text","text":"命令,可以告訴docker-compose“健康”是什麼意思。"},{"type":"link","attrs":{"href":"https:\/\/docs.docker.com\/compose\/compose-file\/#depends_on","title":"","type":null},"content":[{"type":"text","text":"這在3.0版本中不再可用"}]},{"type":"text","text":",也"},{"type":"link","attrs":{"href":"https:\/\/stackoverflow.com\/questions\/47710767\/what-is-the-alternative-to-condition-form-of-depends-on-in-docker-compose-versio","title":"","type":null},"content":[{"type":"text","text":"沒有提供替換項"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Docker文檔的"},{"type":"link","attrs":{"href":"https:\/\/docs.docker.com\/compose\/startup-order\/","title":"","type":null},"content":[{"type":"text","text":"基本建議"}]},{"type":"text","text":"是,服務要在其他服務暫時離線的情況下具有彈性,因爲這在生產環境中可能會發生,例如出現短暫的網絡不穩定,或者一個服務重新啓動。這是無可爭辯的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當你運行一個集成測試,而用於初始化測試環境的例程(例如預先用一些測試數據填充數據庫)在其他服務準備就緒之前無法恢復啓動,就會變得有點麻煩。因此,關於“至少它在生產環境中會有彈性”的論點在這裏並不適用,因爲用測試數據填充數據庫的代碼從未在生產環境中使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於這種情況,你需要等待服務就緒。Docker建議使用"},{"type":"link","attrs":{"href":"https:\/\/github.com\/vishnubob\/wait-for-it","title":"","type":null},"content":[{"type":"text","text":"wait-for-it"}]},{"type":"text","text":"、"},{"type":"link","attrs":{"href":"https:\/\/github.com\/jwilder\/dockerize","title":"","type":null},"content":[{"type":"text","text":"Dockerize"}]},{"type":"text","text":"或"},{"type":"link","attrs":{"href":"https:\/\/github.com\/Eficode\/wait-for","title":"","type":null},"content":[{"type":"text","text":"wait-for"}]},{"type":"text","text":"。但是,請注意,端口就緒並不總是表示服務已經準備好可供使用。例如,在使用具有特定模式的特定SQL DB的集成測試中,當數據庫初始化時,端口變爲可用,但是,測試可能只有在特定模式遷移完成之後才能開始。你可能需要在前面進行特定於應用程序的檢查。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"問題4:在docker-compose中運行數據庫,但在主機上測試"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有這樣一種情況:你想運行一些單元測試,但這些測試依賴於一些外部服務。可能是數據庫,可能是Redis,也可能是另外一個API。簡單:讓我們把這些依賴放在docker-compose中,並讓單元測試連接到它們。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這很好——但請注意,你的測試不再僅僅是單元測試,它們現在是集成測試了。除術語之外,現在還需要考慮一個重要的區別:你需要考慮測試環境的設置和清理。通常,最好在測試代碼之外執行設置\/清理操作——主要原因是可能有多個不同的包依賴於這些外部服務。但你的情況可能不太一樣。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你最終將測試設置和清理分開,那麼你需要多做一些工作,將集成測試容器化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"容器化測試意味着:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你在同一個Docker網絡上,所以連接設置與你在compose中運行服務時相同。配置變得更簡潔。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在設置和清理過程中,你可以重用用於等待其他服務就緒的代碼。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"集成測試不依賴於任何其他的本地系統配置或環境設置,例如你的JFrog憑證或任何構建依賴項。容器是隔離的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果另一個團隊需要針對測試所依賴的服務的更新版本運行你的測試,那麼你只需要共享集成測試鏡像——不需要他們編譯或設置一個構建工具鏈。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你最終使用了多個單獨的集成測試容器,那麼你通常可以並行地運行所有這些容器。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用容器化集成測試的一個技巧是,爲它們使用一個獨立的docker-compose定義。例如,如果你的大部分服務都存在於"},{"type":"text","marks":[{"type":"strong"}],"text":"docker-compose.yml"},{"type":"text","text":"中,那麼可以添加一個包含集成測試定義的"},{"type":"text","marks":[{"type":"strong"}],"text":"docker- composition .test.yml"},{"type":"text","text":"文件。這意味着"},{"type":"text","marks":[{"type":"strong"}],"text":"docker-compose up"},{"type":"text","text":"會提供你常用的服務,而"},{"type":"text","marks":[{"type":"strong"}],"text":"docker-compose -f docker-compose.yml -f docker-compose.test.yml up"},{"type":"text","text":"會啓動集成測試。要了解完整的實現示例,請參閱Ardan Labs提供的這個優秀的"},{"type":"link","attrs":{"href":"https:\/\/github.com\/george-e-shaw-iv\/integration-tests-example","title":"","type":null},"content":[{"type":"text","text":"docker-compose集成測試庫"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說這是錯誤有點不公平。在許多情況下,不容器化是更可取的。舉個簡單的例子,許多語言都與IDE深度集成,這使得在語言和IDE之間插入容器幾乎不可能。有很多正當的理由不這樣做。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對本地開發來說,Docker Compose是一個非常棒的工具。儘管它有一些缺陷,但通常,它會給許多工程團隊帶來很多生產力方面的好處,尤其是在與集成測試一起使用時。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https:\/\/blog.earthly.dev\/youre-using-docker-compose-wrong\/"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章