本文补充了关于在云中部署和管理容器化工作负载的网络研讨会系列 。 该系列涵盖容器的基本要素,包括管理容器生命周期,部署多容器应用程序,扩展工作负载以及使用Kubernetes。 它还突出显示了运行有状态应用程序的最佳实践。
本教程包括本系列第五部分中的概念和命令,即在Kubernetes中部署和缩放微服务。
介绍
Kubernetes是一款用于管理容器化应用程序的开源容器编排工具。 在本系列的前一篇教程中,您深入了解Kubernetes ,了解了Kubernetes的构建模块。
在本教程中,您将应用前面教程中的概念来构建,部署和管理Kubernetes中的端到端微服务应用程序。 您将在本教程中使用的示例Web应用程序是一个使用MongoDB作为数据库的Node.js中编写的“待办事项列表”应用程序。 这是我们在教程Building Containerized Applications中使用的同一个应用程序 。
您将从Dockerfile为此应用程序构建一个容器映像,将映像推送到Docker Hub,然后将其部署到群集中。 然后,您将扩展应用程序以满足更高的需求。
先决条件
要完成本教程,您需要:
Kubernetes集群,您可以在本教程系列的第三部分“ Kubernetes入门”中对其进行配置 。
活动的Docker Hub帐户来存储图像。
安装在本地机器上的Git。 您可以按照教程贡献开源:Git入门在您的计算机上安装和设置Git。
第1步 - 使用Dockerfile构建映像
我们将通过将Web应用程序打包到Docker镜像中进行容器化。
首先转到您的主目录,然后使用Git从GitHub上的官方存储库克隆本教程的示例Web应用程序。
cd ~
git clone https://github.com/janakiramm/todo-app.git
从Dockerfile构建容器镜像。 使用-t开关为注册表用户名,图像名称和可选标记标记图像。
docker build -t sammy/todo .
输出确认图像已成功构建并进行了适当标记。
OutputSending build context to Docker daemon 8.238MB
Step 1/7 : FROM node:slim
---> 286b1e0e7d3f
Step 2/7 : LABEL maintainer = "jani@janakiram.com"
---> Using cache
---> ab0e049cf6f8
Step 3/7 : RUN mkdir -p /usr/src/app
---> Using cache
---> 897176832f4d
Step 4/7 : WORKDIR /usr/src/app
---> Using cache
---> 3670f0147bed
Step 5/7 : COPY ./app/ ./
---> Using cache
---> e28c7c1be1a0
Step 6/7 : RUN npm install
---> Using cache
---> 7ce5b1d0aa65
Step 7/7 : CMD node app.js
---> Using cache
---> 2cef2238de24
Successfully built 2cef2238de24
Successfully tagged sammy/todo-app:latest
通过运行docker images命令验证映像是否已创建。
docker images
您可以看到图像的大小以及自创建以来的时间。
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
sammy/todo-app latest 81f5f605d1ca 9 minutes ago 236MB
接下来,将您的映像推送到Docker Hub上的公共注册表中。 为此,请登录您的Docker Hub帐户:
docker login
一旦您提供了您的凭证,请使用Docker Hub用户名标记您的图像:
docker tag your_docker_hub_username/todo-app
然后将您的图片推送到Docker Hub:
docker push
您可以通过在Web浏览器中搜索Docker Hub来验证新图像是否可用。
将Docker映像推送到注册表中,让我们为Kubernetes打包应用程序。
第2步 - 在Kubernetes中部署MongoDB Pod
应用程序使用MongoDB来存储通过Web应用程序创建的待办事项列表。 要在Kubernetes中运行MongoDB,我们需要将它打包为Pod。 当我们启动这个Pod时,它会运行一个MongoDB实例。
创建一个名为db-pod.yaml的新YAML文件:
nano db-pod.yaml
添加下面的代码,它定义了一个基于MongoDB的容器。 我们公开端口27017
,这是MongoDB使用的标准端口。 请注意,该定义包含标签name
和app
。 我们将使用这些标签来识别和配置特定的Pod。
apiVersion: v1
kind: Pod
metadata:
name: db
labels:
name: mongo
app: todoapp
spec:
containers:
- image: mongo
name: mongo
ports:
- name: mongo
containerPort: 27017
volumeMounts:
- name: mongo-storage
mountPath: /data/db
volumes:
- name: mongo-storage
hostPath:
path: /data/db
数据存储在名为mongo-storage
的卷中,映射到该节点的/data/db
位置。 有关卷的更多信息,请参阅官方Kubernetes卷文档 。
运行以下命令来创建Pod。
kubectl create -f db-pod.yml
你会看到这个输出:
Outputpod "db" created
现在验证Pod的创建。
kubectl get pods
输出显示Pod并指示它正在运行:
OutputNAME READY STATUS RESTARTS AGE
db 1/1 Running 0 2m
让我们使该Pod可供群集的内部消费者访问。
创建一个名为db-service.yaml
的新文件,其中包含定义了MongoDB Service的代码:
apiVersion: v1
kind: Service
metadata:
name: db
labels:
name: mongo
app: todoapp
spec:
selector:
name: mongo
type: ClusterIP
ports:
- name: db
port: 27017
targetPort: 27017
该服务发现与name: db
的标签相同的命名空间中的所有豆荚。 YAML文件的selector
部分明确定义了这种关联。
我们通过声明type: ClusterIP
指定服务在集群内可见type: ClusterIP
。
保存文件并退出编辑器。 然后使用kubectl
将其提交给群集。
kubectl create -f db-service.yml
你会看到这个输出表明服务已成功创建:
Outputservice "db" created
让我们来获取Pod可用的端口。
kubectl get services
你会看到这个输出:
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db ClusterIP 10.109.114.243 <none> 27017/TCP 14s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 47m
从这个输出中,您可以看到服务在端口27017
上可用。 该Web应用程序可以通过此服务访问MongoDB。 当它使用主机名db
,Kubernetes内运行的DNS服务会将地址解析到与该服务关联的ClusterIP。 这种机制允许Pod发现并相互通信。
使用数据库Pod和Service,我们为Web应用程序创建一个Pod。
第3步 - 将Node.JS Web App部署为Pod
让我们将您在本教程的第一步中创建的Docker镜像打包为Pod并将其部署到群集。 这将作为最终用户可访问的前端Web应用程序层。
创建一个名为web-pod.yaml
的新YAML文件:
nano web-pod.yaml
添加下面的代码,该代码基于sammy/todo-app
Docker镜像定义了一个容器。 它通过TCP协议暴露在端口3000
上。
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
name: web
app: todoapp
spec:
containers:
- image: sammy/todo-app
name: myweb
ports:
- containerPort: 3000
请注意,该定义包含标签name
和app
。 服务将使用这些标签将入站流量路由到适当的端口。
运行以下命令来创建Pod:
kubectl create -f web-pod.yaml
Outputpod "web" created
我们来验证Pod的创建:
kubectl get pods
OutputNAME READY STATUS RESTARTS AGE
db 1/1 Running 0 8m
web 1/1 Running 0 9s
请注意,我们将MongoDB数据库和Web应用程序都作为Pods运行。
现在我们将使web
Pod可供公共互联网访问。
服务在内部或外部公开一组Pod。 让我们定义一个使web
Pod公开可用的服务。 我们将通过一个NodePort来公开它,它是一个通过在集群的每个节点上打开的任意端口访问Pod的方案。
创建一个名为web-service.yaml
的新文件,其中包含为应用程序定义Service的代码:
apiVersion: v1
kind: Service
metadata:
name: web
labels:
name: web
app: todoapp
spec:
selector:
name: web
type: NodePort
ports:
- name: http
port: 3000
targetPort: 3000
protocol: TCP
该服务发现相同名称空间中与名称web
匹配的标签的所有窗格。 YAML文件的选择器部分明确定义了这种关联。
我们通过type: NodePort
声明来指定服务的类型为NodePort
。
使用kubectl
将其提交给群集。
kubectl create -f web-service.yml
你会看到这个输出表明服务已成功创建:
Outputservice "web" created
让我们来获取Pod可用的端口。
kubectl get services
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db ClusterIP 10.109.114.243 <none> 27017/TCP 12m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 59m
web NodePort 10.107.206.92 <none> 3000:30770/TCP 12s
从这个输出中,我们看到该服务在端口30770
上30770
。 我们尝试连接到其中一个工作节点。
通过使用DigitalOcean控制台获取与您的Kubernetes集群关联的其中一个工作节点的公共IP地址。
获取IP地址后,使用curl
命令向端口30770
上的其中一个节点发出HTTP请求:
curl http://your_worker_ip_address:30770
你会看到类似这样的输出:
Output<!DOCTYPE html>
<html>
<head>
<title>Containers Todo Example</title>
<link rel='stylesheet' href='/stylesheets/screen.css' />
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div id="layout">
<h1 id="page-title">Containers Todo Example</h1>
<div id="list">
<form action="/create" method="post" accept-charset="utf-8">
<div class="item-new">
<input class="input" type="text" name="content" />
</div>
</form>
</div>
<div id="layout-footer"></div>
</div>
<script src="/javascripts/ga.js"></script>
</body>
</html>
您已经定义了Web Pod和一项服务。 现在让我们看看如何使用副本集对其进行缩放。
第5步 - 扩展Web应用程序
副本集确保始终在群集中运行最小数量的Pod。 当Pod被打包为副本集时,Kubernetes将始终运行规范中定义的最小Pod数。
让我们删除当前的Pod并通过副本集重新创建两个Pod。 如果我们让Pod运行,它不会成为Replica Set的一部分。 因此,即使计数只有一个,通过副本集启动Pod也是一个好主意。
首先,删除现有的Pod。
kubectl delete pod web
Outputpod "web" deleted
现在创建一个新的副本集声明。 副本集的定义与Pod相同。 关键区别在于它包含定义需要运行的Pod数量的replica
元素。 与Pod一样,它还包含标签作为帮助进行服务发现的元数据。
创建文件web-rs.yaml
并将此代码添加到文件中:
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: web
labels:
name: web
app: todoapp
spec:
replicas: 2
template:
metadata:
labels:
name: web
spec:
containers:
- name: web
image: sammy/todo-app
ports:
- containerPort: 3000
保存并关闭文件。
现在创建副本集:
kubectl create -f web-rs.yaml
Outputreplicaset "web" created
然后检查豆荚的数量:
kubectl get pods
OutputNAME READY STATUS RESTARTS AGE
db 1/1 Running 0 18m
web-n5l5h 1/1 Running 0 25s
web-wh6nf 1/1 Running 0 25s
当我们通过NodePort访问服务时,请求将被发送到由副本集管理的一个Pod中。
让我们通过删除其中一个Pod来测试副本集的功能,并查看会发生什么情况:
kubectl delete pod web-wh6nf
Outputpod "web-wh6nf" deleted
再看看豆荚:
kubectl get pods
OutputNAME READY STATUS RESTARTS AGE
db 1/1 Running 0 19m
web-n5l5h 1/1 Running 0 1m
web-wh6nf 1/1 Terminating 0 1m
web-ws59m 0/1 ContainerCreating 0 2s
一旦Pod被删除,Kubernetes创建了另一个来确保所需计数得以维持。
我们可以扩展副本集以运行其他Web Pod。
运行以下命令将Web应用程序缩放到10个Pod。
kubectl scale rs/web --replicas=10
Outputreplicaset "web" scaled
检查Pod计数:
kubectl get pods
你会看到这个输出:
OutputNAME READY STATUS RESTARTS AGE
db 1/1 Running 0 22m
web-4nh4g 1/1 Running 0 21s
web-7vbb5 1/1 Running 0 21s
web-8zd55 1/1 Running 0 21s
web-f8hvq 0/1 ContainerCreating 0 21s
web-ffrt6 1/1 Running 0 21s
web-k6zv7 0/1 ContainerCreating 0 21s
web-n5l5h 1/1 Running 0 3m
web-qmdxn 1/1 Running 0 21s
web-vc45m 1/1 Running 0 21s
web-ws59m 1/1 Running 0 2m
Kubernetes已经启动了web
Pod的扩展过程。 当请求通过NodePort进入服务时,它将被路由到副本集中的其中一个Pod。
当流量和负载平息时,我们可以恢复到两个Pod的原始配置。
kubectl scale rs/web --replicas=2
Outputreplicaset "web" scaled
该命令终止除两个之外的所有Pod。
kubectl get pods
OutputNAME READY STATUS RESTARTS AGE
db 1/1 Running 0 24m
web-4nh4g 1/1 Terminating 0 2m
web-7vbb5 1/1 Terminating 0 2m
web-8zd55 1/1 Terminating 0 2m
web-f8hvq 1/1 Terminating 0 2m
web-ffrt6 1/1 Terminating 0 2m
web-k6zv7 1/1 Terminating 0 2m
web-n5l5h 1/1 Running 0 5m
web-qmdxn 1/1 Terminating 0 2m
web-vc45m 1/1 Terminating 0 2m
web-ws59m 1/1 Running 0 4m
要验证副本集的可用性,请尝试删除其中一个副本并检查计数。
kubectl delete pod web-ws59m
Outputpod "web-ws59m" deleted
kubectl get pods
OutputNAME READY STATUS RESTARTS AGE
db 1/1 Running 0 25m
web-n5l5h 1/1 Running 0 7m
web-ws59m 1/1 Terminating 0 5m
web-z6r2g 0/1 ContainerCreating 0 5s
一旦Pod计数发生变化,Kubernetes就会调整它以匹配YAML文件中定义的计数。 当删除副本集中的某个网络Pod时,会立即创建另一个Pod来维护所需的计数。 通过确保最小数量的Pod一直在运行,从而确保应用程序的高可用性。
您可以使用以下命令删除在本教程中创建的所有对象:
kubectl delete -f db-pod.yaml -f db-service.yaml -f web-rs.yaml -f web-service.yaml
Outputpod "db" deleted
service "db" deleted
replicaset "web" deleted
service "web" deleted
结论
在本教程中,您应用了本系列中涵盖的所有概念来打包,部署和扩展微服务应用程序。
在本系列的下一部分中,您将了解如何通过将它作为StatefulSet运行来使MongoDB高度可用。