网络研讨会系列:在Kubernetes中部署和扩展微服务

在本教程中,您将应用前面教程中的概念来构建,部署和管理Kubernetes中的端到端微服务应用程序。您将在本教程中使用的示例Web应用程序是一个使用MongoDB作为数据库的Node.js中编写的“todo list”应用程序。您将从Dockerfile为此应用程序构建一个容器映像,并将映像推送到Docker Hub,然后将其部署到群集中。然后,您将扩展应用程序以满足更高的需求。

本文补充了关于在云中部署和管理容器化工作负载网络研讨会系列 该系列涵盖容器的基本要素,包括管理容器生命周期,部署多容器应用程序,扩展工作负载以及使用Kubernetes。 它还突出显示了运行有状态应用程序的最佳实践。

本教程包括本系列第五部分中的概念和命令,即在Kubernetes中部署和缩放微服务。

介绍

Kubernetes是一款用于管理容器化应用程序的开源容器编排工具。 在本系列的前一篇教程中,您深入了解Kubernetes ,了解了Kubernetes的构建模块。

在本教程中,您将应用前面教程中的概念来构建,部署和管理Kubernetes中的端到端微服务应用程序。 您将在本教程中使用的示例Web应用程序是一个使用MongoDB作为数据库的Node.js中编写的“待办事项列表”应用程序。 这是我们在教程Building Containerized Applications中使用的同一个应用程序

您将从Dockerfile为此应用程序构建一个容器映像,将映像推送到Docker Hub,然后将其部署到群集中。 然后,您将扩展应用程序以满足更高的需求。

先决条件

要完成本教程,您需要:

第1步 - 使用Dockerfile构建映像

我们将通过将Web应用程序打包到Docker镜像中进行容器化。

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

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

从Dockerfile构建容器镜像。 使用-t开关为注册表用户名,图像名称和可选标记标记图像。

docker build -t sammy/todo .

输出确认图像已成功构建并进行了适当标记。

Sending 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

您可以看到图像的大小以及自创建以来的时间。

REPOSITORY                                       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使用的标准端口。 请注意,该定义包含标签nameapp 我们将使用这些标签来识别和配置特定的Pod。

DB-pod.yaml
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

你会看到这个输出:

pod "db" created

现在验证Pod的创建。

kubectl get pods

输出显示Pod并指示它正在运行:

NAME      READY     STATUS    RESTARTS   AGE
db   1/1       Running   0          2m

让我们使该Pod可供群集的内部消费者访问。

创建一个名为db-service.yaml的新文件,其中包含定义了MongoDB Service的代码:

DB-service.yaml
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

你会看到这个输出表明服务已成功创建:

service "db" created

让我们来获取Pod可用的端口。

kubectl get services

你会看到这个输出:

NAME         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上。

网络pod.yaml
apiVersion: v1
kind: Pod

metadata:
  name: web
  labels:
    name: web
    app: todoapp

spec:
  containers:
    - image: sammy/todo-app
      name: myweb
      ports:
        - containerPort: 3000

请注意,该定义包含标签nameapp 服务将使用这些标签将入站流量路由到适当的端口。

运行以下命令来创建Pod:

kubectl create -f web-pod.yaml
pod "web" created

我们来验证Pod的创建:

kubectl get pods
NAME      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

你会看到这个输出表明服务已成功创建:

service "web" created

让我们来获取Pod可用的端口。

kubectl get services
NAME         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

从这个输出中,我们看到该服务在端口3077030770 我们尝试连接到其中一个工作节点。

通过使用DigitalOcean控制台获取与您的Kubernetes集群关联的其中一个工作节点的公共IP地址。

DigitalOcean控制台显示工作节点

获取IP地址后,使用curl命令向端口30770上的其中一个节点发出HTTP请求:

curl http://your_worker_ip_address:30770

你会看到类似这样的输出:

<!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
pod "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
replicaset "web" created

然后检查豆荚的数量:

kubectl get pods
NAME        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
pod "web-wh6nf" deleted

再看看豆荚:

kubectl get pods
NAME        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
replicaset "web" scaled

检查Pod计数:

kubectl get pods

你会看到这个输出:

NAME        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
replicaset "web" scaled

该命令终止除两个之外的所有Pod。

kubectl get pods
NAME        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
pod "web-ws59m" deleted

kubectl get pods
NAME        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
pod "db" deleted
service "db" deleted
replicaset "web" deleted
service "web" deleted

结论

在本教程中,您应用了本系列中涵盖的所有概念来打包,部署和扩展微服务应用程序。

在本系列的下一部分中,您将了解如何通过将它作为StatefulSet运行来使MongoDB高度可用。


分享按钮