容器实例服务 (Container Instance Service , CIS) 可以帮您在云上快捷, 灵活的部署容器, 让您专注于构建程序和使用容器而非管理设备上. 无需预购 CVM, 您就可以在几秒内启动一批容器来执行任务. 您也可以通过 kubernetes API 把已有 kubernetes 集群的 pod 调度到 CIS 上以处理突增业务. CIS 根据您实际使用的资源计费, 可以帮您节约计算成本. 使用 CIS 可以极大降低您部署容器的门槛, 降低您执行 batch 型任务或处理业务突增的成本.
本文将介绍 Kubernetes 部署和容器工作负载的相关内容. 包含管理容器生命周期, 部署多容器应用程序, 扩展工作负载以及与 Kubernetes 进行协同工作. 本文包括一些概念和命令, 教大家快速入门 Kubernetes, 并入门 CIS.
介绍
Kubernetes https://kubernetes.io/ 是一个用于管理容器化应用程序的开源容器资源编排工具.
本文中, 您将应用一些容器化的概念来构建, 部署和管理 Kubernetes 中端到端的微服务应用程序. 本文中使用的示例 web 应用程序是一个用 Node.js 编写的 "待办事项列表" 应用程序, 它使用 MongoDB 作为数据库.
本次将从 Dockerfile 中为此应用程序构建容器镜像, 将镜像推送到 Docker Hub, 然后部署到您的集群. 以便在未来您将扩展应用程序以满足不断增长的需求.
准备
要完成本文, 您需要:
Kubernetes 集群, 您可以参考腾讯云开发者实验室 https://cloud.tencent.com/developer/labs 产品进行入门.
用于存储镜像的 Docker Hub https://hub.docker.com/ 帐户.
在本地机器安装 docker, 您也可以参考腾讯云开发者实验室 https://cloud.tencent.com/developer/labs 产品进行入门.
第一步, 使用 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
您可以登录 Docker Hub https://hub.docker.com/ 官网搜索查看你的镜像, 来验证新镜像是否可用.
将 Docker 镜像推送到 Docker Hub https://hub.docker.com/ 后, 接下来我们可以将应用程序打包为 Kubernetes.
第二步, 在 Kubernetes 中部署 MongoDB Pod
这个应用程序使用 MongoDB 存储通过 Web 应用程序创建的待办事项列表. 要在 Kubernetes 中运行 MongoDB, 我们需要将其打包为 Pod. 当我们启动这个 Pod 时, 它将运行一个 MongoDB 实例.
创建一个名为 db-pod.yaml 的新 YAML 文件:
nano db-pod.yaml
添加以下代码, 该代码使用基于 MongoDB 的一个容器定义 Pod. 同时, 我们打开了 MongoDB 使用的标准端口 port. 请注意, 定义包含名为 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
数据存储在调用的卷中, 该卷映射到节点的位置. 有关卷的更多信息, 请参阅 Kubernetes 官方文档 https://kubernetes.io/docs/concepts/storage/volumes .
运行以下命令以创建 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 服务的代码:
- 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 标签相匹配的同一命名空间中的所有 Pod .YAML 文件中的 selector 部分明确地定义了这种关联关系.
我们通过声明 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 服务将解析与服务关联的 IP 的地址. 这种机制允许 Pod 之间相互检测并通信.
接下来我们可以使用数据库 Pod 和 Service, 为 Web 应用程序创建一个额外的 Pod.
第三步, 将 Node.JS Web App 部署为 Pod
我们将在本文第一步中创建的 Docker 镜像打包为 Pod 并将其部署到集群. 这将被作为最终用户可访问的前端 Web 应用程序层.
创建一个名为的新 YAML 文件: web-pod.yaml
nano web-pod.yaml
添加以下代码, 该代码根据 sammy/todo-app 的 Docker 镜像定义具有一个容器的 Pod . 它通过 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
运行以下命令以创建 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 应用程序都作为 Pod 运行.
现在, 我们将使 web Pod 可以访问互联网.
服务会在内部或外部公开一组 Pod. 让我们定义一个使 web Pod 可以公开使用的服务. 我们将通过 NodePort 公开它. NodePort 是一种通过在集群的每个节点上打开任意端口用来访问 Pod 的方案.
创建一个名为 web-service.yaml 的新文件, 其中包含定义应用服务的代码:
- 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
服务发现同一名称空间中与 Label 匹配的所有 Pod 都具有名称 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
从此输出中, 我们看到该服务在端口 30770 上可用. 让我们尝试连接到其中一个工作节点.
使用腾讯云控制台获取与你服务器的 IP 地址:
获得 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 运行, 它将不会是副本集的一部分. 因此, 我们最好通过副本集启动 Pod, 即使只有一个 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
然后检查 Pod 的数量:
- 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
让我们再来看一下 Pods:
- 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 会创建另一个 Pod 副本, 以确保其能够维持所需的计数.
我们可以扩展副本集以运行其他的 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
要验证副本集的可用性, 请尝试删除其中一个 Pod 并检查计数.
- 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 文件中定义的计数. 删除副本集中的一个 Web 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
本文就先写到这里, 欢迎大家使用腾讯云 CIS 产品, 产品链接: https://cloud.tencent.com/document/product/858
参考文献:Webinar Series: Deploying and Scaling Microservices in Kubernetes
翻译: Zach 展, 审校: Techeek
来源: https://www.qcloud.com/developer/article/1158254