网络研讨会系列:构建容器化应用程序

美元(注)

网友系列

本文补充了一系列关于在云中部署和管理集装箱工作负载的网络研讨会(https://go.digitalocean.com/containers-and-microservices-webinars-series)。该系列涵盖了集装箱的基本内容,包括集装箱生命周期管理,部署多集装箱应用程序,扩展工作负载,了解Kubernetes,以及突出运行状态应用程序的最佳实践。

本教程包括系列中的第二个会话中涵盖的概念和命令,构建集装箱化应用程序 <$>

[youtube bYWLK904fBE 480 854 ]

介绍

在最后一本教程中,我们探索了一种方法来将 Docker 容器转换为 Docker 图像

在许多情况下,您需要将现有的代码带入容器图像中,并且需要一个可重复且一致的机制来创建与最新版本的代码库同步的Docker图像。

一个 Dockerfile通过提供一个声明和一致的方式来构建Docker图像来解决这些需求。

此外,你有时会想要集装整个应用程序,这些应用程序由多个不等式的容器组成,这些容器一起部署和管理。

Docker Compose,像Dockerfile一样,采用宣言方法,为您提供定义整个技术堆栈的方法,包括网络和存储需求,这不仅使构建集装箱应用程序更容易,还使其更容易管理和扩展。

在本教程中,您将使用基于 Node.jsMongoDB的样本Web应用程序从Dockerfile构建Docker图像,您将创建一个自定义网络,允许Docker容器进行通信,您将使用Docker Compose启动和扩展一个集装箱应用程序。

前提条件

要遵循本教程,您将需要:

步骤 1 — 使用 Dockerfile 构建图像

首先,转到您的主目录,然后使用 Git来克隆本教程的样本Web应用程序,从其 GitHub上的官方存储库

1cd ~
2git clone https://github.com/janakiramm/todo-app.git

这将将样本应用程序复制到名为todo-app的新目录中。

切换到todo-app,然后使用ls查看目录的内容。

1cd todo-app
2ls

新目录包含两个子目录和两个文件:

  • app - 存储样本应用程序的源代码的目录
  • compose - 存储 Docker Compose 配置文件的目录
  • Dockerfile - 包含构建 Docker 图像 的指示的文件
  • README.md - 包含样本应用程序的一个句子摘要的文件

运行猫Dockerfile显示我们如下:

1[label ~/todo-app/Dockerfile]
2FROM node:slim
3LABEL maintainer = "[email protected]"
4RUN mkdir -p /usr/src/app
5WORKDIR /usr/src/app
6COPY ./app/ ./
7RUN npm install
8CMD ["node", "app.js"]

让我们更详细地看看这个文件的内容:

  • FROM ' 表示您正在构建定制图像的基础图像。 在这个例子中,图像基于"node:slim",一个[公有Node.js 映像 (LINK0),它只包含运行"node"所需的最小软件包. (- ) * LABEL'是用于添加描述性信息的典型的关键值对. 在这种情况下,它包含维护者的电子邮件地址. (_) ) * `RUN'在容器内执行命令。 这包括创建目录和通过运行基本的Linux命令来初始化容器等任务. 此文件中的第一个 " RUN " 命令用于创建拥有源代码的目录 " /usr/src/app " 。
  • `WORKDIR ' 定义了执行所有命令的目录。 它通常是复制代码的目录
  • COPY ' 将主机的文件复制到容器图像中。 在这种情况下,您将把整个应用目录复制到图像中。 (_) ( )* 第二个RUN'命令执行npm安装',以安装package.json'(- )*所定义的应用程序依赖性。 CMD'运行将保持容器运行的过程。 在此示例中,您将使用 app.js 参数执行节点`。 (_) (英语)

现在是时候从Dockerfile中构建图像了,使用-t开关以使用注册表用户名、图像名和可选标签来标记图像。

1docker build -t sammy/todo-web .

输出确认图像是成功构建和适当标记。

 1[secondary_label Output from docker build -t]
 2Sending build context to Docker daemon 8.238MB
 3Step 1/7 : FROM node:slim
 4 ---> 286b1e0e7d3f
 5Step 2/7 : LABEL maintainer = "[email protected]"
 6 ---> Using cache
 7 ---> ab0e049cf6f8
 8Step 3/7 : RUN mkdir -p /usr/src/app
 9 ---> Using cache
