在本文中数人云将带你们了解如何利用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。