一. 简介
Kubernetes 里“最小”的 API 对象是 Pod。Pod 可以等价为一个应用,所以,Pod 可以由多个紧密协作的容器组成。在 Kubernetes 中,我们经常会看到它通过一种 API 对象来管理另一种 API 对象,比如 Deployment 和 Pod 之间的关系,而由于 Pod 是“最小”的对象,所以它往往都是被其他对象控制的。
这种组合方式,正是 Kubernetes 进行容器编排的重要模式。
二. 流程
关于如何演示kubectl apply 如何使用,下面讲创造一个这样的场景:
运行一个3个副本的nginx应用,并且nginx指定版本为1.7.9,同时对外暴露80端口。
本篇文章相关代码,已在GitHub此链接:demo-deployment.yaml
2.1 编写YAML
Kubernetes 跟 Docker 等很多项目最大的不同,就在于它不推荐我们使用命令行的方式直接运行容器(虽然 Kubernetes 项目也支持这种方式,比如:kubectl run
),而是希望用 YAML
文件的方式,即:把容器的定义、参数、配置,统统记录在一个YAML
文件中。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
如上的一个 YAML 文件,对应到 Kubernetes 中,就是一个 API Object(API 对象)。当你为这个对象的各个字段填好值并提交给 Kubernetes 之后,Kubernetes 就会负责创建出这些对象所定义的容器或者其他类型的 API 资源。
2.2 分析YAML
针对如上的yaml,细节参数含义如下:
- apiVersion:Api版本
- kind:指定当前Api type为Deployment
- spec.selector.mathcLabels:标签,作为过滤筛选使用
- spec.replicas:当前定义的Pod的数量,也就是期望值,Kubernetes会一直保证期望值=实际值
- spec.containers.image:容器的具体镜像名称+版本号
- spec.containers:容器相关配置
Labels这个字段也是上面的重点。这样的每一个 API 对象都有一个叫作 Metadata 的字段,这个字段就是 API 对象的“标识”,即元数据,它也是我们从 Kubernetes 里找到这个对象的主要依据。这其中最主要使用到的字段是 Labels。换句话说,Labels 就是一组 key-value 格式的标签。
而像 Deployment 这样的控制器对象,就可以通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。
比如,在上面这个YAML文件中,Deployment 会把所有正在运行的、携带“app: nginx”标签的 Pod 识别为被管理的对象,并确保这些 Pod 的总数严格等于三个。而这个过滤规则的定义,是在 Deployment 的“spec.selector.matchLabels”字段。我们一般称之为:Label Selector。
一个 Kubernetes 的 API 对象的定义,大多可以分为 Metadata 和 Spec 两个部分。
- Metadata
Metadata存放的是这个对象的元数据,对所有 API 对象来说,这部分的字段和格式基本上是一样的。
- Spec
Spec存放的是属于这个对象独有的定义,用来描述它所要表达的功能。
2.3 运行YAML
指令如下:
kubectl apply -f demo-deployment.yaml (推荐)
# kubectl create -f demo-deployment.yaml
成功后,如下图:
2.4 检查Pods
通过kubectl get命令检查这个YAML运行的运行结果是否与期望一致:
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
demo-deployment-5d59d67564-6dxnv 1/1 Running 0 2m23s
demo-deployment-5d59d67564-lgxzx 1/1 Running 0 2m23s
demo-deployment-5d59d67564-pcdk5 1/1 Running 0 2m23s
看到如下的状态,我们可以看到现在有3个 Pod 处于 Running 状态,也就意味着我们这个 Deployment 所管理的 Pod 都处于预期的状态。
kubectl get
指令的作用,就是从 Kubernetes 里面获取(GET)指定的 API 对象。可以看到,在这里还加上了一个-l参数,即获取所有匹配 app: nginx
标签的 Pod。
一个细节问题:在命令行中,所有key-value
格式的参数,都使用“=”而非“:”表示。
2.5 kubectl describe
我们从上面的Pod Name中挑选一个Pod,使用kubectl describe
进入查看。
kubectl describe pod demo-deployment-5d59d67564-6dxnv
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 8m default-scheduler Successfully assigned default/demo-deployment-5d59d67564-6dxnv to docker-desktop
Normal Pulled 7m59s kubelet Container image "nginx:1.7.9" already present on machine
Normal Created 7m59s kubelet Created container nginx
Normal Started 7m58s kubelet Started container nginx
在kubectl describe
命令返回的结果中,可以清楚地看到这个 Pod 的详细信息,比如它的 IP 地址等等。
在整个pod内容体里面,我们可以关注一下Events的内容。在 Kubernetes 执行的过程中,对 API 对象的所有重要操作,都会被记录在这个对象的 Events 里,并且显示在kubectl describe
指令返回的结果中。
这个Events从上向下看即可,对于当前这个 Pod,我们可以看到它被创建之后,被调度器调度(Successfully assigned)
到了 docker-desktop
,拉取了指定的镜像(本地)(Container image)
,然后启动了 Pod 里定义的容器(Started container)
。
这个正是我们将来进行 Debug 的重要依据。如果有异常发生,我们就可以第一时间查看这些 Events,往往可以看到非常详细的错误信息。
2.6 更新YAML(patch功能)
如果我们要对这个 Nginx 服务进行升级,把它的镜像版本从 1.7.9 升级为 1.8,那么操作还是很简单。
只需要变动YAML的nginx版本即可
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8.0 # 这里被从1.7.9修改为1.8
ports:
- containerPort: 80
执行kubectl apply
指令
kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
demo-deployment-64c9d67564-9m64m 1/1 Running 0 39s
demo-deployment-64c9d67564-m7l8w 1/1 Running 0 37s
demo-deployment-64c9d67564-rv5vs 1/1 Running 0 82s
是否觉得同一条指令kubectl apply
可以进行增改操作很神奇?这就是 Kubernetes“声明式 API”所推荐的使用方法。
执行的命令始终是 kubectl apply
,而 Kubernetes 则会根据 YAML 文件的内容变化,自动进行具体的处理。而这个流程的好处是,它有助于帮助开发和运维人员,围绕着可以版本化管理的 YAML 文件,而不是“行踪不定”的命令行进行协作,从而大大降低开发人员和运维人员之间的沟通成本。
2.7 删除demo-deployment
Kubernetes会自动删除YAML所相关的所有程序,这也是自动化与自描述带来的好处。
执行如下指令即可:
kubectl delete -f demo-deployment.yaml
三. Volume
3.1 Volume基础类型
这儿的Volume是Kubernetes里面,而非docker,但是本质上出别不大。具体为如下俩种:
- emptyDir
其实就等同于我们之前讲过的 Docker 的隐式 Volume 参数,即:不显式声明宿主机目录的 Volume。所以,Kubernetes 也会在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的 Volume 目录上。
- hostPath
与上面相反,就是显式的 Volume 定义。
3.2 案例
emptyDir类型
参考项目项目中的V2版本,使用的是emptyDir类型。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nginx-volume
volumes:
- name: nginx-volume
emptyDir: {}
我们在 Deployment
的 Pod 模板部分添加了一个volumes字段,定义了这个 Pod 声明的所有Volume
。它的名字叫作nginx-volume
,类型是emptyDir
。
Pod 中的容器,使用的是 volumeMounts
字段来声明自己要挂载哪个Volume,并通过mountPath
字段来定义容器内的 Volume 目录,比如:/usr/share/nginx/html
。
hostPath类型
参考项目项目中的V3版本,使用的是hostPath
类型。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nginx-volume
volumes:
- name: nginx-volume
hostPath:
path: "/var/tmp"
这个和上面类型原理同等,只是容器 Volume 挂载的宿主机目录,就变成了 /var/tmp。
3.4 kubectl exec
可以使用 kubectl exec
指令,进入到这个 Pod 当中(即容器的 Namespace 中)查看这个 Volume 目录:
kubectl exec -it demo-deployment-5846465d4d-vpkb2 -- /bin/bash
# ls /usr/share/nginx/html/
Reference
https://kubernetes.io/docs/setup/
https://medium.com/google-cloud/kubernetes-101-pods-nodes-containers-and-clusters-c1509e409e16
https://time.geekbang.org/column/article/40008?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title