云原生探索系列(五):部署第一个容器化应用

2024年 7月 12日 45.1k 0

前言

前段时间,已经在虚拟机部署了kubernetes集群,先部署一个容器化应用感受一下。 忘了在哪本书上看到这也一句话:看一个事儿千万不要直接陷入细节里,你应该先鸟瞰其全貌,这样能够帮助你从高维度理解问题。所以我们先不研究细节,先部署个 应用体验一下。

定义YAML文件

kubernetes不推荐你使用命令行的方式直接运行容器(比如:kubectl run),而是希望你用 YAML 文件的方式,即:把容器的定义、参数、配置,统统记录在一个 YAML 文件中,然后用一句指令把它运行起来。
先来看下面这个yaml文件, nginx-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.7.9
          ports:
          - containerPort: 80

这段YAML文件是一个 Kubernetes 的 Deployment 资源配置文件,用于定义如何部署和运行一个名为 nginx-deployment 的应用。

  • apiVersion: apps/v1 指定了使用的 Kubernetes API 版本和资源类型。在这里, apps/v1 表示我们使用的是 Kubernetes 中的 apps API 组的第一个版本。 Deployment 是 Kubernetes 中用于管理应用部署的资源类型。

  • kind: Deployment 指定了资源的类型,这里是一个 Deployment 资源,表示我们将定义一个应用的部署方式。

  • metadata: 在 metadata 下面是一些元数据,用于描述 Deployment 自身的信息,比如名称、标签等。

    • name: nginx-deployment :指定 Deployment 的名称为 nginx-deployment
  • spec: 定义了 Deployment 的规格,即我们希望 Kubernetes 如何管理应用的部署。

    • replicas: 2 :指定要运行的 Pod 副本数为 2。Deployment 会确保始终有指定数量的 Pod 在运行,如果少于指定数量,则会启动新的 Pod。

    • selector: :指定用来选择哪些 Pod 属于这个 Deployment 的标签选择器。

      selector:
        matchLabels:
          app: nginx
      

      这里指定了选择器 matchLabels ,它会选择所有具有 app: nginx 标签的 Pod 加入到这个 Deployment 中。

    • template: :定义了创建新 Pod 的模板。

      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx:1.7.9
              ports:
                - containerPort: 80
      
      • metadata.labels: :指定了 Pod 的标签,这里设置为 app: nginx 。Deployment 使用这个标签来识别和管理属于它的 Pod。

      • spec.containers: :定义了 Pod 中运行的容器。

        • name: nginx :容器的名称为 nginx

        • image: nginx:1.7.9 :指定了要使用的 Docker 镜像,这里是 nginx 镜像的 1.7.9 版本。

        • ports: :定义了容器暴露的端口。

          ports:
            - containerPort: 80
          

          这里指定容器监听的端口为 80 ,允许外部流量通过该端口访问该容器内的服务。

运行容器

大概了解了配置文件的基本知识之后,我们将其运行起来。
这里先创建一个命名空间,你也可以不创建,默认为default. 命令如下:

kubectl create namespace nginx

创建完成之后,可以使用如下命令查看:

kubectl get ns

好,接下来我们将在命名空间nginx上启动服务 命令如下:

kubectl apply -f nginx-deploy.yaml -n nginx

执行后,会看到如下输出

deployment.apps/nginx-deployment created

可以使用如下命令,查看Pod:

kubectl get po -n nginx -l app=nginx -owide

可以看到,在这里我还加上了一个 -l 参数,即获取所有匹配 app: nginx 标签的 Pod。需要注意的是,在命令行中,所有 key-value 格式的参数,都使用“=”而非“:”表示。
执行后,看到如下输出内容,

NAME                                READY   STATUS    RESTARTS   AGE    IP            NODE         NOMINATED NODE   READINESS GATES
nginx-deployment-5d59d67564-ht6zf   1/1     Running   0          3m2s   10.244.2.34   k8s-node02   <none>           <none>
nginx-deployment-5d59d67564-khcgr   1/1     Running   0          3m2s   10.244.2.35   k8s-node02   <none>           <none>

从返回的结果中,我们可以看到现在有两个 Pod 处于 Running 状态,也就意味着我们这个 Deployment 所管理的 Pod 都处于预期的状态。
当然,如果你想看到pod启动更详细的信息,可以执行如下命令:

kubectl describe po -n nginx nginx-deployment-5d59d67564-ht6zf

执行后,输出内容如下:

......
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  9m23s  default-scheduler  Successfully assigned nginx/nginx-deployment-5d59d67564-ht6zf to k8s-node02
  Normal  Pulled     9m22s  kubelet            Container image "nginx:1.7.9" already present on machine
  Normal  Created    9m22s  kubelet            Created container nginx
  Normal  Started    9m22s  kubelet            Started container nginx

