如何使用Helm在Kubernetes上使用MongoDB扩展Node.js应用程序
介绍
Kubernetes 是一个用于大规模运行现代容器化应用程序的系统。 有了它,开发人员可以跨机器集群部署和管理应用程序。 尽管它可用于提高单实例应用程序设置的效率和可靠性,但 Kubernetes 旨在跨机器组运行应用程序的多个实例。
在使用 Kubernetes 创建多服务部署时,许多开发人员选择使用 Helm 包管理器。 Helm 通过提供协调这些对象如何交互的图表和模板来简化创建多个 Kubernetes 资源的过程。 它还为流行的开源项目提供了预打包的图表。
在本教程中,您将使用 Helm 图表将带有 MongoDB 数据库的 Node.js 应用程序部署到 Kubernetes 集群上。 您将使用 官方 Helm MongoDB 副本集图表 创建一个 StatefulSet 对象,由三个 Pods、一个 Headless Service 和三个PersistentVolumeClaims。 您还将创建一个图表以使用自定义应用程序映像部署多副本 Node.js 应用程序。 您将在本教程中构建的设置将反映 使用 Docker Compose 将 Node.js 应用程序容器化 中描述的代码的功能,并且将是使用 MongoDB 构建弹性 Node.js 应用程序的良好起点可以根据您的需求扩展的数据存储。
先决条件
要完成本教程,您需要:
- 启用了基于角色的访问控制 (RBAC) 的 Kubernetes 1.10+ 集群。 此设置将使用 DigitalOcean Kubernetes 集群,但您可以自由地 使用另一种方法 创建集群。
kubectl
命令行工具安装在您的本地机器或开发服务器上并配置为连接到您的集群。 您可以在官方文档中阅读更多关于安装kubectl
的信息。- Helm 安装在本地机器或开发服务器上,Tiller 安装在集群上,遵循 如何使用 Helm 包管理器 在 Kubernetes 集群上安装软件的步骤 1 和 2 中概述的说明。
- Docker 安装在本地机器或开发服务器上。 如果您使用的是 Ubuntu 18.04,请按照 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2; 否则,请按照 官方文档 了解有关在其他操作系统上安装的信息。 确保将您的非 root 用户添加到
docker
组,如链接教程的第 2 步所述。 - 一个 Docker Hub 帐户。 有关如何设置的概述,请参阅 this Introduction to Docker Hub。
第 1 步 — 克隆和打包应用程序
要将我们的应用程序与 Kubernetes 一起使用,我们需要将其打包,以便 kubelet 代理 可以拉取镜像。 但是,在打包应用程序之前,我们需要修改应用程序代码中的 MongoDB 连接 URI,以确保我们的应用程序可以连接到我们将使用 Helm mongodb-replicaset
图表。
我们的第一步是从 DigitalOcean 社区 GitHub 帐户 克隆 node-mongo-docker-dev 存储库。 此存储库包含 Containerizing a Node.js Application for Development With Docker Compose 中描述的设置中的代码,它使用带有 MongoDB 数据库的演示 Node.js 应用程序来演示如何设置开发环境码头工人撰写。 您可以在 From Containers to Kubernetes with Node.js 系列中找到有关应用程序本身的更多信息。
将存储库克隆到名为 node_project
的目录中:
git clone https://github.com/do-community/node-mongo-docker-dev.git node_project
导航到 node_project
目录:
cd node_project
node_project
目录包含用于处理用户输入的鲨鱼信息应用程序的文件和目录。 它已经现代化,可以与容器一起使用:敏感和特定的配置信息已从应用程序代码中删除并重构为在运行时注入,应用程序的状态已卸载到 MongoDB 数据库。
有关设计现代容器化应用程序的更多信息,请参阅 为 Kubernetes 构建应用程序 和 为 Kubernetes 现代化应用程序。
当我们部署 Helm mongodb-replicaset
图表时,它将创建:
- 具有三个 Pod 的 StatefulSet 对象——MongoDB 副本集 的成员。 每个 Pod 都会有一个关联的 PersistentVolumeClaim,并且在重新调度时会保持一个固定的身份。
- 由 StatefulSet 中的 Pod 组成的 MongoDB 副本集。 该集合将包括一个主要和两个次要。 数据将从主服务器复制到辅助服务器,确保我们的应用程序数据保持高可用性。
为了让我们的应用程序与数据库副本进行交互,我们代码中的 MongoDB 连接 URI 将需要包括副本集成员的主机名以及副本集本身的名称。 因此,我们需要在 URI 中包含这些值。
我们克隆的存储库中指定数据库连接信息的文件称为 db.js
。 现在使用 nano
或您喜欢的编辑器打开该文件:
nano db.js
目前,该文件包括运行时在数据库连接 URI 中引用的 constants。 这些常量的值是使用 Node 的 process.env 属性注入的,该属性返回一个对象,其中包含有关运行时用户环境的信息。 在我们的应用程序代码中动态设置值允许我们将代码与底层基础设施分离,这在动态、无状态的环境中是必需的。 有关以这种方式重构应用程序代码的更多信息,请参阅 Containerizing a Node.js Application for Development With Docker Compose 的 Step 2 以及 The 12-Factor 中的相关讨论应用程序。
连接 URI 和 URI 字符串本身的常量当前如下所示:
~/node_project/db.js
... const { MONGO_USERNAME, MONGO_PASSWORD, MONGO_HOSTNAME, MONGO_PORT, MONGO_DB } = process.env; ... const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`; ...
为了与 12FA 方法保持一致,我们不想将副本实例的主机名或副本集名称硬编码到此 URI 字符串中。 现有的 MONGO_HOSTNAME
常量可以扩展为包含多个主机名——我们的副本集的成员——所以我们将保留它。 但是,我们需要在 URI 字符串的 选项部分 中添加一个副本集常量。
将 MONGO_REPLICASET
添加到 URI 常量对象和连接字符串:
~/node_project/db.js
... const { MONGO_USERNAME, MONGO_PASSWORD, MONGO_HOSTNAME, MONGO_PORT, MONGO_DB, MONGO_REPLICASET } = process.env; ... const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?replicaSet=${MONGO_REPLICASET}&authSource=admin`; ...
在 URI 的选项部分中使用 replicaSet 选项 允许我们传入副本集的名称,它与 MONGO_HOSTNAME
常量中定义的主机名一起,将允许我们连接到集合成员。
完成编辑后保存并关闭文件。
修改数据库连接信息以使用副本集后,您现在可以打包应用程序,使用 docker build 命令构建映像,并将其推送到 Docker Hub。
使用 docker build
和 -t
标志构建图像,这使您可以使用易于记忆的名称标记图像。 在这种情况下,使用您的 Docker Hub 用户名标记图像并将其命名为 node-replicas
或您自己选择的名称:
docker build -t your_dockerhub_username/node-replicas .
命令中的 .
指定构建上下文是当前目录。
构建映像需要一两分钟。 完成后,检查您的图像:
docker images
您将看到以下输出:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/node-replicas latest 56a69b4bc882 7 seconds ago 90.1MB node 10-alpine aa57b0242b33 6 days ago 71MB
接下来,登录到您在先决条件中创建的 Docker Hub 帐户:
docker login -u your_dockerhub_username
出现提示时,输入您的 Docker Hub 帐户密码。 以这种方式登录将使用您的 Docker Hub 凭据在非 root 用户的主目录中创建一个 ~/.docker/config.json
文件。
使用 docker push 命令 将应用程序镜像推送到 Docker Hub。 请记住将 your_dockerhub_username
替换为您自己的 Docker Hub 用户名:
docker push your_dockerhub_username/node-replicas
您现在拥有一个应用程序映像,您可以拉取该映像以使用 Kubernetes 运行您复制的应用程序。 下一步将配置特定参数以与 MongoDB Helm 图表一起使用。
第 2 步 — 为 MongoDB 副本集创建 Secret
stable/mongodb-replicaset
图表在使用 Secrets 时提供了不同的选项,我们将创建两个用于图表部署:
- 我们的 副本集密钥文件 的秘密,它将作为副本集成员之间的共享密码,允许他们对其他成员进行身份验证。
- 我们的 MongoDB 管理员用户的秘密,将在
admin
数据库上创建为 root 用户。 此角色将允许您在将应用程序部署到生产环境时创建具有有限权限的后续用户。
有了这些 Secret,我们将能够在专用值文件中设置我们的首选参数值,并使用 Helm 图表创建 StatefulSet 对象和 MongoDB 副本集。
首先,让我们创建密钥文件。 我们将使用带有 rand
选项的 openssl 命令 为密钥文件生成一个 756 字节的随机字符串:
openssl rand -base64 756 > key.txt
该命令生成的输出将被 base64 编码,确保统一的数据传输,并重定向到一个名为 key.txt
的文件,遵循 mongodb-replicaset 图表身份验证文档中所述的准则。 键本身 的长度必须介于 6 到 1024 个字符之间,仅包含 base64 集中的字符。
您现在可以使用此文件和 kubectl create 创建一个名为 keyfilesecret
的 Secret:
kubectl create secret generic keyfilesecret --from-file=key.txt
这将在 default
命名空间 中创建一个 Secret 对象,因为我们还没有为我们的设置创建一个特定的命名空间。
您将看到以下输出,表明您的 Secret 已创建:
Outputsecret/keyfilesecret created
删除 key.txt
:
rm key.txt
或者,如果您想保存文件,请确保 限制其权限 并将其添加到您的 .gitignore 文件 以使其不受版本控制。
接下来,为您的 MongoDB 管理员用户创建 Secret。 第一步是将您想要的用户名和密码转换为 base64。
转换您的数据库用户名:
echo -n 'your_database_username' | base64
记下您在输出中看到的值。
接下来,转换您的密码:
echo -n 'your_database_password' | base64
还要注意此处输出中的值。
打开 Secret 文件:
nano secret.yaml
注意:Kubernetes对象是通常使用YAML定义,严格禁止制表符,缩进需要两个空格。 如果您想检查任何 YAML 文件的格式,可以使用 linter 或使用 kubectl create
和 --dry-run
和 [ X174X] 标志:
kubectl create -f your_yaml_file.yaml --dry-run --validate=true
通常,在使用 kubectl
创建资源之前验证您的语法是个好主意。
将以下代码添加到文件中以创建一个 Secret,该 Secret 将使用您刚刚创建的编码值定义 user
和 password
。 请务必将此处的虚拟值替换为您自己的 编码 用户名和密码:
~/node_project/secret.yaml
apiVersion: v1 kind: Secret metadata: name: mongo-secret data: user: your_encoded_username password: your_encoded_password
在这里,我们使用 mongodb-replicaset
图表预期的键名:user
和 password
。 我们已将 Secret 对象命名为 mongo-secret
,但您可以随意命名它。
完成编辑后保存并关闭文件。
使用以下命令创建 Secret 对象:
kubectl create -f secret.yaml
您将看到以下输出:
Outputsecret/mongo-secret created
同样,您可以删除 secret.yaml
或限制其权限并将其添加到您的 .gitignore
文件中。
创建 Secret 对象后,您可以继续指定将与 mongodb-replicaset
图表一起使用的参数值并创建 MongoDB 部署。
第 3 步 — 配置 MongoDB Helm 图表并创建部署
Helm 带有一个名为 stable 的积极维护的存储库,其中包含我们将使用的图表:mongodb-replicaset
。 要将此图表与我们刚刚创建的秘密一起使用,我们将创建一个名为 mongodb-values.yaml
的配置参数值文件,然后使用此文件安装图表。
我们的 mongodb-values.yaml
文件将在很大程度上反映 mongodb-replicaset
图表存储库中默认的 values.yaml 文件。 但是,我们将对文件进行以下更改:
- 我们将
auth
参数设置为true
以确保我们的数据库实例以 启用授权 开始。 这意味着所有客户端都需要进行身份验证才能访问数据库资源和操作。 - 我们将添加有关我们在上一步中创建的 Secret 的信息,以便图表可以使用这些值来创建副本集密钥文件和管理员用户。
- 我们将减小与 StatefulSet 中每个 Pod 关联的 PersistentVolume 的大小,以使用 最小可行的 DigitalOcean 块存储单元 ,1GB,尽管您可以随意修改它以满足您的存储要求。
但是,在编写 mongodb-values.yaml
文件之前,您应该首先检查是否已创建并配置了一个 StorageClass 以供应存储资源。 数据库 StatefulSet 中的每个 Pod 都将具有粘性标识和关联的 PersistentVolumeClaim,这将为 Pod 动态配置 PersistentVolume。 如果重新调度 Pod,则 PersistentVolume 将挂载到调度 Pod 的任何节点(但如果永久删除关联的 Pod 或 StatefulSet,则必须手动删除每个卷)。
因为我们正在使用 DigitalOcean Kubernetes,我们的默认 StorageClass provisioner
设置为 dobs.csi.digitalocean.com
— DigitalOcean Block Storage — 我们可以通过键入以下内容进行检查:
kubectl get storageclass
如果您使用的是 DigitalOcean 集群,您将看到以下输出:
OutputNAME PROVISIONER AGE do-block-storage (default) dobs.csi.digitalocean.com 21m
如果您不使用 DigitalOcean 集群,则需要创建一个 StorageClass 并配置您选择的 provisioner
。 具体操作方法请参见【X49X】官方文档【X75X】。
现在您已确保配置了 StorageClass,打开 mongodb-values.yaml
进行编辑:
nano mongodb-values.yaml
您将在此文件中设置将执行以下操作的值:
- 启用授权。
- 引用您的
keyfilesecret
和mongo-secret
对象。 - 为您的 PersistentVolume 指定
1Gi
。 - 将您的副本集名称设置为
db
。 - 为集合指定
3
副本。 - 将
mongo
映像固定到撰写本文时的最新版本:4.1.9
。
将以下代码粘贴到文件中:
~/node_project/mongodb-values.yaml
replicas: 3 port: 27017 replicaSetName: db podDisruptionBudget: {} auth: enabled: true existingKeySecret: keyfilesecret existingAdminSecret: mongo-secret imagePullSecrets: [] installImage: repository: unguiculus/mongodb-install tag: 0.7 pullPolicy: Always copyConfigImage: repository: busybox tag: 1.29.3 pullPolicy: Always image: repository: mongo tag: 4.1.9 pullPolicy: Always extraVars: {} metrics: enabled: false image: repository: ssalaues/mongodb-exporter tag: 0.6.1 pullPolicy: IfNotPresent port: 9216 path: /metrics socketTimeout: 3s syncTimeout: 1m prometheusServiceDiscovery: true resources: {} podAnnotations: {} securityContext: enabled: true runAsUser: 999 fsGroup: 999 runAsNonRoot: true init: resources: {} timeout: 900 resources: {} nodeSelector: {} affinity: {} tolerations: [] extraLabels: {} persistentVolume: enabled: true #storageClass: "-" accessModes: - ReadWriteOnce size: 1Gi annotations: {} serviceAnnotations: {} terminationGracePeriodSeconds: 30 tls: enabled: false configmap: {} readinessProbe: initialDelaySeconds: 5 timeoutSeconds: 1 failureThreshold: 3 periodSeconds: 10 successThreshold: 1 livenessProbe: initialDelaySeconds: 30 timeoutSeconds: 5 failureThreshold: 3 periodSeconds: 10 successThreshold: 1
persistentVolume.storageClass
参数在此处被注释掉:删除注释并将其值设置为 "-"
将禁用动态配置。 在我们的例子中,因为我们没有定义这个值,所以图表将选择默认的 provisioner
— 在我们的例子中,dobs.csi.digitalocean.com
。
另请注意与 persistentVolume
键关联的 accessMode
:ReadWriteOnce
表示配置的卷将只能由单个节点读写。 有关不同访问模式的更多信息,请参阅 文档。
要了解有关文件中包含的其他参数的更多信息,请参阅 repo 中包含的 配置表 。
完成编辑后保存并关闭文件。
在部署 mongodb-replicaset
图表之前,您需要使用 helm repo update 命令 更新 stable repo:
helm repo update
这将从 stable 存储库中获取最新的图表信息。
最后,使用以下命令安装图表:
helm install --name mongo -f mongodb-values.yaml stable/mongodb-replicaset
注意: 在安装图表之前,您可以使用 --dry-run
和 --debug
选项运行 helm install
来检查为您的版本生成的清单:
helm install --name your_release_name -f your_values_file.yaml --dry-run --debug your_chart
请注意,我们将 Helm 命名为 release mongo
。 此名称将引用我们指定的配置选项的图表的此特定部署。 我们通过包含 -f
标志和我们的 mongodb-values.yaml
文件来指出这些选项。
另请注意,由于我们没有在 helm install
中包含 --namespace
标志,因此我们的图表对象将在 default
命名空间中创建。
创建发布后,您将看到有关其状态的输出,以及有关已创建对象的信息以及与它们交互的说明:
OutputNAME: mongo LAST DEPLOYED: Tue Apr 16 21:51:05 2019 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1/ConfigMap NAME DATA AGE mongo-mongodb-replicaset-init 1 1s mongo-mongodb-replicaset-mongodb 1 1s mongo-mongodb-replicaset-tests 1 0s ...
您现在可以使用以下命令检查 Pod 的创建:
kubectl get pods
在创建 Pod 时,您将看到如下输出:
OutputNAME READY STATUS RESTARTS AGE mongo-mongodb-replicaset-0 1/1 Running 0 67s mongo-mongodb-replicaset-1 0/1 Init:0/3 0 8s
此处的 READY
和 STATUS
输出表明 StatefulSet 中的 Pod 尚未完全准备好:与 Pod 的容器关联的 Init Containers 仍在运行。 因为 StatefulSet 成员是 按顺序 创建的,所以 StatefulSet 中的每个 Pod 必须是 Running
和 Ready
才能创建下一个 Pod。
创建 Pod 并且所有关联的容器都在运行后,您将看到以下输出:
OutputNAME READY STATUS RESTARTS AGE mongo-mongodb-replicaset-0 1/1 Running 0 2m33s mongo-mongodb-replicaset-1 1/1 Running 0 94s mongo-mongodb-replicaset-2 1/1 Running 0 36s
Running
STATUS
表示您的 Pod 已绑定到节点,并且与这些 Pod 关联的容器正在运行。 READY
表示一个 Pod 中有多少个容器正在运行。 有关详细信息,请参阅有关 Pod 生命周期的 文档。
注意: 如果您在 STATUS
列中看到意外的阶段,请记住您可以使用以下命令对 Pod 进行故障排除:
kubectl describe pods your_pod kubectl logs your_pod
StatefulSet 中的每个 Pod 都有一个名称,它结合了 StatefulSet 的名称和 Pod 的 序数索引 。 因为我们创建了三个副本,所以我们的 StatefulSet 成员编号为 0-2,每个都有一个 稳定的 DNS 条目 ,由以下元素组成:$(statefulset-name)-$(ordinal).$(service name).$(namespace).svc.cluster.local
。
在我们的例子中,由 mongodb-replicaset
图表创建的 StatefulSet 和 Headless Service 具有相同的名称:
kubectl get statefulset
OutputNAME READY AGE mongo-mongodb-replicaset 3/3 4m2s
kubectl get svc
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 42m mongo-mongodb-replicaset ClusterIP None <none> 27017/TCP 4m35s mongo-mongodb-replicaset-client ClusterIP None <none> 27017/TCP 4m35s
这意味着我们的 StatefulSet 的第一个成员将具有以下 DNS 条目:
mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local
因为我们需要我们的应用程序连接到每个 MongoDB 实例,所以我们必须拥有这些信息,以便我们可以直接与 Pod 通信,而不是与服务通信。 当我们创建自定义应用程序 Helm 图表时,我们将使用环境变量将每个 Pod 的 DNS 条目传递给我们的应用程序。
启动并运行数据库实例后,您就可以为 Node 应用程序创建图表了。
第 4 步 — 创建自定义应用程序图表并配置参数
我们将为我们的 Node 应用程序创建一个自定义 Helm 图表,并修改标准图表目录中的默认文件,以便我们的应用程序可以使用我们刚刚创建的副本集。 我们还将创建文件来为我们的应用程序定义 ConfigMap 和 Secret 对象。
首先,使用以下命令创建一个名为 nodeapp
的新图表目录:
helm create nodeapp
这将在您的 ~/node_project
文件夹中创建一个名为 nodeapp
的目录,其中包含以下资源:
- 包含有关图表的基本信息的
Chart.yaml
文件。 - 一个
values.yaml
文件,允许您设置特定的参数值,就像您在 MongoDB 部署中所做的那样。 - 一个
.helmignore
文件,其中包含在打包图表时将被忽略的文件和目录模式。 templates/
目录,其中包含将生成 Kubernetes 清单的模板文件。- 用于测试文件的
templates/tests/
目录。 - 此图表所依赖的任何图表的
charts/
目录。
我们将在这些默认文件中修改的第一个文件是 values.yaml
。 现在打开该文件:
nano nodeapp/values.yaml
我们将在此处设置的值包括:
- 副本数。
- 我们要使用的应用程序图像。 在我们的例子中,这将是我们在 Step 1 中创建的
node-replicas
图像。 - 服务类型。 在这种情况下,我们将指定 LoadBalancer 来创建对我们的应用程序的访问点以用于测试目的。 因为我们正在使用 DigitalOcean Kubernetes 集群,所以当我们部署图表时,这将创建一个 DigitalOcean 负载均衡器 。 在生产环境中,您可以将图表配置为使用 Ingress Resources 和 Ingress Controllers 将流量路由到您的服务。
- targetPort 用于指定 Pod 上将公开我们的应用程序的端口。
我们不会在这个文件中输入环境变量。 相反,我们将为 ConfigMap 和 Secret 对象创建模板,并将这些值添加到位于 ~/node_project/nodeapp/templates/deployment.yaml
的应用程序部署清单中。
在 values.yaml
文件中配置以下值:
~/node_project/nodeapp/values.yaml
# Default values for nodeapp. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 3 image: repository: your_dockerhub_username/node-replicas tag: latest pullPolicy: IfNotPresent nameOverride: "" fullnameOverride: "" service: type: LoadBalancer port: 80 targetPort: 8080 ...
完成编辑后保存并关闭文件。
接下来,在nodeapp/templates
目录下打开一个secret.yaml
文件:
nano nodeapp/templates/secret.yaml
在此文件中,为您的 MONGO_USERNAME
和 MONGO_PASSWORD
应用程序常量添加值。 这些是您的应用程序期望在运行时访问的常量,如您的数据库连接文件 db.js
中所指定的。 当您添加这些常量的值时,请记住使用您之前在创建 mongo-secret
对象时在 Step 2 中使用的 base64-encoded 值。 如果您需要重新创建这些值,您可以返回到第 2 步并再次运行相关命令。
将以下代码添加到文件中:
~/node_project/nodeapp/templates/secret.yaml
apiVersion: v1 kind: Secret metadata: name: {{ .Release.Name }}-auth data: MONGO_USERNAME: your_encoded_username MONGO_PASSWORD: your_encoded_password
此 Secret 对象的名称将取决于您的 Helm 版本的名称,您将在部署应用程序图表时指定该名称。
完成后保存并关闭文件。
接下来,打开一个文件为您的应用程序创建一个 ConfigMap:
nano nodeapp/templates/configmap.yaml
在这个文件中,我们将定义我们的应用程序期望的其余变量:MONGO_HOSTNAME
、MONGO_PORT
、MONGO_DB
和 MONGO_REPLICASET
。 我们的 MONGO_HOSTNAME
变量将包括副本集中 each 实例的 DNS 条目,因为这是 MongoDB 连接 URI 需要的 。
根据 Kubernetes 文档,当应用程序实现活跃度和就绪性检查时,连接 Pod 时应使用 SRV 记录。 如 Step 3 中所述,我们的 Pod SRV 记录遵循以下模式:$(statefulset-name)-$(ordinal).$(service name).$(namespace).svc.cluster.local
。 由于我们的 MongoDB StatefulSet 实现了 liveness 和 readiness 检查,我们应该在定义 MONGO_HOSTNAME
变量的值时使用这些稳定的标识符。
将以下代码添加到文件中以定义 MONGO_HOSTNAME
、MONGO_PORT
、MONGO_DB
和 MONGO_REPLICASET
变量。 您可以为您的 MONGO_DB
数据库随意使用其他名称,但您的 MONGO_HOSTNAME
和 MONGO_REPLICASET
值必须按照此处显示的方式写入:
~/node_project/nodeapp/templates/configmap.yaml
apiVersion: v1 kind: ConfigMap metadata: name: {{ .Release.Name }}-config data: MONGO_HOSTNAME: "mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local,mongo-mongodb-replicaset-1.mongo-mongodb-replicaset.default.svc.cluster.local,mongo-mongodb-replicaset-2.mongo-mongodb-replicaset.default.svc.cluster.local" MONGO_PORT: "27017" MONGO_DB: "sharkinfo" MONGO_REPLICASET: "db"
因为我们已经创建了 StatefulSet 对象和副本集,所以此处列出的主机名必须在您的文件中列出,与本示例中的显示完全相同。 如果您销毁这些对象并重命名您的 MongoDB Helm 版本,那么您将需要修改此 ConfigMap 中包含的值。 这同样适用于 MONGO_REPLICASET
,因为我们在 MongoDB 版本中指定了副本集名称。
另请注意,此处列出的值是引用的,这是 Helm 中环境变量的期望值。
完成编辑后保存并关闭文件。
定义图表参数值并创建 Secret 和 ConfigMap 清单后,您可以编辑应用程序部署模板以使用您的环境变量。
第 5 步 — 将环境变量集成到您的 Helm 部署中
有了我们的应用程序 Secret 和 ConfigMap 的文件,我们需要确保我们的应用程序部署可以使用这些值。 我们还将自定义已在部署清单中定义的 活跃度和就绪性探测器 。
打开应用程序部署模板进行编辑:
nano nodeapp/templates/deployment.yaml
尽管这是一个 YAML 文件,但 Helm 模板使用与标准 Kubernetes YAML 文件不同的语法来生成清单。 有关模板的更多信息,请参阅 Helm 文档。
在文件中,首先将 env
键添加到您的应用程序容器规范中,在 imagePullPolicy
键下方和 ports
上方:
~/node_project/nodeapp/templates/deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: ... spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: ports:
接下来,将以下键添加到 env
变量列表中:
~/node_project/nodeapp/templates/deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: ... spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} env: - name: MONGO_USERNAME valueFrom: secretKeyRef: key: MONGO_USERNAME name: {{ .Release.Name }}-auth - name: MONGO_PASSWORD valueFrom: secretKeyRef: key: MONGO_PASSWORD name: {{ .Release.Name }}-auth - name: MONGO_HOSTNAME valueFrom: configMapKeyRef: key: MONGO_HOSTNAME name: {{ .Release.Name }}-config - name: MONGO_PORT valueFrom: configMapKeyRef: key: MONGO_PORT name: {{ .Release.Name }}-config - name: MONGO_DB valueFrom: configMapKeyRef: key: MONGO_DB name: {{ .Release.Name }}-config - name: MONGO_REPLICASET valueFrom: configMapKeyRef: key: MONGO_REPLICASET name: {{ .Release.Name }}-config
每个变量都包含对其值的引用,在 Secret 值的情况下由 secretKeyRef 键 定义,对于 ConfigMap 值由 configMapKeyRef 定义。 这些键指向我们在上一步中创建的 Secret 和 ConfigMap 文件。
接下来,在 ports
键下,修改 containerPort
定义以指定容器上将暴露我们的应用程序的端口:
~/node_project/nodeapp/templates/deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: ... spec: containers: ... env: ... ports: - name: http containerPort: 8080 protocol: TCP ...
接下来,让我们修改默认包含在此部署清单中的活动性和就绪性检查。 这些检查确保我们的应用程序 Pod 正在运行并准备好为流量提供服务:
- 就绪探测评估 Pod 是否准备好为流量提供服务,停止对 Pod 的所有请求,直到检查成功。
- Liveness 探针检查基本的应用程序行为,以确定容器中的应用程序是否按预期运行和表现。 如果活性探测失败,Kubernetes 将重新启动容器。
有关两者的更多信息,请参阅 Architecting Applications for Kubernetes 中的 相关讨论 。
在我们的例子中,我们将构建 Helm 默认提供的 httpGet 请求 并测试我们的应用程序是否正在接受 /sharks
端点上的请求。 kubelet 服务 将通过向运行在应用程序 Pod 容器中的节点服务器发送 GET 请求并侦听端口 8080
来执行探测。 如果响应的状态码介于 200 和 400 之间,则 kubelet
将得出容器健康的结论。 否则,在状态为 400 或 500 的情况下,kubelet
将在就绪探测的情况下停止到容器的流量,或者在活性探测的情况下重新启动容器。
为 liveness 和 readiness 探针添加以下修改:
~/node_project/nodeapp/templates/deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: ... spec: containers: ... env: ... ports: - name: http containerPort: 8080 protocol: TCP livenessProbe: httpGet: path: /sharks port: http readinessProbe: httpGet: path: /sharks port: http
完成编辑后保存并关闭文件。
您现在已准备好使用 Helm 创建应用程序版本。 运行以下 helm install 命令,其中包括发布的名称和图表目录的位置:
helm install --name nodejs ./nodeapp
请记住,您可以首先使用 --dry-run
和 --debug
选项运行 helm install
,如 Step 3 中所述,检查为您的版本生成的清单。
同样,因为我们没有在 helm install
中包含 --namespace
标志,我们的图表对象将在 default
命名空间中创建。
您将看到以下输出,表明您的版本已创建:
OutputNAME: nodejs LAST DEPLOYED: Wed Apr 17 18:10:29 2019 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1/ConfigMap NAME DATA AGE nodejs-config 4 1s ==> v1/Deployment NAME READY UP-TO-DATE AVAILABLE AGE nodejs-nodeapp 0/3 3 0 1s ...
同样,输出将指示发布的状态,以及有关已创建对象的信息以及如何与它们交互。
检查 Pod 的状态:
kubectl get pods
OutputNAME READY STATUS RESTARTS AGE mongo-mongodb-replicaset-0 1/1 Running 0 57m mongo-mongodb-replicaset-1 1/1 Running 0 56m mongo-mongodb-replicaset-2 1/1 Running 0 55m nodejs-nodeapp-577df49dcc-b5fq5 1/1 Running 0 117s nodejs-nodeapp-577df49dcc-bkk66 1/1 Running 0 117s nodejs-nodeapp-577df49dcc-lpmt2 1/1 Running 0 117s
一旦您的 Pod 启动并运行,请检查您的服务:
kubectl get svc
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 96m mongo-mongodb-replicaset ClusterIP None <none> 27017/TCP 58m mongo-mongodb-replicaset-client ClusterIP None <none> 27017/TCP 58m nodejs-nodeapp LoadBalancer 10.245.33.46 your_lb_ip 80:31518/TCP 3m22s
与 nodejs-nodeapp
服务关联的 EXTERNAL_IP
是您可以从集群外部访问应用程序的 IP 地址。 如果您在 EXTERNAL_IP
列中看到 <pending>
状态,这意味着您的负载均衡器仍在创建中。
在该列中看到 IP 后,在浏览器中导航到它:http://your_lb_ip
。
您应该看到以下登录页面:
现在您的复制应用程序正在运行,让我们添加一些测试数据以确保复制在副本集的成员之间正常工作。
第 6 步 — 测试 MongoDB 复制
随着我们的应用程序运行并通过外部 IP 地址访问,我们可以添加一些测试数据并确保它在我们的 MongoDB 副本集的成员之间被复制。
首先,确保您已将浏览器导航到应用程序登录页面:
单击获取鲨鱼信息按钮。 您将看到一个带有输入表单的页面,您可以在其中输入鲨鱼名称和对该鲨鱼一般特征的描述:
在表格中,添加您选择的初始鲨鱼。 为了演示,我们将 Megalodon Shark
添加到 Shark Name 字段,并将 Ancient
添加到 Shark Character 字段:
单击提交按钮。 您将看到一个页面,其中向您显示此鲨鱼信息:
现在点击顶部导航栏中的 Sharks 返回鲨鱼信息表单:
输入您选择的新鲨鱼。 我们将使用 Whale Shark
和 Large
:
单击 Submit 后,您将看到新的鲨鱼已添加到数据库中的鲨鱼集合中:
让我们检查一下我们输入的数据是否已经在我们的副本集的主要和次要成员之间复制。
获取您的 Pod 列表:
kubectl get pods
OutputNAME READY STATUS RESTARTS AGE mongo-mongodb-replicaset-0 1/1 Running 0 74m mongo-mongodb-replicaset-1 1/1 Running 0 73m mongo-mongodb-replicaset-2 1/1 Running 0 72m nodejs-nodeapp-577df49dcc-b5fq5 1/1 Running 0 5m4s nodejs-nodeapp-577df49dcc-bkk66 1/1 Running 0 5m4s nodejs-nodeapp-577df49dcc-lpmt2 1/1 Running 0 5m4s
要访问 Pod 上的 mongo shell,您可以使用 kubectl exec 命令 和在 Step 2 中创建 mongo-secret
时使用的用户名]。 使用以下命令访问 StatefulSet 中第一个 Pod 上的 mongo
shell:
kubectl exec -it mongo-mongodb-replicaset-0 -- mongo -u your_database_username -p --authenticationDatabase admin
出现提示时,输入与此用户名关联的密码:
OutputMongoDB shell version v4.1.9 Enter password:
您将被放入一个管理 shell:
OutputMongoDB server version: 4.1.9 Welcome to the MongoDB shell. ... db:PRIMARY>
虽然提示本身包含此信息,但您可以使用 rs.isMaster() 方法 手动检查哪个副本集成员是主副本集成员:
rs.isMaster()
您将看到如下输出,指示主节点的主机名:
Outputdb:PRIMARY> rs.isMaster() { "hosts" : [ "mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local:27017", "mongo-mongodb-replicaset-1.mongo-mongodb-replicaset.default.svc.cluster.local:27017", "mongo-mongodb-replicaset-2.mongo-mongodb-replicaset.default.svc.cluster.local:27017" ], ... "primary" : "mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local:27017", ...
接下来,切换到您的 sharkinfo
数据库:
use sharkinfo
Outputswitched to db sharkinfo
列出数据库中的集合:
show collections
Outputsharks
输出集合中的文档:
db.sharks.find()
您将看到以下输出:
Output{ "_id" : ObjectId("5cb7702c9111a5451c6dc8bb"), "name" : "Megalodon Shark", "character" : "Ancient", "__v" : 0 } { "_id" : ObjectId("5cb77054fcdbf563f3b47365"), "name" : "Whale Shark", "character" : "Large", "__v" : 0 }
退出 MongoDB Shell:
exit
现在我们已经检查了主节点上的数据,让我们检查它是否被复制到辅助节点。 使用以下命令将 kubectl exec
转换为 mongo-mongodb-replicaset-1
:
kubectl exec -it mongo-mongodb-replicaset-1 -- mongo -u your_database_username -p --authenticationDatabase admin
进入管理 shell 后,我们将需要使用 db.setSlaveOk()
方法来允许从辅助实例进行读取操作:
db.setSlaveOk(1)
切换到sharkinfo
数据库:
use sharkinfo
Outputswitched to db sharkinfo
允许对 sharks
集合中的文档进行读取操作:
db.setSlaveOk(1)
输出集合中的文档:
db.sharks.find()
您现在应该会看到在主实例上运行此方法时看到的相同信息:
Outputdb:SECONDARY> db.sharks.find() { "_id" : ObjectId("5cb7702c9111a5451c6dc8bb"), "name" : "Megalodon Shark", "character" : "Ancient", "__v" : 0 } { "_id" : ObjectId("5cb77054fcdbf563f3b47365"), "name" : "Whale Shark", "character" : "Large", "__v" : 0 }
此输出确认您的应用程序数据正在副本集的成员之间复制。
结论
您现在已经使用 Helm 图表在 Kubernetes 集群上部署了一个复制的、高可用性的鲨鱼信息应用程序。 当您为应用程序构建自定义图表并利用 Helm 的 stable 存储库和 其他图表存储库 时,此演示应用程序和本教程中概述的工作流可以作为起点。
当您转向生产时,请考虑实施以下操作:
- 集中记录和监控。 请参阅 Kubernetes 的现代化应用程序 中的 相关讨论 以获得一般概述。 您还可以查看 如何在 Kubernetes 上设置 Elasticsearch、Fluentd 和 Kibana (EFK) 日志记录堆栈 以了解如何使用 Elasticsearch、Fluentd 设置日志记录堆栈 和 Kibana。 另请查看 An Introduction to Service Meshes 以了解有关 Istio 等服务网格如何实现此功能的信息。
- 将流量路由到集群的入口资源。 如果您正在运行多个服务,每个服务都需要自己的 LoadBalancer,或者您希望实现应用程序级路由策略(例如 A/B 和金丝雀测试),这是 LoadBalancer 的一个很好的替代方案。 更多信息请查看How to Set Up an Nginx Ingress with Cert-Manager on DigitalOcean Kubernetes和An Introduction to Service Mesh context中路由的相关讨论服务网格。
- Kubernetes 对象的备份策略。 有关使用 DigitalOcean 的 Kubernetes 产品使用 Velero(以前的 Heptio Ark)实施备份的指南,请参阅 如何使用 Heptio Ark 在 DigitalOcean 上备份和恢复 Kubernetes 集群。
要了解有关 Helm 的更多信息,请参阅 Helm 简介、Kubernetes 包管理器 、如何使用 Helm 包管理器 在 Kubernetes 集群上安装软件和 Helm 文档。