<$>[note] 本文补充了关于部署和管理云中的集装箱工作负载的Webinar系列(https://go.digitalocean.com/containers-and-microservices-webinars-series)。该系列涵盖了集装箱的基本内容,包括管理集装箱生命周期,部署多集装箱应用程序,扩展工作负载,以及与Kubernetes合作。
本教程包括系列第五个会话中的概念和命令,在Kubernetes中部署和扩展微服务
[youtube LS57ul_cTsA 480 854 ]
介绍
Kubernetes是一个开源集装箱编排工具,用于管理集装箱应用程序. 在本系列的上一本教程中,您了解了Kubernetes的构建要素。
在本教程中,您将应用以前的教程的概念来构建,部署和管理Kubernetes中的端到端微服务应用程序。本教程中您将使用的样本Web应用程序是用Node.js编写的全部列表
应用程序,该应用程序使用MongoDB作为数据库。
您将从 Dockerfile 构建此应用程序的容器图像,将图像推到 Docker Hub,然后将其部署到您的集群中。
前提条件
要完成本教程,您将需要:
- 一个 Kubernetes 集群,您可以在本教程系列的第三部分中配置, Getting Started with Kubernetes。
- 一个活跃的 Docker Hub帐户来存储图像.
- Git 安装在您的本地机器上。
步骤 1 – 使用 Dockerfile 构建图像
我们将开始通过将Web应用程序包装成Docker图像。
首先,转到您的主目录,然后使用Git从GitHub上的官方存储库克隆本教程的样本Web应用程序。
1cd ~
2git clone https://github.com/janakiramm/todo-app.git
从 Dockerfile 构建容器图像. 使用 -t 交换机以注册表用户名、图像名和可选标签来标记图像。
1docker build -t sammy/todo .
输出证实图像已成功构建并适当标记。
1[secondary_label Output]
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-app:latest
通过运行 docker 图像命令来验证图像是否被创建。
1docker images
您可以看到图像的大小以及自创建以来的时间。
1[secondary_label Output]
2REPOSITORY TAG IMAGE ID CREATED SIZE
3sammy/todo-app latest 81f5f605d1ca 9 minutes ago 236MB
接下来,将您的图像推到 Docker Hub 上的公共注册表. 要做到这一点,请登录到您的 Docker Hub 帐户:
1docker login
一旦您提供身份证,请使用您的 Docker Hub 用户名标记您的图像:
1docker tag your_docker_hub_username/todo-app
然后将图像推到 Docker Hub:
1docker push
您可以通过在您的 Web 浏览器中搜索 Docker Hub来验证新图像是否可用。
随着Docker图像被推到注册表,让我们为Kubernetes包装应用程序。
步骤 2 – 在 Kubernetes 中部署 MongoDB Pod
该应用程序使用MongoDB存储通过Web应用程序创建的任务列表. 要在Kubernetes中运行MongoDB,我们需要将其包装为Pod。
创建一个名为 db-pod.yaml 的新的 YAML 文件:
1nano db-pod.yaml
添加以下代码,该代码定义了一个基于 MongoDB 的 Pod 容器。我们暴露了 27017
端口,这是 MongoDB 使用的标准端口. 请注意,该定义包含标签 名称
和 应用程序
。
1[label db-pod.yaml]
2apiVersion: v1
3kind: Pod
4metadata:
5 name: db
6 labels:
7 name: mongo
8 app: todoapp
9
10spec:
11 containers:
12 - image: mongo
13 name: mongo
14 ports:
15 - name: mongo
16 containerPort: 27017
17
18 volumeMounts:
19 - name: mongo-storage
20 mountPath: /data/db
21
22 volumes:
23 - name: mongo-storage
24 hostPath:
25 path: /data/db
数据存储在名为mongo-storage
的卷中,该卷被绘制到节点的/data/db
位置. 有关Volumes
的更多信息,请参阅官方的 Kubernetes volumes documentation。
运行以下命令来创建一个Pod。
1kubectl create -f db-pod.yml
你会看到这个输出:
1[secondary_label Output]
2pod "db" created
现在检查Pod的创建。
1kubectl get pods
输出显示了Pod并表示它正在运行:
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3db 1/1 Running 0 2m
让我们让这个Pod对集群的内部消费者可用。
创建一个名为「db-service.yaml」的新文件,其中包含定义 MongoDB 服务的此代码:
1[label db-service.yaml]
2apiVersion: v1
3kind: Service
4metadata:
5 name: db
6 labels:
7 name: mongo
8 app: todoapp
9
10spec:
11 selector:
12 name: mongo
13
14 type: ClusterIP
15 ports:
16 - name: db
17 port: 27017
18 targetPort: 27017
该服务发现了相同的名称空间中的所有 Pods,这些 Pods 与名称:db
的标签相匹配。
我们指出,服务可通过声明类型:ClusterIP
在集群内可见。
保存文件并离开编辑器,然后使用kubectl
将其提交到集群中。
1kubectl create -f db-service.yml
您将看到此输出,表示服务已成功创建:
1[secondary_label Output]
2service "db" created
让我们获得Pod可用的端口。
1kubectl get services
你会看到这个输出:
1[secondary_label Output]
2NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3db ClusterIP 10.109.114.243 <none> 27017/TCP 14s
4kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 47m
从这个输出中,你可以看到服务在端口 27017
上可用。网页应用程序可以通过该服务访问MongoDB。当它使用主机名 db
时,在Kubernetes内运行的DNS服务将解决与服务相关的ClusterIP地址。
有了数据库 Pod 和服务,让我们为 Web 应用程序创建一个 Pod。
步骤 3 – 部署 Node.JS Web App 作为一个 Pod
让我们将您在本教程的第一步创建的Docker图像包装为Pod,并部署到集群中。
创建一个新的 YAML 文件,名为 web-pod.yaml
:
1nano web-pod.yaml
添加以下代码,该代码定义了基于sammy/todo-app
Docker 图像的一个容器的 Pod。
1[label web-pod.yaml]
2apiVersion: v1
3kind: Pod
4
5metadata:
6 name: web
7 labels:
8 name: web
9 app: todoapp
10
11spec:
12 containers:
13 - image: sammy/todo-app
14 name: myweb
15 ports:
16 - containerPort: 3000
请注意,该定义包含标签名称
和应用程序
。
运行以下命令来创建 Pod:
1kubectl create -f web-pod.yaml
1[secondary_label Output]
2pod "web" created
让我们来验证Pod的创建:
1kubectl get pods
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3db 1/1 Running 0 8m
4web 1/1 Running 0 9s
请注意,我们有 MongoDB 数据库和 Web 应用程序作为 Pods 运行。
现在,我们将使Web
Pod可供公共互联网使用。
服务内部或外部暴露了一组Pod。让我们定义一个服务,使Web
Pod公开可用。我们将通过NodePort暴露它,这是一种系统,使Pod通过在集群的每个节点上打开的任意端口可访问。
创建一个名为web-service.yaml
的新文件,其中包含该应用程序的服务定义代码:
1apiVersion: v1
2kind: Service
3metadata:
4 name: web
5 labels:
6 name: web
7 app: todoapp
8
9spec:
10 selector:
11 name: web
12 type: NodePort
13 ports:
14 - name: http
15 port: 3000
16 targetPort: 3000
17 protocol: TCP
该服务在同一个名称空间中发现所有匹配标签名为web
的 Pods. YAML 文件的选择器部分明确定义了这一关联。
我们通过类型:NodePort
声明来指定服务类型为NodePort
。
使用kubectl
将此提交到集群中。
1kubectl create -f web-service.yml
您将看到此输出,表示服务已成功创建:
1[secondary_label Output]
2service "web" created
让我们获得Pod可用的端口。
1kubectl get services
1[secondary_label Output]
2NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
3db ClusterIP 10.109.114.243 <none> 27017/TCP 12m
4kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 59m
5web NodePort 10.107.206.92 <none> 3000:30770/TCP 12s
从这个输出中,我们可以看到服务在端口30770
上可用,让我们尝试连接到一个工人节点。
通过使用 DigitalOcean 控制台获取与您的 Kubernetes 集群关联的 Worker 节点之一的公共 IP 地址。
一旦您获得了IP地址,请使用弯曲
命令将HTTP请求发送到端口30770
上的一个节点:
1curl http://your_worker_ip_address:30770
你会看到类似于此的输出:
1[secondary_label Output]
2<!DOCTYPE html>
3<html>
4 <head>
5 <title>Containers Todo Example</title>
6 <link rel='stylesheet' href='/stylesheets/screen.css' />
7 <!--[if lt IE 9]>
8 <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
9 <![endif]-->
10 </head>
11 <body>
12 <div id="layout">
13<h1 id="page-title">Containers Todo Example</h1>
14<div id="list">
15 <form action="/create" method="post" accept-charset="utf-8">
16 <div class="item-new">
17 <input class="input" type="text" name="content" />
18 </div>
19 </form>
20</div>
21 <div id="layout-footer"></div>
22 </div>
23 <script src="/javascripts/ga.js"></script>
24 </body>
25</html>
您已经定义了 Web Pod 和一个服务,现在让我们看看使用 Replica Sets 来扩展它。
步骤5 – 扩展 Web 应用程序
复制集确保在集群中始终运行最少数量的Pod。当一个Pod被包装为复制集时,Kubernetes 将始终运行规格中定义的最少数量的Pod。
让我们删除当前的Pod,并通过复制集重新创建两个Pod。如果我们离开了运行的Pod,它将不会是复制集的一部分。
首先,删除现有的Pod。
1kubectl delete pod web
1[secondary_label Output]
2pod "web" deleted
现在创建一个新的 replika set 声明. replika set 的定义与pod 相同. 关键的区别是它包含了 replika
元素,该元素定义了需要运行的pods 的数量。
创建web-rs.yaml
文件,并将此代码添加到文件中:
1apiVersion: extensions/v1beta1
2kind: ReplicaSet
3metadata:
4 name: web
5 labels:
6 name: web
7 app: todoapp
8
9spec:
10 replicas: 2
11 template:
12 metadata:
13 labels:
14 name: web
15 spec:
16 containers:
17 - name: web
18 image: sammy/todo-app
19 ports:
20 - containerPort: 3000
保存并关闭文件。
现在创建复制套件:
1kubectl create -f web-rs.yaml
1[secondary_label Output]
2replicaset "web" created
然后检查Pods的数量:
1kubectl get pods
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3db 1/1 Running 0 18m
4web-n5l5h 1/1 Running 0 25s
5web-wh6nf 1/1 Running 0 25s
当我们通过 NodePort 访问服务时,请求将发送到由 Replica Set 管理的 Pods 之一。
让我们通过删除其中一个Pod来测试复制集的功能,看看会发生什么:
1kubectl delete pod web-wh6nf
1[secondary_label Output]
2pod "web-wh6nf" deleted
再来看看子:
1kubectl get pods
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3db 1/1 Running 0 19m
4web-n5l5h 1/1 Running 0 1m
5web-wh6nf 1/1 Terminating 0 1m
6web-ws59m 0/1 ContainerCreating 0 2s
一旦Pod被删除,Kubernetes就创建了另一个,以确保所需的计数保持。
我们可以扩展 replika set 以运行额外的 web Pods。
运行以下命令将 Web 应用程序扩展到 10 个 Pods。
1kubectl scale rs/web --replicas=10
1[secondary_label Output]
2replicaset "web" scaled
点击查看 Pod count:
1kubectl get pods
你会看到这个输出:
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3db 1/1 Running 0 22m
4web-4nh4g 1/1 Running 0 21s
5web-7vbb5 1/1 Running 0 21s
6web-8zd55 1/1 Running 0 21s
7web-f8hvq 0/1 ContainerCreating 0 21s
8web-ffrt6 1/1 Running 0 21s
9web-k6zv7 0/1 ContainerCreating 0 21s
10web-n5l5h 1/1 Running 0 3m
11web-qmdxn 1/1 Running 0 21s
12web-vc45m 1/1 Running 0 21s
13web-ws59m 1/1 Running 0 2m
Kubernetes已经启动了扩展Web
Pod的过程,当请求通过NodePort到达服务时,它会被路由到复制集中的一个Pod。
当流量和负载下降时,我们可以回到两个Pod的原始配置。
1kubectl scale rs/web --replicas=2
1[secondary_label Output]
2replicaset "web" scaled
此命令會終止除兩個外的所有 Pods。
1kubectl get pods
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3db 1/1 Running 0 24m
4web-4nh4g 1/1 Terminating 0 2m
5web-7vbb5 1/1 Terminating 0 2m
6web-8zd55 1/1 Terminating 0 2m
7web-f8hvq 1/1 Terminating 0 2m
8web-ffrt6 1/1 Terminating 0 2m
9web-k6zv7 1/1 Terminating 0 2m
10web-n5l5h 1/1 Running 0 5m
11web-qmdxn 1/1 Terminating 0 2m
12web-vc45m 1/1 Terminating 0 2m
13web-ws59m 1/1 Running 0 4m
要验证复制件集的可用性,请尝试删除其中一个Pod,并检查计数。
1kubectl delete pod web-ws59m
1[secondary_label Output]
2pod "web-ws59m" deleted
1kubectl get pods
1[secondary_label Output]
2NAME READY STATUS RESTARTS AGE
3db 1/1 Running 0 25m
4web-n5l5h 1/1 Running 0 7m
5web-ws59m 1/1 Terminating 0 5m
6web-z6r2g 0/1 ContainerCreating 0 5s
只要 Pod 數量發生變化,Kubernetes 就會調整它以匹配 YAML 檔案中定義的數量。 當 Replica Set 中的 Web Pod 之一被刪除時,會立即創建另一個 Pod 以保持所需數量。
您可以使用以下命令删除本教程中创建的所有对象:
1kubectl delete -f db-pod.yaml -f db-service.yaml -f web-rs.yaml -f web-service.yaml
1[secondary_label Output]
2pod "db" deleted
3service "db" deleted
4replicaset "web" deleted
5service "web" deleted
结论
在本教程中,您将系列中涵盖的所有概念应用于包装,部署和扩展微服务应用程序。
在本系列的下一部分中,您将学习如何通过运行它作为StatefulSet来使MongoDB高度可用。