侧边栏壁纸
博主头像
Wyatt博主等级

Done is better than perfect!

  • 累计撰写 103 篇文章
  • 累计创建 31 个标签
  • 累计收到 7 条评论

Kubernetes:入门案例

Wyatt
2021-03-25 / 0 评论 / 0 点赞 / 224 阅读 / 7,576 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-03-25,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

一. 简介

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

成功后,如下图:
success

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 都处于预期的状态。
3 pods

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,那么操作还是很简单。

  1. 只需要变动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
  1. 执行kubectl apply指令
kubectl apply -f demo-deployment.yaml (推荐)
# kubectl replace -f demo-deployment.yaml(不推荐)
  1. 查看Pods状态
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 案例

  1. 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

  1. 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/

四. 总结

关于Kubernetes与Docker的组合,我们已经可以按照如下步骤进行落地编写了:

  1. 在本地通过 Docker 测试代码,制作镜像
  2. 选择合适的 Kubernetes API 对象,编写对应 YAML 文件(比如,Pod,Deployment)
  3. 在 Kubernetes 上部署这个 YAML 文件。

我们会发现Kubernetes与Docker很多指令很相同,但是以后我们还是少用Docker指令,毕竟Docker只是整个云原生中很小的一个实现类而已,Kubernetes才是中枢系统。
本篇文章,我讲了一个最常见的Deployment落地的全生命周期操作流程,关于每个细节其实还有很多其他内容,这也是后面细分方向文章的重点。

最后,欢迎关注我的博客:https://blog.wyatt.plus

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

0

评论区