10 ---> 897176832f4d
11Step 4/7 : WORKDIR /usr/src/app
12 ---> Using cache
13 ---> 3670f0147bed
14Step 5/7 : COPY ./app/ ./
15 ---> Using cache
16 ---> e28c7c1be1a0
17Step 6/7 : RUN npm install
18 ---> Using cache
19 ---> 7ce5b1d0aa65
20Step 7/7 : CMD node app.js
21 ---> Using cache
22 ---> 2cef2238de24
23Successfully built 2cef2238de24
24Successfully tagged sammy/todo-web:latest

我们可以通过运行docker images命令来验证图像是否被创建。

1docker images

在这里,我们可以看到图像的大小以及自创建以来过去的时间。

1[secondary_label Output from docker images]
2REPOSITORY TAG IMAGE ID CREATED SIZE
3sammy/todo-web latest 81f5f605d1ca 9 minutes ago 236MB

由于我们还需要一个MongoDB容器来运行样本Web应用程序,让我们把它带到我们的机器上。

1docker pull mongo:latest

输出准确地报告了哪些图像和下载状态。

1[secondary_label Output from docker pull]
2latest: Pulling from library/mongo
3Digest: sha256:18b239b996e0d10f4ce2b0f64db6f410c17ad337e2cecb6210a3dcf2f732ed82
4Status: Downloaded newer image for mongo:latest

我们现在拥有运行样本应用程序所需的一切,所以让我们创建一个自定义网络,使我们的容器彼此通信。

步骤 2 – 创建一个连接容器的网络

如果我们通过docker run命令独立启动Web应用程序和数据库容器,它们将无法找到对方。

要查看为什么,请查看 Web 应用程序的数据库配置文件的内容。

1cat app/db.js

导入 Mongoose — Node.js 的 MongoDB 对象建模库和定义新的 数据库方案后,Web 应用程序尝试以尚不存在的主机名 db连接到数据库。

 1[label ~/todo-app/app/db.js]
 2var mongoose = require( 'mongoose' );
 3var Schema   = mongoose.Schema;
 4
 5var Todo = new Schema({
 6    user_id    : String,
 7    content    : String,
 8    updated_at : Date
 9});
10
11mongoose.model( 'Todo', Todo );
12
13mongoose.connect( 'mongodb://db/express-todo' );

为了确保属于同一应用程序的容器相互发现,我们需要在同一网络上启动它们。

Docker 提供了在安装过程中创建的 默认网络之外,创建自定义网络的能力。

您可以通过以下命令检查当前可用的网络:

1docker network ls

Docker 创建的每个网络都基于一个 驱动程序。在下面的输出中,我们看到名为的网络是基于驱动程序的。

1[secondary_label Output from docker network ls]
2NETWORK ID NAME DRIVER SCOPE
35029df19d0cf bridge bridge local
4367330960d5c host host local
5f280c1593b89 none null local

我们现在将为我们的应用程序创建一个名为todo_net的自定义网络,然后在该网络上推出容器。

1docker network create todo_net

输出告诉我们创建的网络的哈希。

1[secondary_label Output from docker network create]
2C09f199809ccb9928dd9a93408612bb99ae08bb5a65833fefd6db2181bfe17ac

现在,再次列出可用的网络。

1docker network ls

在这里,我们看到‘todo_net’已经准备好使用。

1[secondary_label Output from docker network ls]
2NETWORK ID NAME DRIVER SCOPE
3c51377a045ff bridge bridge local
42e4106b07544 host host local
57a8b4801a712 none null local
6bc992f0b2be6 todo_net bridge local

当使用docker run命令时,我们现在可以用--network交换机引用这个网络。让我们用特定的主机名启动网页和数据库容器,从而确保这些容器可以通过这些主机名互连。

首先,启动MongoDB数据库容器。

1docker run -d \
2--name=db \
3--hostname=db \
4--network=todo_net \
5mongo

