一、YAML 文件基本语法格式
前面我们得 Kubernetes 集群已经搭建成功了,现在我们就可以在集群里面来跑我们的应用了。要在集群里面运行我们自己的应用,首先我们需要知道几个概念。
第一个当然就是应用的镜像,因为我们在集群中运行的是容器,所以首先需要将我们的应用打包成镜像,前面的课程中我们已经学习过如何将应用打包成镜像,这里就不再赘述了。
镜像准备好了,Kubernetes 集群也准备好了,其实我们就可以把我们的应用部署到集群中了。但是镜像到集群中运行这个过程如何完成呢?必然有一个地方可以来描述我们的应用,然后把这份描述告诉集群,然后集群按照这个描述来部署应用。
在之前 Docker 环境下面我们是直接通过命令 docker run 来运行我们的应用的,在 Kubernetes 环境下面我们同样也可以用类似 kubectl run 这样的命令来运行我们的应用,但是在 Kubernetes 中却是不推荐使用命令行的方式,而是希望使用我们称为资源清单的东西来描述应用,资源清单可以用 YAML 或者 JSON 文件来编写,一般来说 YAML 文件更方便阅读和理解,所以我们的课程中都会使用 YAML 文件来进行描述。
通过一个资源清单文件来定义好一个应用后,我们就可以通过 kubectl 工具来直接运行它:
$ kubectl create -f xxxx.yaml
我们知道 kubectl 是直接操作 APIServer 的,所以就相当于把我们的清单提交给了 APIServer,然后集群获取到清单描述的应用信息后存入到 etcd 数据库中,然后 kube-scheduler 组件发现这个时候有一个 Pod 还没有绑定到节点上,就会对这个 Pod 进行一系列的调度,把它调度到一个最合适的节点上,然后把这个节点和 Pod 绑定到一起(写回到 etcd),然后节点上的 kubelet 组件这个时候 watch 到有一个 Pod 被分配过来了,就去把这个 Pod 的信息拉取下来,然后根据描述通过容器运行时把容器创建出来,最后当然同样把 Pod 状态再写回到 etcd 中去,这样就完成了一整个的创建流程。
二、第一个容器化应用
比如现在我们通过 YAML 文件编写了一个如下的资源清单,命名为 nginx-deployment.yaml:
apiVersion: apps/v1 # API版本
kind: Deployment # API对象类型
metadata:
name: nginx-deploy
labels:
chapter: first-app
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # Pod 副本数量
template: # Pod 模板
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
然后直接用 kubectl 命令来创建这个应用:
$ kubectl create -f nginx-deployment.yaml
deployment.apps/nginx-deploy created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deploy-54f57cf6bf-2fdjz 1/1 Running 0 7s
nginx-deploy-54f57cf6bf-57287 1/1 Running 0 7s
我们可以看到会在集群中生成两个 Pod 出来。而整个资源清单文件对应到 Kubernetes 中就是一个 API Object(API 对象),我们按照这些对象的要求填充上对应的属性后,提交给 Kubernetes 集群,就可以为我们创建出对应的资源对象,比如我们这里定义的是一个 Deployment 类型的 API 对象,我们按照这个 API 对象的要求填充了一些属性,就会为我们创建出对应的资源对象,我们可以通过下面的命令来获取:
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 2/2 2 2 12m
Deployment 这个资源对象就是用来定义多副本应用的对象,而且还支持对每个副本进行滚动更新,上面我们的资源清单中的描述中有一个属性 replicas: 2,所以最后生成两个副本的 Pod。
而这个 Deployment 定义的副本 Pod 具体是什么样的,是通过下面的 Pod 模板来定义的,就是 template 下面的定义,这个模板中定义了我们的 Pod 中只有一个名为 nginx 的容器,容器使用的镜像是 nginx:1.7.9(spec.containers[0].image),并且这个容器监听的端口是 80(spec.containers[0].ports[0].containerPort),另外我们还为 Pod 添加了一个app: nginx这样的 Label 标签,这里需要非常注意的是上面的 selector.matchLabels 区域就是来表示我们的 Deployment 来管理哪些 Pod 的,所以这个地方需要和 Pod 模板中的 Label 标签保持一致,这个 Label 标签之前我们也提到过是非常重要的。
另外我们也可以发现每个 API 对象都有一个 Metadata 的字段,用来表示该对象的元数据的,比如定义 name、namespace 等,比如上面 Deployment 和 Pod 模板中都有这个字段,至于为什么 Pod 模板中没有 name 这个元信息呢,这是因为 Deployment 这个控制器会自动在他自己的 name 基础上生成 Pod 名,不过 Deployment 下面定义的 Label 标签就没有 Pod 中定义的 Label 标签那么重要了,只是起到一个对该对象标识和过滤的作用。比如我们在查询对象的时候可以带上标签来进行过滤:
$ kubectl get deployment -l chapter=first-app
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 2/2 2 2 51m
$ kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deploy-54f57cf6bf-2fdjz 1/1 Running 0 51m
nginx-deploy-54f57cf6bf-57287 1/1 Running 0 51m
到这里我们就完成了我们的第一个应用的容器化部署,但是往往我们在部署应用的过程中或多或少会遇到一些问题,这个时候我们可以使用一个 kubectl describe 命令来查看资源对象的详细信息,比如我们用下面的命令来查看 Pod 的详细信息:
$ kubectl describe pod nginx-deploy-54f57cf6bf-2fdjz
Name: nginx-deploy-54f57cf6bf-2fdjz
Namespace: default
Priority: 0
Node: ydzs-node2/10.151.30.23
Start Time: Sat, 09 Nov 2019 15:20:32 +0800
Labels: app=nginx
pod-template-hash=54f57cf6bf
Annotations: <none>
Status: Running
IP: 10.244.2.199
IPs:
IP: 10.244.2.199
Controlled By: ReplicaSet/nginx-deploy-54f57cf6bf
Containers:
nginx:
Container ID: docker://e40e78eee7a431b6e7277b414967cce936ac750e2a8ba30298302cdd89e54300
Image: nginx:1.7.9
Image ID: docker-pullable://nginx@sha256:e3456c851a152494c3e4ff5fcc26f240206abac0c9d794affb40e0714846c451
Port: 80/TCP
Host Port: 0/TCP
State: Running
Started: Sat, 09 Nov 2019 15:20:35 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-5tsh4 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-5tsh4:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-5tsh4
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/nginx-deploy-54f57cf6bf-2fdjz to ydzs-node2
Normal Pulled 52m kubelet, ydzs-node2 Container image "nginx:1.7.9" already present on machine
Normal Created 52m kubelet, ydzs-node2 Created container nginx
Normal Started 52m kubelet, ydzs-node2 Started container nginx
我们可以看到看到很多这个 Pod 的详细信息,比如调度到的节点、状态、IP 等,一般我们比较关心的是下面的 Events 部分,就是我们说的事件。
在 Kubernetes 创建资源对象的过程中,对该对象的一些重要操作,都会被记录在这个对象的 Events 里面,可以通过 kubectl describe 指令查看对应的结果。所以这个指令也会是以后我们排错过程中会经常使用的命令,一定要记住这个重要的命令。比如上面我们描述的这个 Pod,我们可以看到它被创建之后,被调度器调度(Successfully assigned)到了 ydzs-node2 节点上,然后指定的镜像已经在该节点上存在了,所以没有再去拉取镜像,然后创建我们定义的 nginx 容器,最后启动定义的容器。
另外一个方面如果我们相对我们的应用进行升级的话应该怎么办呢?这个操作在我们日常工作中还是非常常见的,而在 Kubernetes 这里也是非常简单的,我们只需要修改我们的资源清单文件即可,比如我们把镜像升级到最新版本nginx:latest:
...
spec:
containers:
- name: nginx
image: nginx:latest # 这里被从 1.7.9 修改为latest
ports:
- containerPort: 80
然后我们可以通过kubectl apply命令来直接更新,这个命令也是推荐我们使用的,我们不必关心当前的操作是创建,还是更新,执行的命令始终是 kubectl apply,Kubernetes 则会根据 YAML 文件的内容变化,自动进行具体的处理,所以无论是创建还是更新都可以直接使用这个命令:
$ kubectl apply -f nginx-deployment.yaml
通过这个命令就可以来更新我们的应用了,由于我们这里使用的是一个 Deployment 的控制器,所以会滚动更新我们的应用,我们可以通过在命令后面加上 --watch 参数来查看 Pod 的更新过程:
$ kubectl get pods -l app=nginx --watch
NAME READY STATUS RESTARTS AGE
nginx-deploy-54f57cf6bf-2fdjz 1/1 Terminating 0 72m
nginx-deploy-54f57cf6bf-57287 1/1 Running 0 72m
nginx-deploy-786b576769-69lrl 1/1 Running 0 4s
nginx-deploy-786b576769-wwmvz 0/1 ContainerCreating 0 2s
nginx-deploy-54f57cf6bf-2fdjz 0/1 Terminating 0 72m
nginx-deploy-786b576769-wwmvz 1/1 Running 0 3s
nginx-deploy-54f57cf6bf-57287 1/1 Terminating 0 72m
nginx-deploy-54f57cf6bf-57287 0/1 Terminating 0 72m
nginx-deploy-54f57cf6bf-57287 0/1 Terminating 0 72m
nginx-deploy-54f57cf6bf-57287 0/1 Terminating 0 72m
nginx-deploy-54f57cf6bf-2fdjz 0/1 Terminating 0 72m
nginx-deploy-54f57cf6bf-2fdjz 0/1 Terminating 0 72m
可以看到更新过程是先杀掉了一个 Pod,然后又重新创建了一个新的 Pod,然后又杀掉一个旧的 Pod,再创建一个新的 Pod,这样交替替换的,最后剩下两个新的 Pod,这就是我们所说的滚动更新,滚动更新对于我们的应用持续提供服务是非常重要的手段,在日常工作中更新应用肯定会采用这种方式。
最后,如果需要把我们的应用从集群中删除掉,可以用 kubectl delete 命令来清理:
$ kubectl delete -f nginx-deployment.yaml
下一篇将介绍Kubernetes中YAML如何编写资源清单
(转发请注明出处:http://www.cnblogs.com/zhangyongli2011/ 如发现有错,请留言,谢谢)