在本文中數人云將帶你們瞭解如何利用Docker Compose建立一套分佈式應用捆綁包,並將其做爲Docker Stack在Docker Swarm Mode中進行部署。java
Docker 1.12的首套候選發行版於三周以前公佈,而近期又有更多新功能計劃被添加至該版本當中。node
下面首先來看各項新的功能特性:git
內置編排機制:一般來說,應用利用一個Docker Compose文件進行定義。此定義由多個被部署在不一樣主機上的容器共同構成。這種做法除了可以避免單點故障(簡稱SPOF)以外,也可以讓應用具有彈性。目前包括Docker Swarm、Kubernetes以及Mesos在內的多種編排框架都容許你們對此類應用進行編排。不過如今咱們又有了新的選擇——Docker Engine現在迎來了內置編排機制。更多細節內容將在後文中進行說明。github
Service:如今你們能夠利用docker service create 命令輕鬆建立一項複製且分佈式的負載均衡服務。該應用可實現「理想狀態」,例如運行三套Couchbase容器,並具有自我修復能力。Docker引擎可以確保必要容器數量始終運行於集羣當中。若是某容器發生故障,那麼另外一容器將旋即啓動。若是某臺節點發生故障,則該節點上的容器會在另外一節點上啓動。稍後咱們將詳細說明其做用。web
零配置安全性: Docker 1.12採用相互驗證TLS,可以對swarm當中各節點間的通訊內容進行驗證、受權與加密。更多詳盡內容將在後文中進行討論。
Docker Stack與分佈式應用捆綁包:分佈式應用捆綁包,或者簡稱DAB,是一種多服務可分發鏡像格式。在後文中咱們會進一步討論。docker
截至目前,你們已經能夠選定一個Dockerfile,並利用docker build命令由此建立鏡像。使用docker run命令則可啓動容器。這條命令亦可以輕鬆同時啓動多套容器。另外,你們也可使用Docker Compose文件並利用docker-compose scale命令對容器進行規模擴展。
安全
鏡像屬於單一容器的一種便攜式格式。而Docker 1.12當中新推出的分佈式應用捆綁包,或者簡稱DAB,則屬於一種新的概念,其專門面向多套容器的遷移需求。每一個捆綁包均可做爲stack在運行時中進行部署。服務器
感興趣的朋友能夠前往 docker.com/dab 瞭解更多與DAB相關的內容。爲了簡單起見,在這裏咱們利用類比來進行說明:負載均衡
Dockerfile -> 鏡像 -> 容器
Docker Compose -> 分佈式應用捆綁包 -> Docker Stack框架
下面咱們使用一個Docker Compose文件來建立DAB,並將其做爲Docker Stack加以部署。
須要強調的是,這項實驗性功能僅存在於1.12-RC2版本當中。
Docker Compose CLI添加了一條新的bundle命令。下面來看其具體說明:
docker-compose bundle --help Generate a Docker bundle from the Compose file. Local images will be pushed to a Docker registry, and remote images will be pulled to fetch an image digest. Usage: bundle [options] Options: -o, --output PATH Path to write the bundle file to. Defaults to "<project name>.dsb".
如今,讓咱們選取一條Docker Compose定義並以此爲基礎建立DAB。如下爲咱們的Docker Compose定義內容:
version: "2" services: db: container_name: "db" image: arungupta/oreilly-couchbase:latest ports: -8091:8091 -8092:8092 -8093:8093 -11210:11210 web: image: arungupta/oreilly-wildfly:latest depends_on: -db environment: -COUCHBASE_URI=db ports: -8080:8080
此Compose文件會啓動WildFly與Couchbase服務器。其中WildFly服務器中已經預部署了一款Java EE應用,且接入Couchbase服務器並容許利用REST API執行CRUD操做。該文件的源代碼來自:github.com/arun-gupta/oreilly-docker-book/blob/master/hello-javaee/docker-compose.yml。 利用它生成一個應用捆綁包:
docker-compose bundle WARNING: Unsupported key 'depends_on' in services.web - ignoring WARNING: Unsupported key 'container_name' in services.db - ignoring Wrote bundle to hellojavaee.dsb
depends_on只負責建立兩項服務之間的依賴性,並以特定順序對兩者進行啓動。這能確保Docker容器首先啓動,而運行在其中的應用則須要更長時間才能啓動完成。所以,此屬性只在必定程度上解決了這一問題。
container_name可以爲該容器提供一個特定名稱。對特定容器名稱的依賴性爲緊密耦合,且不容許咱們對該容器進行規模伸縮。所以這裏咱們暫時忽略這兩條警告。此命令會利用Compose項目名(也就是其目錄名稱)生成一個文件。所以在本示例中,生成的文件名爲hellojavaee.dsb。此文件的擴展名在RC3中則爲.dab。今生成的應用捆綁包內容以下所示:
{ "services": { "db": { "Image": "arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c", "Networks": [ "default" ], "Ports": [ { "Port": 8091, "Protocol": "tcp" }, { "Port": 8092, "Protocol": "tcp" }, { "Port": 8093, "Protocol": "tcp" }, { "Port": 11210, "Protocol": "tcp" } ] }, "web": { "Env": [ "COUCHBASE_URI=db" ], "Image": "arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914", "Networks": [ "default" ], "Ports": [ { "Port": 8080, "Protocol": "tcp" } ] } }, "version": "0.1" }
此文件爲包含在應用內的各項服務提供完整的描述。固然,將來咱們應該可使用其它容器格式,例如Rkt或者VM等形式。不過就目前來說,其還僅支持Docker這一種格式。
正如以前所提到,目前「理想狀態」由Docker Swarm負責保持。而其如今已經被歸入Docker Engine當中。在本篇文章中,咱們使用新增的一條命令,即docker swarm:
docker swarm --help Usage: docker swarm COMMAND Manage Docker Swarm Options: --help Print usage Commands: init Initialize a Swarm join Join a Swarm as a node and/or manager update Update the Swarm leave Leave a Swarm inspect Inspect the Swarm Run 'docker swarm COMMAND --help' for more information on a command.
在Docker Engine中對一個Swarm節點(做爲工做節點)進行初始化:
docker swarm init Swarm initialized: current node (ek9p1k8r8ox7iiua5c247skci) is now a manager.
關於該節點的更多細節信息可利用docker swarm inspect命令進行查看。
docker swarm inspect [ { "ID": "1rcvu7m9mv2c8hiaijr7an9zk", "Version": { "Index": 1895 }, "CreatedAt": "2016-07-01T23:52:38.074748177Z", "UpdatedAt": "2016-07-02T04:54:32.79093117Z", "Spec": { "Name": "default", "AcceptancePolicy":{ "Policies": [ { "Role": "worker", "Autoaccept": true }, { "Role": "manager", "Autoaccept":false } ] }, "Orchestration": { "TaskHistoryRetentionLimit":10 }, "Raft": { "SnapshotInterval": 10000, "LogEntriesForSlowFollowers":500, "HeartbeatTick":1, "ElectionTick":3 }, "Dispatcher": { "HeartbeatPeriod": 5000000000 }, "CAConfig": { "NodeCertExpiry": 7776000000000000 } } } ]
從輸出結果中能夠看到,該節點只屬於工做節點而非管理節點。若是在單節點集羣當中,這樣的設置並沒有不妥。不過在多節點集羣當中,則應至少存在一個管理節點。
利用docker deploy命令建立一個stack:
docker deploy -f hellojavaee.dsb hellojavaee Loading bundle from hellojavaee.dsb Creating network hellojavaee_default Creating service hellojavaee_db Creating service hellojavaee_web
下面來看各服務列表:
docker service ls ID NAME REPLICAS IMAGE COMMAND 2g8kmrimztes hellojavaee_web 1/1 arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914 46xhlb15cc60 hellojavaee_db 1/1 arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c
在輸出結果中,咱們能夠看到正在運行的兩項服務,分別爲WildFly與Couchbase。 Service概念一樣新增於Docker 1.12版本,其負責爲咱們提供「理想狀態」,而具體實現則由Docker Engine負責。使用docker ps命令顯示當前正在運行的容器列表:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 622756277f40 arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c "/entrypoint.sh /opt/" 3 seconds ago Up 1 seconds 8091-8093/tcp, 11207/tcp, 11210-11211/tcp, 18091-18092/tcp hellojavaee_db.1.19enwdt6i5m853m5675tx3z29 abf8703ed713 arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914 "/opt/jboss/wildfly/b" 3 seconds ago Up 1 seconds 8080/tcp hellojavaee_web.1.70piloz6j4zt06co8htzisgyl
WildFly容器會在Couchbase容器啓動並運行以前先行啓動。這意味着Java EE應用會嘗試接入Couchbase服務器但發生失敗。所以,該應用將永遠沒法成功完成引導。
Docker Service負責保持應用的「理想狀態」。在本示例中,咱們的理想狀態是確保特定服務有且只有一套容器與之對應且持續運行。若是咱們移除該容器,而非服務,則該服務會自動重啓容器。使用如下命令移除容器:
docker rm -f abf8703ed713
請注意,這裏之因此要使用-f,是由於該容器已經處於運行狀態。Docker 1.12自我修復機制會介入並自動重啓此容器。如今再次打開運行容器列表:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES db483ac27e41 arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914 "/opt/jboss/wildfly/b" 1 seconds ago Up Less than a second 8080/tcp hellojavaee_web.1.ddvwdmojjysf46d4n3x4g8uv4 622756277f40 arungupta/oreilly-couchbase@sha256:f150fcb9fca5392075c96f1baffc7f893858ba763f3c05cf0908ef2613cbf34c "/entrypoint.sh /opt/" 26 seconds ago Up 25 seconds 8091-8093/tcp, 11207/tcp, 11210-11211/tcp, 18091-18092/tcp hellojavaee_db.1.19enwdt6i5m853m5675tx3z29
結果顯示新容器已經啓動完成。檢查WildFly服務:
docker service inspect hellojavaee_web [ { "ID": "54otfi6dc9bis7z6gc6ubynwc", "Version": { "Index": 328 }, "CreatedAt": "2016-07-02T01:36:35.735767569Z", "UpdatedAt": "2016-07-02T01:36:35.739240775Z", "Spec": { "Name": "hellojavaee_web", "Labels": { "com.docker.stack.namespace": "hellojavaee" }, "TaskTemplate": { "ContainerSpec": { "Image": "arungupta/oreilly-wildfly@sha256:d567ade7bb82ba8f15a85df0c6d692d85c15ec5a78d8826dfba92756babcb914", "Env": [ "COUCHBASE_URI=db" ] } }, "Mode": { "Replicated": { "Replicas": 1 } }, "Networks": [ { "Target": "epw57lz7txtfchmbf6u0cimis", "Aliases": [ "web" ] } ], "EndpointSpec": { "Mode": "vip", "Ports": [ { "Protocol": "tcp", "TargetPort": 8080 } ] } }, "Endpoint": { "Spec": {}, "Ports": [ { "Protocol": "tcp", "TargetPort": 8080, "PublishedPort": 30004 } ], "VirtualIPs": [ { "NetworkID": "9lpz688ir3pzexubkcb828ikg", "Addr": "10.255.0.5/16" }, { "NetworkID": "epw57lz7txtfchmbf6u0cimis", "Addr": "10.0.0.4/24" } ] } } ]
Swarm會將隨機端口分配給該服務,咱們也能夠利用docker service update命令進行手動更新。在本示例中,容器的端口8080被映射至主機上的端口30004。
下面檢查該應用是否已經成功部署:
curl http://localhost:30004/books/resources/book [{"books":0}]
爲該應用添加新的book:
再次驗證該book: curl http://localhost:30004/books/resources/book [{"books":{"name":"Minecraft Modding with Forge","cost":29.99,"id":"1","isbn":"978-1-4919-1889-0"}}, {"books":1}]
欲瞭解更多與此Java應用相關的信息,請訪問github.com/arun-gupta/oreilly-docker-book/tree/master/hello-javaee。