仔细看看这个命令,我们看到:

  • -d 交换机将容器运行在 分离模式
  • --name--hostname 交换机将用户定义的名称分配给容器。 --hostname 交换机还会向 Docker 管理的 DNS 服务添加一个条目。这有助于根据主机名称解决容器
  • `--network' 交换机指示 Docker Engine 在自定义网络上启动容器,而不是默认桥梁网络

当我们看到一个长串作为docker run命令的输出时,我们可以假定容器已成功启动,但这可能不保证容器实际运行。

1[secondary_label Output docker run]
2aa56250f2421c5112cf8e383b68faefea91cd4b6da846cbc56cf3a0f04ff4295

检查db容器是否已启动并使用docker日志命令运行。

1docker logs db

这将容器日志打印为stdout。日志的最后一行表示MongoDB已准备好并等待连接

1[secondary_label Output from docker logs]
22017-12-10T02:55:08.284+0000 I CONTROL  [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=db
3. . . .
42017-12-10T02:55:08.366+0000 I NETWORK  [initandlisten] waiting for connections on port 27017

现在,让我们启动网页容器并验证它,这一次,我们还包括了 -publish=3000:3000,将主机的端口3000发布到容器的端口3000

1docker run -d \
2--name=web \
3--publish=3000:3000 \
4--hostname=web \
5--network=todo_net \
6sammy/todo-web

您将收到像以前一样长串的输出。

让我们也检查这个容器是否正在运行。

1docker logs web

输出证实了 Express —我们测试的 Node.js 框架是基于 —是倾听在端口 3000

1[secondary_label Output from docker logs]
2Express server listening on port 3000

检查网页容器是否能够用ping命令与db容器交谈,我们通过在与假TTY(-t')附加的互动模式中运行docker exec`命令来执行此操作。

1docker exec -it web ping db

该命令产生标准的ping输出,让我们知道两个容器可以相互通信。

1[secondary_label Output from docker exec -it web ping db]
2PING db (172.18.0.2): 56 data bytes
364 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.210 ms
464 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.095 ms
5...

CTRL+C来停止ping命令。

最后,通过将您的网页浏览器指向http://your_server_ip:3000来访问样本应用程序,您将看到一个带有标签的网页,其中读取了 Containers Todo Example,以及一个接受 todo 任务作为输入的文本框。

为了避免命名冲突,您现在可以停止容器并使用docker rmdocker网络删除命令清理资源。

1docker rm -f db
2docker rm -f web
3docker network remove todo_net

此时,我们有一个由两个单独的容器组成的集装箱化Web应用程序,在下一步,我们将探索一个更强大的方法。

步骤三:部署多容器应用程序

虽然我们能够推出链接容器,但这不是处理多容器应用程序的最优雅方式,我们需要一个更好的方法来宣布所有相关的容器并将它们管理为一个逻辑单元。

Docker Compose 是开发人员可以处理多容器应用程序的框架,与 Dockefile 一样,它是定义整个堆栈的声明机制,我们现在将我们的 Node.js 和 MongoDB 应用程序转换为基于 Docker Compose 的应用程序。

开始安装 Docker Compose。

1sudo apt-get install -y docker-compose

让我们来看看位于样本Web应用程序的构成目录中的docker-compose.yaml文件。

1cat compose/docker-compose.yaml

docker-compose.yaml文件将所有内容聚合在一起,它将 MongoDB 容器定义为 db: 区块,在 web: 区块中的 Node.js 网络容器和在 networks: 区块中的自定义网络。

请注意,在构建:../.指令中,我们指向应用程序目录中的Dockerfile

 1[label ~/todo-app/compose/docker-compose.yaml]
 2version: '2'
 3services:
 4  db:
 5    image: mongo:latest
 6    container_name: db
 7    networks:
 8      - todonet
 9  web:
10    build: ../.
11    networks:
12      - todonet
13    ports:
14     - "3000"
15networks:
16  todonet:
17    driver: bridge

现在,切换到构成目录,并使用docker-compose up命令启动应用程序。

1cd compose
2docker-compose up -d

该输出报告称,Docker Compose创建了一个名为compose_todonet的网络,并在其上启动了两个容器。

1[secondary_label Output from docker-compose up -d]
2Creating network "compose_todonet" with driver "bridge"
3Creating db
4Creating compose_web_1

请注意,我们没有提供明确的主机端口映射。这将迫使 Docker Compose 分配随机端口,以便在主机上曝光 Web 应用程序。

1docker ps

我們看到,網頁應用程式在主機端口「32782」上曝光。

