如何使用 Docker Compose 來構建一套開發環境
???
一個高效的團隊需要擁有能簡便建立開發環境的方法。下面這個案例會詳細演示如何進行這個簡便過程。
前面的博客文章提到過,你應該堅持使用一種簡單可重複的方式,來為你的項目建立開發環境。
前面博客文章的鏈接:
http://danlebrero.com/2017/09/01/a-docker-compose-environment/#content
在這篇博客中,我們將深入案例的細節,這個案例來源於Akvo上的一個開源項目。
Akvo鏈接:
https://akvo.org/
恕我直言,擁有一種簡便的建立開發環境的方法,是減少開源項目貢獻者間衝突的重要的一方面。
AkvoLumen架構
我們將要研究的項目叫做AkvoLumen,這是一個「易於使用數據進行聚合、分析和發布的平台」(「easyto use data mashup, analysis and publishing platform」)。
AkvoLumen鏈接:
https://github.com/akvo/akvo-lumen
「easyto use data mashup, analysis and publishing platform」鏈接:
http://akvo.org/products/akvo-lumen/
AkvoLumen是一個用Javascript實現的獨立頁面的web應用程序(SPA),有一個Clojure後端,就像下面這樣:
Keycloak是一個開源的單點登錄的應用。它被其他的Akvo產品所共享。
Nginx服務於SPA,且向後端發送請求。
這裡的後端就是後端(目測是clojure實現的所謂的後端)......
Keycloak鏈接:
Nginx鏈接:
https://www.nginx.com/
不使用dockercompose構建環境的步驟
我們管這種構建開發環境的步驟叫做「傳統」的方式,即:安裝Postgres,運行初始化腳本,下載構建工具,執行一系列命令等等。
但是在當下介紹的這個工具的使用下,構建步驟被劃分為了三個文件目錄:keycloak、backend和SPA。
Keycloak鏈接:
https://github.com/akvo/akvo-lumen/tree/21882586987927bc496e94c76e3faf6cbf965085/keycloak
Backend鏈接:
https://github.com/akvo/akvo-lumen/tree/21882586987927bc496e94c76e3faf6cbf965085/backend
SPA鏈接:
https://github.com/akvo/akvo-lumen/tree/21882586987927bc496e94c76e3faf6cbf965085/client
從個人來講,當執行上述一系列命令的時候,我不清楚我需要本地運行KeyCloak以及npm依賴包中的某一個不會在我的開發環境中編譯。我也不知道其原因。我也根本不想知道。
當然,存在更好的方式。
使用了dockercompose後的構建步驟
使用dockercompose後新的步驟在這裡:
https://github.com/akvo/akvo-lumen/blob/d0c535166a8f276242d4de48f869c5d4ae931859/README.dev.md
可以總結為:
sudosh -c "echo "127.0.0.1 t1.lumen.localhost t2.lumen.localhostauth.lumen.localhost" >> /etc/hosts"
docker-composeup -d && docker-compose logs -f --tail=10
第一步之所以需要,是因為Lumen是一個多租戶的產品,且租戶基於host配置。
第二步等同於docker-composeup,但是不會佔據你的終端。
有趣的是,這次的步驟中不再有npm和mvn的安裝命令了。
上述步驟完成後,下面的環境就運行起來了:
Dockercompose文件
完整的dockercompose文件可以在這裡:
https://github.com/akvo/akvo-lumen/blob/develop/docker-compose.yml
Postgres和Keycloak
從觀察Postgres鏡像開始:
postgres:
build: postgres
ports:
-"5432:5432"
嚴格來說,我們不需要暴露Postgres的埠,但是在開發過程中能夠使用UI工具查看資料庫表是很方便的。
Dockerfile相當簡單:
FROMpostgres:9.5
ADD./provision /docker-entrypoint-initdb.d/
Dockerfile鏈接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/postgres/Dockerfile
依照Postgresoffical image執行步驟,我們拷貝我們的initialsetup scripts,它會在容器第一次啟動的時候運行。
Postgresoffical image鏈接:
https://hub.docker.com/_/postgres/
initialsetup scripts鏈接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/postgres/provision/setup.sh
這個腳本的功能只是創建一堆空的資料庫。Lumen後端會創建需要的表並引用數據,這些作為資料庫遷移邏輯的一部分。
Keycloakimage很簡單,只是建立初始用戶的集合,密碼和認證,建立的方式採用Keycloakyway。
Keycloakimage鏈接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/keycloak/Dockerfile
Keycloakyway鏈接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/keycloak/akvo.json
LumenBackend
LumenBackend是一個Clojure服務。它的dockercompose配置如下:
backend:
build:
context: ./backend
dockerfile: Dockerfile-dev
volumes:
- ./backend:/app
- ~/.m2:/root/.m2
- ~/.lein:/root/.lein
links:
ports:
- "47480:47480"
第一個有意思的地方是它使用了與生產配置不同的Dockerfile。
不同的Dockerfile鏈接:
https://github.com/akvo/akvo-lumen/blob/e0881e38cc1c4bf77692cbaa611427af3db288ab/backend/Dockerfile-dev
生產配置鏈接:
https://github.com/akvo/akvo-lumen/blob/e0881e38cc1c4bf77692cbaa611427af3db288ab/backend/Dockerfile
在開發過程中,我們需要構建工具,像我們例子中的Lein,而且我們需要好的REPL提供的快速的應答;但是在生產環境中,我們只希望能快速啟動。
Lein鏈接:
https://leiningen.org/
REPL鏈接:
http://vvvvalvalval.github.io/posts/what-makes-a-good-repl.html
要注意到因為我們的構建工具已經成為了docker鏡像的一部分,所以團隊里的每個人都會運行Lein的同一個版本,並且是運行在相同的JVM和OS上。其他項目可能會使用不同版本的Lein,或者不同的工具,因為容器隔離了不同的項目。
每次我們對源碼文件做了改動之後,我們不想都重新構建和重啟我們的BackendDocker鏡像,所以「volumes」的第一行(-./backend:/app)使得源碼能在Docker容器中可用:任何源碼的改動,會馬上在容器中可見。
我們把第二個卷掛在到本地的專用的倉庫目錄上(-~/.m2:/root/.m2)。這在某種程度上會弄亂你的開發環境,因為刪除掉Docker容器並不會同時去掉下載的依賴包,但是理論上你的本地的專用倉庫目錄只是一個緩存,所以不論任何時候它變得太大後,都可以刪掉它,不會有什麼影響。
如果你一點也不想影響到你的開發環境,那麼你可以利用緩衝層(layers)的方法,只在項目文件有了改動後再去下載依賴包。
layers鏈接:
http://bitjudo.com/blog/2014/03/13/building-efficient-dockerfiles-node-dot-js/
最後的卷(-~/.lein:/root/.lein)使得Lein全局信息能在容器內看到。當你在意避免任何「它運行在我的機器上」的爭議,可以使用它。
Lein全局信息鏈接:
https://jakemccrary.com/blog/2015/01/11/overview-of-my-leiningen-profiles-dot-clj/
最後,我們使REPL埠可用,這樣就可以使用你最喜歡的IDE鏈接它了。你需要準確的指出lein的配置:repl-options來監聽那個埠,以便可以接受來自任何主機的連接。
任何主機鏈接:
https://github.com/akvo/akvo-lumen/blob/e0881e38cc1c4bf77692cbaa611427af3db288ab/backend/project.clj#L74
LumenClient
LumenClient鏡像是一個Nginx,運行著SPA服務,能傳遞請求到後端。
client:
build:
context: ./client
dockerfile: Dockerfile-dev
volumes:
- ./client:/lumen
ports:
- "3030:3030"
對於開發我們優先考慮的是快速的交互,所以在生產環境和開發環境中的Docker鏡像是不同的。
在這個案例中,我們用webpackDev Server來替換Nginx,這個服務會再次編譯SPA,任何時候我們對源碼做出修改都會在你的瀏覽器上做及時的代碼載入(hotcode reload)。掛載的卷會讓源碼為容器內部所見。
暴露的埠即為主應用的入口。
執行測試和其他的構建任務
在這個環境中,你不用安裝任何的npm、lein或者其他的包,及執行任何這些工具帶來的任務,只需要在Docker容器內運行他們即可。
例如,執行Backend測試:
docker-composeexec backend lein test
或者如果你準備運行幾個命令,你可以啟動一個bashshell:
docker-composeexec backend bash
貼士:如果你想保留bash的歷史命令,只需要添加一個新的卷到DockerCompose文件中,掛在home目錄。
關於啟動有依賴的各個部分
DockerCompose沒有提供(verylittle help)功能來支持各個容器的啟動順序。
Verylittle help鏈接:
https://docs.docker.com/compose/startup-order/
這部分工作有你來做,以確保對其他有依賴的容器能夠等待足夠長的時間來讓被依賴的部分準備好,通常會設置一個等待時間的最大值。
對於這個工程,Backend同時依賴Keycloak和Postgres,但是Backend啟動的時間比其他兩個都長,所以我們這次就忽略掉這個問題。
如何處理其他工程的啟動依賴問題可以借鑒下面兩條:
鏈接:
https://github.com/akvo/akvo-maps/blob/4aaaea55230b3eb7f704ecee73d89db115ccc3fa/end-to-end-tests/test/windshaft_test/core_test.clj#L11
鏈接:
https://github.com/dlebrero/kafka-streams-and-ktable-example/blob/master/our-service/src/our_service/util.clj#L41
環境的升級
需求產生的契機是AkvoLumen要添加新的特性,來提供一些交互的方面的功能
(interactivemaps)。
這意味著這個工程現在需要:
PostGis鏈接:
http://postgis.net/
Windshaft鏈接:
https://github.com/akvo/akvo-maps/tree/develop
Redis鏈接:
https://redis.io/
所以我們需要DockerCompose來完成下面的內容:
---a/postgres/Dockerfile
+++b/postgres/Dockerfile
-FROMpostgres:9.5
+FROMmdillon/postgis:9.6
---a/postgres/provision/helpers/create-extensions.sql
+++b/postgres/provision/helpers/create-extensions.sql
+CREATEEXTENSION IF NOT EXISTS postgis WITH SCHEMA public;
---a/docker-compose.yml
+++b/docker-compose.yml
+redis:
+ image: redis:3.2.9
+windshaft:
+ image: akvo/akvo-maps:2469ae0cb95ba090412f042fdfa8c7038273fe0e
+ environment:
+ - NODE_ENV=development
+ volumes:
+ - ./windshaft/config/dev:/config
然後執行一次docker-composedown; docker-compose up構建一下,整個團隊都會享受這個構建過程。
這樣豈不是很開心?
譯者介紹:
韓海蛟,雲技術社區譯者,現在在電信雲計算,做libvirtqemu和ironic相關,工作四年


TAG:全球大搜羅 |