可以重点看一下Events ,在 Kubernetes 执行的过程中,对 API 对象的所有重要操作,都会被记录在这个对象的 Events 里,并且显示在 kubectl describe 指令返回的结果中。
比如,对于这个 Pod,我们可以看到它被创建之后,被调度器调度(Successfully assigned)到了k8s-node02,拉取了指定的镜像(pulling image),然后启动了 Pod 里定义的容器(Started container)。
如果有异常发生,你一定要第一时间查看这些 Events,往往可以看到非常详细的错误信息。

nginx镜像版本升级

这就比较简单了,我们编辑nginx-deploy.yaml ,将1.7.9改为1.8,然后重新执行如下命令:

kubectl apply -f nginx-deploy.yaml -n nginx

在执行这条命令前,我们可以在另一个窗口,执行如下命令监控pod的变化:

kubectl get po -n nginx -l app=nginx -w

看到输出内容如下:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5d59d67564-ht6zf   1/1     Running   0          15m
nginx-deployment-5d59d67564-khcgr   1/1     Running   0          15m
nginx-deployment-64c9d67564-t87mn   0/1     Pending   0          0s
nginx-deployment-64c9d67564-t87mn   0/1     Pending   0          0s
nginx-deployment-64c9d67564-t87mn   0/1     ContainerCreating   0          0s
nginx-deployment-64c9d67564-t87mn   1/1     Running             0          28s
nginx-deployment-5d59d67564-ht6zf   1/1     Terminating         0          21m
nginx-deployment-64c9d67564-sxz9p   0/1     Pending             0          0s
nginx-deployment-64c9d67564-sxz9p   0/1     Pending             0          0s
nginx-deployment-64c9d67564-sxz9p   0/1     ContainerCreating   0          0s
nginx-deployment-5d59d67564-ht6zf   0/1     Terminating         0          21m
nginx-deployment-64c9d67564-sxz9p   1/1     Running             0          1s
nginx-deployment-5d59d67564-khcgr   1/1     Terminating         0          21m
nginx-deployment-5d59d67564-khcgr   0/1     Terminating         0          21m
nginx-deployment-5d59d67564-khcgr   0/1     Terminating         0          21m
nginx-deployment-5d59d67564-khcgr   0/1     Terminating         0          21m
nginx-deployment-5d59d67564-ht6zf   0/1     Terminating         0          21m
nginx-deployment-5d59d67564-ht6zf   0/1     Terminating         0          21m

从监控结果可以看到,在新的pod:nginx-deployment-64c9d67564-t87mn创建成功,旧的pod:nginx-deployment-5d59d67564-ht6zf才被删除 然后再创建一个新的pod,删除一个旧的pod.
新旧两个 Pod,被交替创建、删除,最后剩下的就是新版本的 Pod,这就是滚动更新。

声明Volume

在 Kubernetes 中,Volume 是属于 Pod 对象的一部分。所以,我们就需要修改这个 YAML 文件里的 template.spec 字段,如下图所示

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.8
          ports:
            - containerPort: 80
          volumeMounts:
            - mountPath: "/usr/share/nginx/html"
              name: nginx-vol
      volumes:
        - name: nginx-vol
          emptyDir: {}

volumeMounts 和 volumes 都是用来定义容器中数据卷挂载的部分,允许我们将持久化存储挂载到容器中,以便在容器之间共享数据或持久化数据。

  • volumeMounts 定义了容器中的挂载点,即容器内部文件系统中的一个路径,以及该路径要挂载的实际数据卷。

    • name :挂载的数据卷的名称,在 volumes 部分定义。
    • mountPath :挂载到容器中的路径,这是容器中的一个目录或文件路径,例如 /usr/share/nginx/html
    • 其他可选字段还包括 readOnly ,用于指定挂载是否为只读,默认为 false。 在上面的例子中:
volumeMounts:
  - mountPath: "/usr/share/nginx/html"
    name: nginx-vol

这段代码指定了一个名为 nginx-vol 的数据卷将被挂载到 nginx 容器中的 /usr/share/nginx/html 路径下。这通常用于将持久化数据或配置文件等内容挂载到容器中,使容器可以访问这些数据。

  • volumes 定义了要在 Pod 中使用的存储卷的详细信息。

    • name :数据卷的名称,用于在 volumeMounts 中引用。
    • emptyDir :一个空目录卷,由 Kubernetes 创建,并且在同一个 Pod 中的所有容器之间共享。这个目录在容器重启或被迫重新调度时会被清空。 在上面的例子中:
volumes:
  - name: nginx-vol
    emptyDir: {}

这段代码定义了一个名为 nginx-vol 的空目录卷 (emptyDir ),这意味着 Kubernetes 将在 Pod 中创建一个空目录,并将其挂载到任何需要使用该卷的容器中。这个空目录卷将在 Pod 启动时创建,并且在该 Pod 中的所有容器之间共享。
当然,Kubernetes 也提供了显式的 Volume 定义,它叫作 hostPath。比如下面的这个 YAML 文件:

# ......
spec:
  # ......
  template:
    # ......
    spec:
      # ......
      volumes:
        - name: nginx-vol
          hostPath:
            path: "/opt"

这样,容器 Volume 挂载的宿主机目录,就变成了 /opt。
再次执行kubectl apply -f nginx-deploy.yaml -n nginx ,然后执行kubectl describe po -n nginx nginx-deployment-68d9f66888-6zh52
输出内容中可以看到Volume 的信息,如下

Containers:
  nginx:
    Container ID:   docker://673310fc9646b0939bf2ee62e6bb8b304f9656163392e2b78aad51bb87d9e993
    Image:          nginx:1.8
    Image ID:       docker-pullable://nginx@sha256:c97ee70c4048fe79765f7c2ec0931957c2898f47400128f4f3640d0ae5d60d10
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Thu, 11 Jul 2024 16:12:42 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from nginx-vol (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-hhq2h (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  nginx-vol:
    Type:          HostPath (bare host directory volume)
    Path:          /opt
    HostPathType:

删除Nginx Deployment

可以直接执行如下命令:

kubectl delete -f nginx-deployment.yaml

使用Deployment好处

宕机的时候,会重新pod会在另外的node上重启。我们来实验一下:
执行如下命令:

kubectl get po -n nginx -l app=nginx -w -owide

输出结果如下:

NAME                                READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
nginx-deployment-68d9f66888-6t4nx   1/1     Running   0          11m   10.244.2.39   k8s-node02   <none>           <none>
nginx-deployment-68d9f66888-trqzx   1/1     Running   0          11m   10.244.2.38   k8s-node02   <none>           <none>

可以看到现在pod都跑在k8s-node02节点上,现在我就k8s-node02这台机器关机,看看如何变化,

nginx-deployment-68d9f66888-trqzx   1/1     Running   0          18m     10.244.2.46   k8s-node02   <none>           <none>
nginx-deployment-68d9f66888-6t4nx   1/1     Running   0          18m     10.244.2.47   k8s-node02   <none>           <none>
nginx-deployment-68d9f66888-trqzx   1/1     Terminating   0          23m     10.244.2.46   k8s-node02   <none>           <none>
nginx-deployment-68d9f66888-6t4nx   1/1     Terminating   0          23m     10.244.2.47   k8s-node02   <none>           <none>
nginx-deployment-68d9f66888-7xr6t   0/1     Pending       0          0s      <none>        <none>       <none>           <none>
nginx-deployment-68d9f66888-7xr6t   0/1     Pending       0          0s      <none>        k8s-node01   <none>           <none>
nginx-deployment-68d9f66888-lcrwf   0/1     Pending       0          0s      <none>        <none>       <none>           <none>
nginx-deployment-68d9f66888-lcrwf   0/1     Pending       0          0s      <none>        k8s-node01   <none>           <none>
nginx-deployment-68d9f66888-7xr6t   0/1     ContainerCreating   0          0s      <none>        k8s-node01   <none>           <none>
nginx-deployment-68d9f66888-lcrwf   0/1     ContainerCreating   0          3s      <none>        k8s-node01   <none>           <none>
nginx-deployment-68d9f66888-7xr6t   1/1     Running             0          25s     10.244.1.18   k8s-node01   <none>           <none>
nginx-deployment-68d9f66888-lcrwf   1/1     Running             0          41s     10.244.1.23   k8s-node01   <none>           <none>

从输出结果可以看到,自动将pod调度到k8s-node01节点上了。
注意:这里涉及到了pod的调度策略,可先做个简单了解
Kubernetes 的调度策略可能会影响 Pod 的调度速度。例如,如果你的 Pod 使用了特定的调度限制(如 NodeSelector、NodeAffinity、PodAffinity 等),这可能会限制调度器在可用节点中的选择。
当节点被标记为 NotReady 后,Pod 并不会立即被重新调度到其他节点上。Kubernetes 会等待一段时间(默认情况下是 5 分钟),称为 pod-eviction-timeout ,用于等待节点重新变为 Ready 或确认节点已经永久不可用。在这段时间内,Pod 仍然会保持在该节点上,直到 Kubernetes 确定需要重新调度它们。

最后

无需深入研究细节,首先着手部署属于您的首个容器化应用吧!这将让您对学习充满信心并激发成就感,同时在实践中也能积累丰富的知识。日后学习细节时,相信会发现理解起来轻松许多哦。

相关文章

KubeSphere 部署向量数据库 Milvus 实战指南
探索 Kubernetes 持久化存储之 Longhorn 初窥门径
征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
那些年在 Terraform 上吃到的糖和踩过的坑
无需 Kubernetes 测试 Kubernetes 网络实现
Kubernetes v1.31 中的移除和主要变更

发布评论