虽然它提供了编排多个容器以创建单个“应用程序”的选项 但它并未将此类容器组作为单个实体进行管理。这就是 Fig 之类的工具的用武之地 将一组容器作为单个实体进行讨论。想想“运行一个应用程序” 即“运行一个精心设计的容器集群” 而不是“运行一个容器”。
事实证明 很多使用 docker 的人都同意这种观点。随着 Fig 慢慢流行起来 Docker Inc. 注意到了这一点 收购了该公司并将 Fig 重新命名为 Docker Compose。
那么Compose 有什么用呢 Compose 是一种用于以简单的方式定义和运行多容器 Docker 应用程序的工具。它提供了一个名为的配置文件docker-compose.yml 可用于通过一个命令启动应用程序及其依赖的服务套件。Compose 适用于所有环境 生产、预发布、开发、测试以及 CI 工作流 尽管 Compose 是开发和测试环境的理想选择。
下面看看是否可以docker-compose.yml为 SF-Foodtrucks 应用程序创建一个文件 并评估 Docker Compose 是否兑现了它的承诺。
首先 第一步是安装 Docker Compose。如果运行的是 Windows 或 Mac Docker Compose 已经安装在 Docker 工具箱中。Linux 用户可以按照文档中的说明轻松掌握 Docker Compose 。由于 Compose 是用 Python 编写的 因此可以简单地执行pip install docker-compose. 使用以及测试安装结果
$ docker-compose --version docker-compose version 1.21.2, build a133471
下一步编写Docker Compose 文件docker-compose.yml。YAML 的语法非常简单 repo 已经包含将使用的 docker-compose文件
version: 3 services: image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2 container_name: es environment: - discovery.type single-node ports: - 9200:9200 volumes: - esdata1:/usr/share/elasticsearch/data web: image: prakhar1989/foodtrucks-web command: python3 app.py depends_on: - es ports: - 5000:5000 volumes: - ./flask-app:/opt/flask-app volumes: esdata1: driver: local
详细说一下上面的文件的含义。在父级 定义服务的名称es和web. 该image参数始终是必需的 对于希望 Docker 运行的每个服务 可以添加其他参数。对于es 仅参考elasticsearchElastic Registry 上可用的镜像。对于 Flask 应用程序 指定开头构建的镜像。
其他参数 例如command和ports提供有关容器的更多信息。该volumes参数指定了web容器中代码将驻留的挂载点。这是可选的 如果需要访问日志等 这很有用。稍后将看到这在开发过程中如何有用。请参阅联机参考以了解有关此文件支持的参数的更多信息。此外还为es容器添加了卷 以便加载的数据在重新启动之间保持不变。还指定了depends_on 它告诉 docker 在es之前启动容器web。可以在docker compose docs上阅读有关它的更多信息。
注意 必须在docker-compose.yml文件所在的目录中才能执行大多数 Compose 命令。
现在文件已经准备好了 在开始之前 需要确保端口和名称是空闲的。所以如果有 Flask 和 ES 容器在运行 先把它们关掉。
$ docker stop es foodtrucks-web foodtrucks-web $ docker rm es foodtrucks-web foodtrucks-web
现在可以运行docker-compose. 进入 food trucks 目录并运行docker-compose up
$ docker-compose up
Creating network foodtrucks_default with the default driver
Creating foodtrucks_es_1
Creating foodtrucks_web_1
Attaching to foodtrucks_es_1, foodtrucks_web_1
es_1 | [INFO ][node ] [Comet] version[2.1.1], pid[1], build[40e2c53/2015-12-15T13:05:55Z]
es_1 | [INFO ][node ] [Comet] initializing ...
es_1 | [INFO ][plugins ] [Comet] loaded [], sites []
es_1 | [INFO ][env ] [Comet] using [1] data paths, mounts [[/usr/share/elasticsearch/data (/dev/sda1)]], net usable_space [16gb], net total_space [18.1gb], spins? [possibly], types [ext4]
es_1 | [INFO ][node ] [Comet] initialized
es_1 | [INFO ][node ] [Comet] starting ...
es_1 | [WARN ][common.network ] [Comet] publish address: {0.0.0.0} is a wildcard address, falling back to first non-loopback: {172.17.0.2}
es_1 | [INFO ][transport ] [Comet] publish_address {172.17.0.2:9300}, bound_addresses {[::]:9300}
es_1 | [INFO ][discovery ] [Comet] elasticsearch/cEk4s7pdQ-evRc9MqS2wqw
es_1 | [INFO ][cluster.service ] [Comet] new_master {Comet}{cEk4s7pdQ-evRc9MqS2wqw}{172.17.0.2}{172.17.0.2:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)
es_1 | [WARN ][common.network ] [Comet] publish address: {0.0.0.0} is a wildcard address, falling back to first non-loopback: {172.17.0.2}
es_1 | [INFO ][http ] [Comet] publish_address {172.17.0.2:9200}, bound_addresses {[::]:9200}
es_1 | [INFO ][node ] [Comet] started
es_1 | [INFO ][gateway ] [Comet] recovered [0] indices into cluster_state
es_1 | [INFO ][cluster.metadata ] [Comet] [sfdata] creating index, cause [auto(index api)], templates [], shards [5]/[1], mappings [truck]
es_1 | [INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck]
es_1 | [INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck]
es_1 | [[INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck]
es_1 | [INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck]
es_1 | [INFO ][cluster.metadata ] [Comet] [sfdata] update_mapping [truck]
web_1 | * Running on http://0.0.0.0:5000/ (Press CTRL C to quit)
前往 IP 以实时查看您的应用。只需几行配置 就可以同时成功运行两个 Docker 容器。停止服务并在分离模式下重新运行。
web_1 | * Running on http://0.0.0.0:5000/ (Press CTRL C to quit) Killing foodtrucks_web_1 ... done Killing foodtrucks_es_1 ... done $ docker-compose up -d Creating es ... done Creating foodtrucks_web_1 ... done $ docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------- es /usr/local/bin/docker-entr ... Up 0.0.0.0:9200- 9200/tcp, 9300/tcp foodtrucks_web_1 python3 app.py Up 0.0.0.0:5000- 5000/tcp
不出所料 可以看到两个容器都成功运行。名字从何而来 这些是由 Compose 自动创建的。但是Compose 是否也会自动创建网络 好问题 下面来了解一下。
首先 停止运行服务。但是可以通过一个命令将它们恢复。数据量将持续存在 因此可以使用 docker-compose up 使用相同的数据再次启动集群。要销毁集群和数据卷 只需输入docker-compose down -v
$ docker-compose down -v Stopping foodtrucks_web_1 ... done Stopping es ... done Removing foodtrucks_web_1 ... done Removing es ... done Removing network foodtrucks_default Removing volume foodtrucks_esdata1
在此过程中 还可以删除foodtrucks上次创建的网络
$ docker network rm foodtrucks-net $ docker network ls NETWORK ID NAME DRIVER SCOPE c2c695315b3a bridge bridge local a875bec5d6fd host host local ead0e804a67b none null local
现在我们有了一个干净的环境 重新运行我们的服务 看看Compose是否发挥了它的魔力
$ docker-compose up -d Recreating foodtrucks_es_1 Recreating foodtrucks_web_1 docker container ls ConTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f50bb33a3242 prakhar1989/foodtrucks-web python3 app.py 14 seconds ago Up 13 seconds 0.0.0.0:5000- 5000/tcp foodtrucks_web_1 e299ceeb4caa elasticsearch /docker-entrypoint.s 14 seconds ago Up 14 seconds 9200/tcp, 9300/tcp foodtrucks_es_1
到现在为止还挺好 查看是否创建了任何网络
$ docker network ls NETWORK ID NAME DRIVER c2c695315b3a bridge bridge local f3b80f381ed3 foodtrucks_default bridge local a875bec5d6fd host host local ead0e804a67b none null local
可以看到 compose 继续创建了一个名为foodtrucks_default的新网络 并附加了两个新服务到该网络中 以便其中的每一个都可以被另一个发现。服务的每个容器都加入默认网络 并且可以被该网络上的其他容器访问 并且可以在与容器名称相同的主机名上被它们发现。
docker ps
ConTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8c6bb7e818ec docker.elastic.co/elasticsearch/elasticsearch:6.3.2 /usr/local/bin/dock… about a minute ago Up about a minute 0.0.0.0:9200- 9200/tcp, 9300/tcp es
7640cec7feb7 prakhar1989/foodtrucks-web python3 app.py about a minute ago Up about a minute 0.0.0.0:5000- 5000/tcp foodtrucks_web_1
$ docker network inspect foodtrucks_default
Name : foodtrucks_default ,
Id : f3b80f381ed3e03b3d5e605e42c4a576e32d38ba24399e963d7dad848b3b4fe7 ,
Created : 2018-07-30T03:36:06.0384826Z ,
Scope : local ,
Driver : bridge ,
EnableIPv6 : false,
IPAM : {
Driver : default ,
Options : null,
Config : [
Subnet : 172.19.0.0/16 ,
Gateway : 172.19.0.1
Internal : false,
Attachable : true,
Ingress : false,
ConfigFrom : {
Network :
ConfigOnly : false,
Containers : {
7640cec7feb7f5615eaac376271a93fb8bab2ce54c7257256bf16716e05c65a5 : {
Name : foodtrucks_web_1 ,
EndpointID : b1aa3e735402abafea3edfbba605eb4617f81d94f1b5f8fcc566a874660a0266 ,
MacAddress : 02:42:ac:13:00:02 ,
IPv4Address : 172.19.0.2/16 ,
IPv6Address :
8c6bb7e818ec1f88c37f375c18f00beb030b31f4b10aee5a0952aad753314b57 : {
Name : es ,
EndpointID : 649b3567d38e5e6f03fa6c004a4302508c14a5f2ac086ee6dcf13ddef936de7b ,
MacAddress : 02:42:ac:13:00:03 ,
IPv4Address : 172.19.0.3/16 ,
IPv6Address :
Options : {},
Labels : {
com.docker.compose.network : default ,
com.docker.compose.project : foodtrucks ,
com.docker.compose.version : 1.21.2
Development Workflow
在跳到下一部分之前 将介绍关于 docker-compose 的最后一件事。如前所述 docker-compose 非常适合开发和测试。因此 如何配置 compose 以在开发过程中使得开发更轻松 。
在本教程中 使用了现成的 docker 镜像。虽然是从头开始构建镜像 但我们还没有接触任何应用程序代码 并且主要限制在自己编辑 Dockerfiles 和 YAML 配置。但必须知道的一件事是开发过程中的工作流是什么样的 是否应该为每个更改继续创建 Docker 映像 然后发布它 然后运行它以查看更改是否按预期工作 这肯定听起来非常乏味 一定有更好的方法。在本节中 这就是要探索的内容。
让我们看看如何对刚刚运行的 Foodtrucks 应用程序进行更改。确保应用程序正在运行
$ docker container ls ConTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5450ebedd03c prakhar1989/foodtrucks-web python3 app.py 9 seconds ago Up 6 seconds 0.0.0.0:5000- 5000/tcp foodtrucks_web_1 05d408b25dfe docker.elastic.co/elasticsearch/elasticsearch:6.3.2 /usr/local/bin/dock… 10 hours ago Up 10 hours 0.0.0.0:9200- 9200/tcp, 9300/tcp es
现在看看我们是否可以更改此应用程序以Hello world!在发出/hello路由请求时显示消息。目前 该应用程序以 404 响应。
$ curl -I 0.0.0.0:5000/hello HTTP/1.0 404 NOT FOUND Content-Type: text/html Content-Length: 233 Server: Werkzeug/0.11.2 Python/2.7.15rc1 Date: Mon, 30 Jul 2018 15:34:38 GMT
什么会发生这种情况 由于后端是 Flask 应用程序 可以查看app.py 链接 以获得答案。在 Flask 中 路由是用 app.route 语法定义的。在该文件中 你会看到 只有三条路线定义- / /debug和/search。该/路线呈现主要的应用程序 该debug是用来返回一些调试信息 最后search被使用的应用程序来查询elasticsearch 没有/hello
$ curl 0.0.0.0:5000/debug msg : yellow open sfdata Ibkx7WYjSt-g8NZXOEtTMg 5 1 618 0 1.3mb 1.3mbn , status : success
鉴于这种情况 将修改flask-app/app.py
app.rout( / ) def index(): return render_template( index.html ) # add a new hello route app.rout( /hello ) def hello(): return hello, world!
现在再次尝试发出请求
$ curl -I 0.0.0.0:5000/hello HTTP/1.0 404 NOT FOUND Content-Type: text/html Content-Length: 233 Server: Werkzeug/0.11.2 Python/2.7.15rc1 Date: Mon, 30 Jul 2018 15:34:38 GMT
可以看到还是没有起作用 我们做错了什么 虽然确实更改了app.py 但该文件驻留在的机器 或主机 中 由于 Docker 基于prakhar1989/foodtrucks-web镜像运行我们的容器 因此它不知道此更改。为了验证这一点 run一下看看
$ docker-compose run web bash Starting es ... done root 581e351c82b0:/opt/flask-app# ls app.py package-lock.json requirements.txt templates node_modules package.json static webpack.config.js root 581e351c82b0:/opt/flask-app# grep hello app.py root 581e351c82b0:/opt/flask-app# exit
尝试做的是验证我们的更改不在app.py容器中运行。通过运行docker-compose run命令来做到这一点 它类似于docker run的表亲 但服务 web例子中是 采用了额外的参数。一旦运行bash shell就会按照我们/opt/flask-app的Dockerfile 中的指定打开。从 grep 命令中可以看到我们的更改不在文件中。
下面进行修复这个问题。首先 需要告诉 docker compose 不要使用镜像 而是使用本地文件。此外还将调试模式设置为true以便 Flask 知道在app.py更改时重新加载服务器。像这样替换文件的web一部分docker-compose.yml
version: 3 services: image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2 container_name: es environment: - discovery.type single-node ports: - 9200:9200 volumes: - esdata1:/usr/share/elasticsearch/data web: build: . # replaced image with build command: python3 app.py environment: - DEBUG True # set an env var for flask depends_on: - es ports: - 5000:5000 volumes: - ./flask-app:/opt/flask-app volumes: esdata1: driver: local
有了这个变化 diff 重新停止和启动容器。
$ docker-compose down -v Stopping foodtrucks_web_1 ... done Stopping es ... done Removing foodtrucks_web_1 ... done Removing es ... done Removing network foodtrucks_default Removing volume foodtrucks_esdata1 $ docker-compose up -d Creating network foodtrucks_default with the default driver Creating volume foodtrucks_esdata1 with local driver Creating es ... done Creating foodtrucks_web_1 ... done
再次运行看看
$ curl 0.0.0.0:5000/hello hello world
可以看到得到了有效的回应 自己也可以尝试在应用程序中进行更多更改。
使用 Docker Compose还可以暂停服务、在容器上运行一次性命令 甚至扩展容器的数量。此外还建议查看Docker compose 的其他一些用例。
AWS 弹性容器服务在上一节中 曾经使用docker-compose单个命令docker-compose up在本地运行应用程序 现在有了一个功能强大的应用程序 想与全世界分享这个应用程序 吸引一些用户 赚很多钱。这部分内容将花时间来弄清楚如何使用 AWS 在云上部署多容器应用程序。
如果已经读到这里 就会非常确信 Docker 是一项非常酷的技术。Docker 的迅速崛起 几乎所有云供应商都开始致力于增加对在其平台上部署 Docker 应用程序的支持。截至今天 可以在Google Cloud Platform、AWS、Azure和许多其他平台上部署容器。已经了解了使用 Elastic Beanstalk 部署单容器应用程序的入门知识 在本节中 将介绍AWS 的Elastic Container Service 或 ECS 。
AWS ECS 是一种可扩展且超级灵活的容器管理服务 支持 Docker 容器。它允许您通过易于使用的 API 在 EC2 实例之上操作 Docker 集群。Beanstalk 带有合理的默认值 而 ECS 允许您根据需要完全调整您的环境。在我看来 这使得 ECS 开始使用起来非常复杂。
幸运的是 ECS 有一个友好的CLI工具 它可以理解 Docker Compose 文件并自动在 ECS 上配置集群 由于我们已经有了一个功能 docker-compose.yml因此在 AWS 上启动和运行应该不需要太多的努力。
第一步是安装 CLI。官方文档中非常清楚地解释了在 Mac 和 Linux 上安装 CLI 的说明。安装 CLI完成后 通过运行验证安装
$ ecs-cli --version ecs-cli version 1.18.1 (7e9df84)
接下来 将着手配置 CLI 以便可以与 ECS 对话。按照AWS ECS 文档的官方指南中详述的步骤进行操作。如有任何混淆 请随时参考该指南。
第一步将涉及创建一个配置文件吗 将在本教程的其余部分使用该配置文件。首先获得AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY。要获取这些信息 请按照本页上标题为“访问密钥”和“秘密访问密钥”部分中的详细步骤操作。
$ ecs-cli configure profile --profile-name ecs-foodtrucks --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY
接下来 需要获取一个用于登录实例的密钥对。前往EC2 控制台并创建一个新的密钥对。下载密钥对并将其存储在安全位置。在离开此屏幕之前要注意的另一件事是区域名称。
下一步是配置 CLI
$ ecs-cli configure --region us-east-1 --cluster foodtrucks INFO[0000] Saved ECS CLI configuration for cluster (foodtrucks)
为configure命令提供了希望集群驻留的区域名称和集群名称。确保提供创建密钥对时使用的区域名称相同的区域名称。如果前没有在的计算机上配置AWS CLI 可以使用官方指南 该指南非常详细地解释了如何让一切顺利进行。
下一步使 CLI 能够创建CloudFormation模板
$ ecs-cli up --keypair ecs --capability-iam --size 1 --instance-type t2.medium INFO[0000] Using recommended Amazon Linux 2 AMI with ECS Agent 1.39.0 and Docker version 18.09.9-ce INFO[0000] Created cluster cluster foodtrucks INFO[0001] Waiting for your cluster resources to be created INFO[0001] Cloudformation stack status stackStatus CREATE_IN_PROGRESS INFO[0062] Cloudformation stack status stackStatus CREATE_IN_PROGRESS INFO[0122] Cloudformation stack status stackStatus CREATE_IN_PROGRESS INFO[0182] Cloudformation stack status stackStatus CREATE_IN_PROGRESS INFO[0242] Cloudformation stack status stackStatus CREATE_IN_PROGRESS VPC created: vpc-0bbed8536930053a6 Security Group created: sg-0cf767fb4d01a3f99 Subnet created: subnet-05de1db2cb1a50ab8 Subnet created: subnet-01e1e8bc95d49d0fd Cluster creation succeeded.
在这里 提供了最初下载的密钥对的名称 ecs 、使用的实例数量 ( --size) 以及容器运行的实例类型。该--capability-iam标志告诉 CLI 承认此命令可能会创建 IAM 资源。
最后一步是将使用我们的docker-compose.yml文件的地方。这里需要做一些小改动 所以不要修改原始文件 而是复制一份。此文件的内容 进行更改后 如下所示
version: 2 services: image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2 cpu_shares: 100 mem_limit: 3621440000 environment: - discovery.type single-node - bootstrap.memory_lock true - ES_JAVA_OPTS -Xms512m -Xmx512m logging: driver: awslogs options: awslogs-group: foodtrucks awslogs-region: us-east-1 awslogs-stream-prefix: es web: image: prakhar1989/foodtrucks-web cpu_shares: 100 mem_limit: 262144000 ports: - 80:5000 links: - es logging: driver: awslogs options: awslogs-group: foodtrucks awslogs-region: us-east-1 awslogs-stream-prefix: web
对原始版本docker-compose.yml所做的唯一更改是为每个容器提供mem_limit 以字节cpu_shares为单位 和值 并添加一些日志记录配置。这使我们能够在AWS CloudWatch 中查看容器生成的日志。前往 CloudWatch创建一个名为foodtrucks日志组。请注意 由于 ElasticSearch 通常最终会占用更多内存 因此这里给出了大约 3.4 GB 的内存限制。在进入下一步之前 需要做的另一件事是在 Docker Hub 上发布自己的镜像。
$ docker push prakhar1989/foodtrucks-web
现在将在 ECS 上部署自己的应用程序
$ cd aws-ecs $ ecs-cli compose up INFO[0000] Using ECS task definition TaskDefinition ecscompose-foodtrucks:2 INFO[0000] Starting container... container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/es INFO[0000] Starting container... container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/web INFO[0000] Describe ECS container status container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus RUNNING lastStatus PENDING taskDefinition ecscompose-foodtrucks:2 INFO[0000] Describe ECS container status container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus RUNNING lastStatus PENDING taskDefinition ecscompose-foodtrucks:2 INFO[0036] Describe ECS container status container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus RUNNING lastStatus PENDING taskDefinition ecscompose-foodtrucks:2 INFO[0048] Describe ECS container status container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus RUNNING lastStatus PENDING taskDefinition ecscompose-foodtrucks:2 INFO[0048] Describe ECS container status container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus RUNNING lastStatus PENDING taskDefinition ecscompose-foodtrucks:2 INFO[0060] Started container... container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus RUNNING lastStatus RUNNING taskDefinition ecscompose-foodtrucks:2 INFO[0060] Started container... container 845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus RUNNING lastStatus RUNNING taskDefinition ecscompose-foodtrucks:2
上面的调用看起来类似于Docker Compose 中使用的调用 这并非巧合。如果一切顺利 应该看到 adesiredStatus RUNNING lastStatus RUNNING作为最后一行。
在浏览器中打开http://54.86.14.14 应该会看到Food Trucks的所有黑黄色光彩 AWS ECS控制台的外观如下
可以在上面看到 ECS 集群名为“foodtrucks” 现在正在运行 1 个任务和 2 个容器实例。花一些时间浏览此控制台以了解此处的所有选项。
清理使用已部署的应用程序后 请记住关闭集群
$ ecs-cli down --force INFO[0001] Waiting for your cluster resources to be deleted... INFO[0001] Cloudformation stack status stackStatus DELETE_IN_PROGRESS INFO[0062] Cloudformation stack status stackStatus DELETE_IN_PROGRESS INFO[0124] Cloudformation stack status stackStatus DELETE_IN_PROGRESS INFO[0155] Deleted cluster cluster foodtrucks结论
经过漫长、详尽但有趣的教程后 学习了如何设置 Docker、运行自己的容器、使用静态和动态网站 最重要的是获得了将应用程序部署到云的经验
希望完成本教程能让您对自己处理服务器的能力更有信心。当您有了构建下一个应用程序的想法时 您可以确信您能够以最少的努力将它呈现在人们面前。
后续进入容器世界的旅程才刚刚开始 在本教程中的目标是激发您的胃口并向您展示 Docker 的强大功能。在新技术的海洋中 单独在水域中航行是很困难的 像这样的教程可以提供帮助。这是我刚开始时希望拥有的 Docker 教程。希望它的目的是让您对容器感到兴奋 这样您就不再只能作为一个旁观者了。
以下是一些有益的额外资源。对于下一个项目 强烈建议使用 Docker。
其他资源
Awesome DockerWhy DockerDocker Weekly and archivesCodeship Blog Reference https://docker-curriculum.com/#what-is-docker-