1[secondary_label Output from docker ps]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
36700761c0a1e compose_web         "node app.js"            2 minutes ago Up 2 minutes 0.0.0.0:32782->3000/tcp compose_web_1
4ad7656ef5db7 mongo:latest        "docker-entrypoint..."   2 minutes ago Up 2 minutes 27017/tcp db

请通过导航您的网页浏览器到 http://your_server_ip:32782. 这将像您在 步骤 2结束时看到的网页应用程序一样。

有了我们的多容器应用程序并通过Docker Compose运行,让我们看看我们的应用程序的管理和扩展。

步骤 4 – 管理和扩展应用程序

Docker Compose 可轻松扩展无状态的 Web 应用程序,我们可以使用单个命令启动 10 个Web 容器实例。

1docker-compose scale web=10

输出允许我们实时观察创建和启动的实例。

 1[secondary_label Output from docker-compose scale]
 2Creating and starting compose_web_2 ... done
 3Creating and starting compose_web_3 ... done
 4Creating and starting compose_web_4 ... done
 5Creating and starting compose_web_5 ... done
 6Creating and starting compose_web_6 ... done
 7Creating and starting compose_web_7 ... done
 8Creating and starting compose_web_8 ... done
 9Creating and starting compose_web_9 ... done
10Creating and starting compose_web_10 ... done

通过运行docker ps来验证 Web 应用程序是否扩展到 10 个实例。

1docker ps

请注意,Docker 已分配一个随机端口来暴露主机上的每个Web容器,任何这些端口都可以用于访问应用程序。

 1[secondary_label Output from docker ps]
 2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
 3cec405db568d compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32788->3000/tcp compose_web_9
 456adb12640bb compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32791->3000/tcp compose_web_10
 54a1005d1356a compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32790->3000/tcp compose_web_7
 6869077de9cb1 compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32785->3000/tcp compose_web_8
 7eef86c56d16f compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32783->3000/tcp compose_web_4
 826dbce7f6dab compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32786->3000/tcp compose_web_5
 90b3abd8eee84 compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32784->3000/tcp compose_web_3
108f867f60d11d compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32789->3000/tcp compose_web_6
1136b817c6110b compose_web         "node app.js"            About a minute ago Up About a minute 0.0.0.0:32787->3000/tcp compose_web_2
126700761c0a1e compose_web         "node app.js"            7 minutes ago Up 7 minutes 0.0.0.0:32782->3000/tcp compose_web_1
13ad7656ef5db7 mongo:latest        "docker-entrypoint..."   7 minutes ago Up 7 minutes 27017/tcp db

您也可以用相同的命令扩展到 Web 容器。

1docker-compose scale web=2

这一次,我们看到额外的实例在实时删除。

1[secondary_label Output from docker-compose]
2Stopping and removing compose_web_3 ... done
3Stopping and removing compose_web_4 ... done
4Stopping and removing compose_web_5 ... done
5Stopping and removing compose_web_6 ... done
6Stopping and removing compose_web_7 ... done
7Stopping and removing compose_web_8 ... done
8Stopping and removing compose_web_9 ... done
9Stopping and removing compose_web_10 ... done

最后,重新检查案例。

1docker ps

结果证实,只剩下两个案例。

1[secondary_label Output from docker ps]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
336b817c6110b compose_web         "node app.js"            3 minutes ago Up 3 minutes 0.0.0.0:32787->3000/tcp compose_web_2
46700761c0a1e compose_web         "node app.js"            9 minutes ago Up 9 minutes 0.0.0.0:32782->3000/tcp compose_web_1
5ad7656ef5db7 mongo:latest        "docker-entrypoint..."   9 minutes ago Up 9 minutes 27017/tcp db

您现在可以停止应用程序,就像以前一样,还可以清理资源以避免命名冲突。

1docker-compose stop
2docker-compose rm -f
3docker network remove compose_todonet

结论

本教程向您介绍了Dockerfiles和Docker Compose,我们开始使用Dockerfile作为构建图像的声明机制,然后探索Docker网络的基础知识。

要扩展新的设置,您可以添加一个 Nginx 反向代理在另一个容器内运行,以将请求路由到可用的 Web 应用程序容器之一。

Published At
Categories with 技术
comments powered by Disqus