Kubernetes Docker Compose 迁移
在构建现代的无状态应用程序时,将应用程序的组件容器化是在分布式平台上部署和扩展的第一步。如果你在开发中使用过 Docker Compose,你可以通过以下方式对应用程序进行容器化:
从代码中提取必要的配置信息。
卸载应用程序的状态。
打包你的应用程序以供重复使用。
编写指定容器镜像应如何运行的服务定义。
要在 Kubernetes 等分布式平台上运行服务,你需要将 Compose 服务定义转换为 Kubernetes 对象,这将使你能够灵活地扩展你的应用程序。kompose 这个工具可以帮助开发人员将 Compose 工作流迁移到 Kubernetes 或 OpenShift 等平台。
在本教程中,你将使用 kompose 将 Compose 服务转换为 Kubernetes 对象。你将使用 kompose 提供的对象定义作为起点并进行调整,以确保你的设置将以 Kubernetes 期望的方式使用 Secrets、Services 和 PersistentVolumeClaims。在本教程结束时,你将拥有一个单实例 Node.js 应用程序,其中包含在 Kubernetes 集群上运行的 MongoDB 数据库。
先决条件
启用了基于 RBAC 的 Kubernetes 1.10+ 集群。我们这里将使用 DigitalOcean Kubernetes 集群,但你可以使用其他方法自由创建集群。
kubectl 命令行工具安装在你的本地机器或开发服务器上并配置连接到你的集群。
安装在本地机器或开发服务器上的 Docker。
一个 Docker Hub 帐户。
1.安装 kompose
要开始使用 kompose,请导航到项目的 GitHub Releases 页面,下载最新版本的 kompose:
有关在非 Linux 系统上安装的详细信息,请参阅安装说明。
使二进制可执行文件:
将其移动到 PATH 路径:
要验证它是否已正确安装,你可以进行版本检查:
如果安装成功,你将看到如下输出:
安装好并准备好使用 kompose 后,你现在可以克隆你将要转换到 Kubernetes 的 Node.js 项目代码。
2.打包应用程序
要将我们的应用程序与 Kubernetes 一起使用,我们需要克隆项目代码并打包应用程序,以便 kubelet 服务可以拉取镜像。
我们的第一步是从 DigitalOcean 社区 GitHub 帐户克隆 node-mongo-docker-dev
代码仓库,此存储库包含使用 Docker Compose 将 Node.js 应用程序容器化的代码,该代码使用 Node.js 来演示如何使用 Docker Compose 设置开发环境。
将代码库克隆到名为 node_project
的目录中:
导航到 node_project
目录:
node_project
目录包含用于处理用户输入的鲨鱼信息应用程序的文件和目录。它可以与容器一起使用:敏感和特定的配置信息已从应用程序代码中删除,并重构为在运行时注入,应用程序的状态已转移到 MongoDB 数据库。
项目目录中包含一个 Dockerfile
文件,其中包含构建应用程序镜像的说明。现在让我们构建镜像,以便可以将其推送到你的 Docker Hub 帐户并在你的 Kubernetes 安装中使用它。
使用 docker build
命令,使用 -t
标志构建镜像,这允许你使用易于记忆的名称对其进行标记。在这种情况下,使用你的 Docker Hub 用户名标记镜像并
将其命名为 node-kubernetes
或你自己选择的名称:
.
在命令中指定构建上下文是当前目录。
构建镜像大约需要一两分钟。完成后,检查你的镜像:
正常情况下你将看到以下输出:
接下来,登录到你在先决条件中创建的 Docker Hub 帐户:
出现提示时,输入你的 Docker Hub 帐户密码。以这种方式登录将使用你的 Docker Hub 凭据在用户的主目录中创建一个 ~/.docker/config.json
文件。
使用 docker push
命令将应用程序镜像推送到 Docker Hub。请记住将 your_dockerhub_username
替换为你自己的 Docker Hub 用户名:
你现在有一个应用程序镜像了,你可以拉取该镜像以使用 Kubernetes 运行你的应用程序。下一步是将你的应用程序服务定义转换为 Kubernetes 对象。
3.使用 kompose 转换 Compose
我们的 Docker Compose 文件(docker-compose.yaml
)列出了将使用 Compose 运行我们的服务的定义。Compose 中的服务是一个正在运行的容器,services
定义包含有关每个容器镜像如何运行的信息。在这一步中,我们将通过使用 kompose 创建 yaml 文件将这些定义转换为 Kubernetes 对象。这些文件将包含描述其所需状态的 Kubernetes 对象的规范。
我们将使用这些文件来创建不同类型的对象,Service 将确保运行我们容器的 Pod 保持可访问性;Deployment 将包含有关我们的 Pod 所需状态的信息;为我们的数据库数据提供存储的 PersistentVolumeClaim;运行时注入的环境变量的 ConfigMap;以及我们应用程序的数据库用户和密码的 Secret。其中一些定义将在 kompose 为我们创建的文件中,而其他定义则需要我们自己创建。
首先,我们需要修改 docker-compose.yaml
文件中的一些定义以使用 Kubernetes。我们将在我们的 nodejs 服务定义中包含对我们新建的应用程序镜像的引用,并删除我们用于在开发中使用 Compose 运行应用程序容器的挂载、卷和其他命令。此外,我们将重新定义两个容器的重启策略,以符合 Kubernetes 所期望的行为。
使用 nano 或你喜欢的编辑器打开文件:
nodejs 应用程序服务的当前定义如下所示:
对上面的服务定义进行以下编辑:
使用你的 node-kubernetes 镜像而不是本地 Dockerfile。
将容器重启策略从
unless-stopped
更改为always
。删除卷列表和命令指令。
完成的服务定义现在将如下所示:
接下来,向下滚动到 db 服务定义。在这里,进行以下编辑:
将服务的重启策略更改为 always。
删除
.env
文件,我们将使用在步骤 4 中创建的 Secret 将MONGO_INITDB_ROOT_USERNAME
和MONGO_INITDB_ROOT_PASSWORD
的值传递给数据库容器,而不是使用.env
文件中的值。
db 服务定义现在如下所示:
最后,在文件的底部,删除 node_modules
卷,对应的 key 看起来像这样:
完成编辑后保存并关闭文件。
在翻译我们的服务定义之前,我们需要编写 .env
文件,kompose 将使用它来使用我们的非敏感信息创建 ConfigMap。创建文件:
kompose 将使用这个文件为我们的应用程序创建一个 ConfigMap。但是,我们不会在 Compose 文件中分配来自 nodejs 服务定义的所有变量,而是仅添加 MONGO_DB
数据库名称和 MONGO_PORT
。当我们在第 4 步手动创建 Secret 对象时,我们将分别分配数据库用户名和密码。
将以下端口和数据库名称信息添加到 .env
文件。如果你愿意,可以随意重命名你的数据库:
完成编辑后保存并关闭文件。
你现在已准备好使用你的对象规范创建文件。kompose 提供了多种翻译资源的选项。你可以:
使用
kompose convert
根据docker-compose.yaml
文件中的服务定义创建 yaml 文件。使用
kompose up
直接创建 Kubernetes 对象。使用
kompose convert -c
创建 Helm Chart。
现在,我们将我们的服务定义转换为 yaml 文件,然后添加和修改 kompose 创建的文件。
使用以下命令将你的服务定义转换为 yaml 文件:
你还可以使用 -f
标志命名特定或多个 Compose 文件。
运行此命令后,kompose 将输出有关它创建的文件的信息:
其中包含 Node 应用程序 Service、Deployment 和 ConfigMap 以及 dbdata PersistentVolumeClaim 和 MongoDB 数据库 Deployment 的 yaml 文件。
这些文件是一个很好的起点,但为了使我们的应用程序的功能与使用 Docker Compose 将 Node.js 应用程序容器化以进行开发中描述的设置相匹配,我们需要对 kompose 生成的文件进行一些添加和更改。
4.创建 Kubernetes Secret
为了让我们的应用程序以我们期望的方式运行,我们需要对 kompose 创建的文件进行一些修改。这些更改中的第一个将为我们的数据库用户和密码生成一个 Secret,并将其添加到我们的应用程序和数据库 Deployment 中。Kubernetes 提供了两种使用环境变量的方式:ConfigMaps 和 Secrets。kompose 已经使用我们在 .env
文件中包含的非机密信息创建了一个 ConfigMap,因此我们现在将使用我们的机密信息创建一个 Secret:我们的数据库用户名和密码。手动创建 Secret 的第一步是将你的用户名和密码转换为 base64,这是一种允许你统一传输数据(包括二进制数据)的编码方案。
转换你的数据库用户名:
记下你在输出中看到的值。接下来,转换你的密码:
还要注意此处输出中的值。
打开 Secret 文件:
注意:Kubernetes 对象通常使用 YAML 定义,它严格禁止制表符,并且需要两个空格进行缩进。如果你想检查任何 yaml 文件的格式,可以使用 linter 或使用带有
--dry-run
和--validate
标志的kubectl create
测试语法的有效性:kubectl create -f your_yaml_file.yaml --dry-run --validate=true
通常,在使用 kubectl 创建资源之前验证你的语法是一个好主意。
将以下代码添加到文件中以创建一个 Secret,它将使用你刚刚创建的编码值定义你的 MONGO_USERNAME
和 MONGO_PASSWORD
。请务必将此处的虚拟值替换为你的编码用户名和密码:
我们已将 Secret 对象命名为 mongo-secret
,但你可以随意命名它。
完成编辑后保存并关闭此文件。正如你对 .env
文件所做的那样,请务必将 secret.yaml
添加到你的 .gitignore
文件中,以使其不受版本控制。
编写完 secret.yaml
后,下一步将确保我们的应用程序和数据库 Pod 都使用我们添加到文件中的值。让我们首先在我们的应用 Deployment 中添加对 Secret 的引用。
打开名为 nodejs-deployment.yaml
的文件:
该文件的容器规范包括在 env 键下定义的以下环境变量:
我们需要向此处列出的 MONGO_USERNAME
和 MONGO_PASSWORD
变量添加对 Secret 的引用,以便我们的应用程序可以访问这些值。我们不会像 MONGO_DB
和 MONGO_PORT
的值那样包含一个 configMapKeyRef
键来指向我们的 nodejs-env
ConfigMap,而是包含一个 secretKeyRef
键来指向我们的 mongo-secret
密钥中的值。
将以下 Secret 引用添加到 MONGO_USERNAME
和 MONGO_PASSWORD
变量:
完成编辑后保存并关闭文件。
接下来,我们将相同的值添加到 db-deployment.yaml
文件中。
打开文件进行编辑:
在此文件中,我们将为以下变量键添加对 Secret 的引用:MONGO_INITDB_ROOT_USERNAME
和 MONGO_INITDB_ROOT_PASSWORD
。mongo 镜像使这些变量,以便你可以修改数据库实例的初始化。MONGO_INITDB_ROOT_USERNAME
和 MONGO_INITDB_ROOT_PASSWORD
一起在 admin 身份验证数据库中创建一个 root 用户,并确保在数据库容器启动时启用身份验证。
使用我们在 Secret 中设置的值可确保我们将拥有一个对数据库实例具有 root 权限的应用程序用户,并可以访问该角色的所有管理和操作权限。在生产中工作时,你需要创建一个具有适当范围权限的专用应用程序用户。
在 MONGO_INITDB_ROOT_USERNAME
和 MONGO_INITDB_ROOT_PASSWORD
变量下,添加对 Secret 值的引用:
完成编辑后保存并关闭文件。
有了 Secret,你可以继续创建数据库服务,并确保你的应用程序容器仅在完全设置和初始化后才尝试连接到数据库。
5.创建数据库和应用初始化容器
现在我们有了 Secret,我们可以继续创建我们的数据库服务和一个初始化容器,它将轮询这个服务,以确保我们的应用程序只在数据库启动任务后尝试连接到数据库,包括创建 MONGO_INITDB
用户和密码。
打开一个文件来定义数据库服务的规范:
将以下代码添加到文件中以定义服务:
我们在此处包含的选择器将将此 Service 对象与我们的数据库 Pod 匹配,这些 Pod 已使用标签 io.kompose.service: db
通过 kompose 在 db-deployment.yaml
文件中定义。我们还将此 Service 命名为 db。
完成编辑后保存并关闭文件。
接下来,让我们在 nodejs-deployment.yaml
中的容器数组中添加一个 Init Container 字段。这将创建一个 Init 容器,我们可以使用它来延迟我们的应用程序容器的启动,直到使用可访问的 Pod 创建 db 服务。
打开 nodejs-deployment.yaml
文件:
在 Pod 规范中和容器数组旁边,我们将添加一个带有容器的 initContainers
字段,该容器将轮询 db 服务。
在 nodejs 容器数组中的端口和资源字段下方以及 restartPolicy
上方添加以下代码:
此 Init Container 使用 BusyBox 镜像,这是一个包含许多 UNIX 实用程序的轻量级镜像。在这种情况下,我们将使用 netcat
来轮询与 db 服务关联的 Pod 是否在端口 27017 上接受 TCP 连接。
此容器命令复制了我们在步骤 3 中从 docker-compose.yaml
文件中删除的等待脚本的功能。初始化容器运行完成;在我们的例子中,这意味着我们的 Node 应用程序容器在数据库容器运行并接受端口 27017 上的连接之前不会启动。db 服务定义允许我们保证这个功能,而不管数据库容器的确切位置是可变的。
完成编辑后保存并关闭文件。
创建数据库服务并准备好 Init Container 以控制容器的启动顺序后,你可以继续检查 PersistentVolumeClaim 中的存储要求并使用 LoadBalancer 公开你的应用程序服务。
6.修改 PVC 并暴露应用
在运行我们的应用程序之前,我们将进行两项最终更改,以确保我们的数据库存储将被正确配置,并且我们可以使用 LoadBalancer 暴露我们的应用程序前端。
首先,让我们修改 kompose 为我们创建的 PersistentVolumeClaim 中定义的存储资源。这个声明允许我们动态配置存储来管理我们应用程序的状态。
要使用 PersistentVolumeClaims,你必须创建一个 StorageClass 并将其配置为供应存储资源。在我们的例子中,因为我们使用的是 DigitalOcean Kubernetes,所以我们的默认 StorageClass 配置器设置为 dobs.csi.digitalocean.com
— DigitalOcean 块存储。
我们可以使用以下命令进行检查:
如果你使用的是 DigitalOcean 集群,你将看到以下输出:
如果你不使用 DigitalOcean 集群,则需要创建一个 StorageClass 。有关如何执行此操作的
当 kompose 创建 dbdata-persistentvolumeclaim.yaml
时,它将存储资源设置为不满足我们要求的大小。因此,我们需要修改 PersistentVolumeClaim 以使用最小可行的 DigitalOcean 块存储单元:1GB。请随意修改它以满足你的存储要求。
打开 dbdata-persistentvolumeclaim.yaml
:
将存储值替换为 1Gi:
另请注意 accessMode:ReadWriteOnce
意味着由于此声明而配置的卷将只能由单个节点读写。
完成后保存并关闭文件。
接下来,打开 nodejs-service.yaml
:
我们将使用 DigitalOcean 负载均衡器在外部暴露此服务。如果你没有使用 DigitalOcean 集群,请查阅你的云提供商的相关文档以获取有关其负载均衡器的信息。
在 Service 规范中,将 LoadBalancer 指定为服务类型:
当我们创建 nodejs 服务时,会自动创建一个负载均衡器,为我们提供一个外部 IP,我们可以在其中访问我们的应用程序。
完成编辑后保存并关闭文件。
准备好所有文件后,我们就可以开始并测试应用程序了。
7.启动和访问应用程序
是时候创建我们的 Kubernetes 对象并测试我们的应用程序是否按预期工作了。
要创建我们定义的对象,我们将使用带有 -f
标志的 kubectl create
命令,这将允许我们指定 kompose 为我们创建的文件以及我们编写的文件。运行以下命令创建 Node 应用和 MongoDB 数据库 Service 和 Deployment,以及你的 Secret、ConfigMap 和 PersistentVolumeClaim:
你将看到以下输出,表明对象已创建:
要检查你的 Pod 是否正在运行,请输入:
你无需在此处指定命名空间,因为我们已经在默认命名空间中创建了对象。
当你的 db 容器正在启动并且你的应用程序 Init Container 正在运行时,你将看到以下输出:
一旦该容器运行并且你的应用程序和数据库容器已启动,你将看到以下输出:
Running
STATUS 表示你的 Pod 已绑定到节点,并且与这些 Pod 关联的容器正在运行。READY
表示一个 Pod 中有多少个容器正在运行。
注意:如果你在 STATUS 列中看到意外阶段,请记住你可以使用以下命令对 Pod 进行故障排除:
随着你的容器运行,你现在可以访问该应用程序。要获取 LoadBalancer 的 IP,请键入:
你将看到以下输出:
与 nodejs 服务关联的 EXTERNAL_IP
是你可以访问应用程序的 IP 地址。如果你在 EXTERNAL_IP
列中看到状态,这意味着你的负载均衡器仍在创建中。
在该列中看到 IP 后,在浏览器中导航到它:http://your_lb_ip
。
你应该看到以下登录页面:
单击获取鲨鱼信息按钮。你将看到一个带有输入表单的页面,你可以在其中输入鲨鱼名称和对该鲨鱼一般特征的描述:
在表格中,添加你选择的鲨鱼。为了演示,我们将 Megalodon Shark 添加到 Shark Name 字段,并将 Ancient 到 Shark Character 字段:
单击提交按钮。你将看到一个页面,其中向你显示此鲨鱼信息:
你现在拥有一个 Node.js 应用程序的单实例应用,其中包含在 Kubernetes 集群上运行的 MongoDB 数据库。
版权声明: 本文为 InfoQ 作者【CTO技术共享】的原创文章。
原文链接:【http://xie.infoq.cn/article/9808d6e832ee3e5f2fb85ce0c】。未经作者许可,禁止转载。
评论