概述

本文旨在利用简单的 web-db 服务,串接 k8s 相关知识

配置

  • 本文采用 kind 搭建测试 k8s 集群

    kind 部署 kubernetes 群集相当方便,可以快速部署多个群集。但在群集的配置和更新上有明显弊端,只能通过重新创建群集来配置和更新群集。所以在初始化群集的时候需要考虑好配置选项。当然我们只是用于测试环境,也无大碍

  • 采用 VMware® Workstation 17 Pro 及 CentOS-7-x86_64-Minimal-2009 AlmaLinux-9.4-x86_64-minimal (centos7 坑有点多,并且不再维护,所以后续使用 almalinux) 作为基础环境

集群搭建

  1. 采用最简单的方式,下载编译后的二进制包,可见 Installing From Release Binaries

    1
    2
    3
    [ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64
    chmod +x ./kind
    sudo mv ./kind /usr/bin/kind
  2. 安装 docker

    1. /etc/yum.repo.d 中新增 docker 源

      1
      sudo curl -o /etc/yum.repo.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    2. 安装: sudo yum install docker-ce-24.0.7

    3. 配置 docker mirror,修改 /etc/docker/daemon.json,加入以下,参考 零门槛最简单国内访问Docker Hub的替代方案

      1
      2
      3
      4
      5
      6
      7
      8
      {
      "registry-mirrors": [
      "https://docker.m.daocloud.io",
      "https://huecker.io",
      "https://dockerhub.timeweb.cloud",
      "https://noohub.ru"
      ]
      }
    4. 生效

      1
      2
      sudo systemctl enable docker
      sudo systemctl start docker
  3. 安装 kubectl,可见 Install and Set Up kubectl on Linux

    1
    2
    curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
  4. 搭建测试集群:kind create cluster --config kind-example-config.yaml --name example,如果失败,重试即可(失败可能是虚拟机资源不够)

    • 配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      # this config file contains all config fields with comments
      # NOTE: this is not a particularly useful config file
      kind: Cluster
      apiVersion: kind.x-k8s.io/v1alpha4
      # patch the generated kubeadm config with some extra settings
      kubeadmConfigPatches:
      - |
      apiVersion: kubelet.config.k8s.io/v1beta1
      kind: KubeletConfiguration
      evictionHard:
      nodefs.available: "0%"
      # patch it further using a JSON 6902 patch
      kubeadmConfigPatchesJSON6902:
      - group: kubeadm.k8s.io
      version: v1beta2
      kind: ClusterConfiguration
      patch: |
      - op: add
      path: /apiServer/certSANs/-
      value: my-hostname
      # 1 control plane node and 3 workers
      nodes:
      # the control plane node config
      - role: control-plane
      # the three workers
      - role: worker
      - role: worker
      - role: worker

      image-20240721181705648

    • 切换为该集群,并查看 node 信息

      1
      2
      kubectl cluster-info --context kind-example
      kubectl get nodes

      image-20240721180936929

web-db

版本1——Pod

  • 由于本文的目的是搭建一个带有 DB 的 web,因此简易版本需要两个镜像 nginx:latestmysql:latest

  • 由于 kind 不推荐使用 latest 标签(可见 [pod 报错 ErrImagePull 或 ImagePullBackOff](##pod 报错 ErrImagePull 或 ImagePullBackOff)),因此使用以下重命名 tag

    1
    2
    docker tag nginx:latest nginx:v1.27.0
    docker tag mysql:latest mysql:v9.0.1
  • 由于 kind 特性,需要将 image 加载到 cluster 中

    1
    kind load docker-image nginx:v1.27.0 mysql:v9.0.1 --name example1

    image-20240727190751229

  • 可以通过 docker exec -it example-worker crictl images 查看某个 node 已加载的 image 信息

    image-20240727190342358

  • 编辑文件webapp-with-db-pod.yaml 并执行: kubectl apply -f webapp-with-db-pod.yaml,即可生成一个 pod,其中含有两个 container

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion: v1
    kind: Pod
    metadata:
    name: webapp-with-db
    labels:
    app: my-webapp
    spec:
    containers:
    - name: webapp
    image: nginx:v1.27.0
    ports:
    - containerPort: 80
    - name: database
    image: mysql:v9.0.1

    image-20240727190833304

  • 通过 kubectl get pods 可查看 pod 状态,kubectl get pods -o yaml 查看更详细信息。一段时间后,发现 pod 状态异常,通过以下指令一步步定位问题

    image-20240727213828657

    1. 通过 kubectl get pods -o yaml 查看更多的信息,没有发现根本原因

      image-20240727213903969

    2. 查看 event: kubectl get events -o yaml,也没有发现更多信息

      image-20240727214304441

    3. 查看 pod 中 database 容器日志:kubectl logs webapp-with-db -c database

      1. 可以发现,MySQL 需要以下环境变量才可以正常启动

        image-20240727214359820

      2. 临时解决办法是使用以下 yaml 重新 apply,注意 value 需要使用 string(需删除之前的 pod)。后续版本有更安全的方式

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        apiVersion: v1
        kind: Pod
        metadata:
        name: webapp-with-db
        labels:
        app: my-webapp
        spec:
        containers:
        - name: webapp
        image: nginx:v1.27.0
        ports:
        - containerPort: 80
        - name: database
        image: mysql:v9.0.1
        env:
        - name: MYSQL_ROOT_PASSWORD
        value: "123"
      3. 如果不删除之前的 pod,报错如下

        image-20240727215051128

版本2——Service(集群内服务提供)

  • 版本1已经成功生成了一个 pod,但是外界无法访问该服务,因此引入 k8s 中 service 概念,将 Pod 提供的服务暴露给集群内部

    It allows you to expose your application to other pods within the cluster or to external clients. Services provide load balancing and automatic scaling for the pods behind them, ensuring that the application remains highly available.

  • 更新 yaml 为以下内容,重新执行 kubectl apply -f webapp-with-db-pod.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    apiVersion: v1
    kind: Pod
    metadata:
    name: webapp-with-db
    labels:
    app: my-webapp
    spec:
    containers:
    - name: webapp
    image: nginx:v1.27.0
    ports:
    - containerPort: 80
    - name: database
    image: mysql:v9.0.1
    env:
    - name: MYSQL_ROOT_PASSWORD
    value: "123"
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp-service
    spec:
    selector:
    app: my-webapp
    ports:
    - protocol: TCP
    port: 80
    targetPort: 80

    image-20240727220711554

  • 与上个版本 diff 如下

  • 此时服务只在集群中可被访问,重新起一个 pod 用于测试连通性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    apiVersion: v1
    kind: Pod
    metadata:
    name: db-test
    labels:
    app: db-test
    spec:
    containers:
    - name: db-test
    image: mysql:v9.0.1
    env:
    - name: MYSQL_ROOT_PASSWORD
    value: "123"
    1. 通过 kubectl get services 拿到 webapp-service 的 cluster ip

      image-20240727223706053

    2. 进入新 pod,测试服务连通性

      1
      2
      3
      4
      kubectl get pods
      kubectl exec -it db-test -- bash
      # 也可以通过 curl http://webapp-service 测试
      curl http://IP:80

      image-20240727224711219

版本3——Service(集群外服务提供)

nodeport

  • 版本2 已经提供了一个集群内可供访问的服务,但是集群外无法访问服务,可以通过 nodeport 的方式对外提供服务

  • 更新 yaml 为以下内容,重新执行 kubectl apply -f webapp-with-db-pod.yaml。其中 IP 为 pod 所在 node IP(任意一个即可),可以通过 kubectl get nodes -o wide 查看 nodeip,即 INTERNAL-IP

    在 Kubernetes 中,当你使用 NodePort 类型的 Service 时,这个 Service 将会在集群中的每个节点上监听一个相同的端口,并将流量转发到对应的 Pod。这就是为什么你可以通过任意一个节点的 IP 地址和指定的 NodePort 访问到服务,即使 Pod 并不在所有节点上。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    apiVersion: v1
    kind: Pod
    metadata:
    name: webapp-with-db
    labels:
    app: my-webapp
    spec:
    containers:
    - name: webapp
    image: nginx:v1.27.0
    ports:
    - containerPort: 80
    - name: database
    image: mysql:v9.0.1
    env:
    - name: MYSQL_ROOT_PASSWORD
    value: "123"
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp-service
    spec:
    selector:
    app: my-webapp
    ports:
    - protocol: TCP
    port: 80
    nodePort: 30001
    type: NodePort

    image-20240728102216756

  • 与上个版本 diff 如下

  • nodeport 和 targetport 区别

    • NodePort
      • 用于在集群外暴露 Service
      • 在每个节点上打开一个固定的端口,用于接收外部流量
      • 外部客户端可以通过节点的 IP 地址和 NodePort 访问 Service
    • targetPort
      • 用于定义 Service 转发流量到 Pod 的哪个端口
      • 是 Pod 内容器暴露的端口
      • 定义了 Service 如何将流量路由到 Pod 内容器

ingress

版本4——ConfigMap

  • 版本3 已经成功将 Pod 中的服务提供给外部访问,但是如果需要更新 pod 中配置文件或者环境变量,应该怎么做?

  • k8s 提供了 ConfigMap 的概念,用于存储配置文件信息

    A Kubernetes ConfigMap is used to store configuration data that can be consumed by pods as environment variables or mounted as configuration files. It helps separate the configuration from the container image, making it easier to update configurations without rebuilding the container.

  • 配置如下

    • 新增了 ConfigMap 配置 webapp-config

    • webapp 通过 envFrom 引用了 webapp-config

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      apiVersion: v1
      kind: Pod
      metadata:
      name: webapp-with-db
      labels:
      app: my-webapp
      spec:
      containers:
      - name: webapp
      image: nginx:v1.27.0
      ports:
      - containerPort: 80
      envFrom:
      - configMapRef:
      name: webapp-config
      - name: database
      image: mysql:v9.0.1
      env:
      - name: MYSQL_ROOT_PASSWORD
      value: "123"
      ---
      apiVersion: v1
      kind: Service
      metadata:
      name: webapp-service
      spec:
      selector:
      app: my-webapp
      ports:
      - protocol: TCP
      port: 80
      nodePort: 30001
      type: NodePort
      ---
      apiVersion: v1
      kind: ConfigMap
      metadata:
      name: webapp-config
      data:
      WEBAPP_ENV: "production"
    • 直接修改后 apply 报错如下 => 解决:删除现有 pod,重新 apply(如果是 deployment 或者 replicaset 只需要删除,k8s 会自动重建)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      service/webapp-service unchanged
      configmap/webapp-config created
      The Pod "webapp-with-db" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`,`spec.initContainers[*].image`,`spec.activeDeadlineSeconds`,`spec.tolerations` (only additions to existing tolerations),`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)
      core.PodSpec{
      Volumes: {{Name: "kube-api-access-85btk", VolumeSource: {Projected: &{Sources: {{ServiceAccountToken: &{ExpirationSeconds: 3607, Path: "token"}}, {ConfigMap: &{LocalObjectReference: {Name: "kube-root-ca.crt"}, Items: {{Key: "ca.crt", Path: "ca.crt"}}}}, {DownwardAPI: &{Items: {{Path: "namespace", FieldRef: &{APIVersion: "v1", FieldPath: "metadata.namespace"}}}}}}, DefaultMode: &420}}}},
      InitContainers: nil,
      Containers: []core.Container{
      {
      ... // 4 identical fields
      WorkingDir: "",
      Ports: {{ContainerPort: 80, Protocol: "TCP"}},
      - EnvFrom: nil,
      + EnvFrom: []core.EnvFromSource{
      + {
      + ConfigMapRef: &core.ConfigMapEnvSource{LocalObjectReference: core.LocalObjectReference{...}},
      + },
      + },
      Env: nil,
      Resources: {},
      ... // 15 identical fields
      },
      {Name: "database", Image: "mysql:v9.0.1", Env: {{Name: "MYSQL_ROOT_PASSWORD", Value: "123"}}, VolumeMounts: {{Name: "kube-api-access-85btk", ReadOnly: true, MountPath: "/var/run/secrets/kubernetes.io/serviceaccount"}}, ...},
      },
      EphemeralContainers: nil,
      RestartPolicy: "Always",
      ... // 28 identical fields
      }
      • 原因:向已经运行的 Pod 添加 ConfigMap 是不支持的。ConfigMap 是 Kubernetes 中用于存储非敏感数据的对象,用于配置应用程序、存储环境变量等。当一个 Pod 创建时,它会使用 ConfigMap 中的数据或者环境变量,但是一旦 Pod 已经创建并且在运行中,就不能直接修改它的配置或环境变量
      • 如果你需要在一个正在运行的 Pod 中更新配置,你有几种选择:
        1. 重新启动 Pod
          • 最简单的方法是重新启动 Pod。你可以更新 Pod 配置文件,将新的 ConfigMap 引入到 Pod 的配置中,然后删除现有的 Pod,Kubernetes 会自动创建一个新的 Pod,并使用更新后的配置。
        2. 使用 Sidecar 容器
          • 你可以在需要更新配置的 Pod 中添加一个 Sidecar 容器,这个 Sidecar 容器负责监视 ConfigMap 的变化,并将更新后的配置传递给主要的应用程序容器。
        3. 使用 Init 容器
          • 另一种方法是使用 Init 容器。你可以在 Pod 中添加一个 Init 容器,这个容器可以在主要应用程序容器启动之前获取最新的配置,并将其传递给主要应用程序容器。
        4. 使用动态配置
          • 一些应用程序支持动态地重新加载配置,而不需要重新启动。如果你的应用程序支持动态配置加载,你可以更新 ConfigMap,然后通知应用程序重新加载配置。
  • 通过 kubectl exec -it webapp-with-db -c webapp -- bash 查看 env 可以发现多了一个环境变量

    image-20240728120149792

  • 如果此时修改 WEBAPP_ENV 的值,通过 kubectl describe configmap webapp-config 可以发现值已经变量,但是再次进入容器内,可以发现值没有发生变化,是因为 在 Kubernetes 中,Pod 中使用 ConfigMap 设置的环境变量通常需要 Pod 重启才能加载新的值。这是因为当 Pod 启动时,它会获取 ConfigMap 中的值并将其转换为环境变量。如果在运行时更新了 ConfigMap 中的数据,Pod 中的容器并不会自动获取到这些更新后的值,除非 Pod 重新启动

  • 如果希望不重启修改能生效,可以采用以下方案

    • 使用 Sidecar 容器:添加一个 Sidecar 容器,这个容器可以监视 ConfigMap 的变化并更新主要应用程序容器的环境变量。
    • 使用 Init 容器:引入一个 Init 容器,它可以在主要应用程序容器启动之前获取最新的配置,并将其传递给主要应用程序容器。
    • 应用程序支持动态更新:有些应用程序支持动态地重新加载配置,可以在配置发生变化时主动更新环境变量,而不需要重启应用程序

版本5——Secret

  • 版本4 已经能够满足配置文件的存储需要了,但是敏感信息直接使用 ConfigMap 安全性不够

  • k8s 提供了 Secret 的概念,用于存储敏感信息

    Kubernetes Secrets are used to store sensitive information, such as passwords, API keys, or TLS certificates. Secrets are base64-encoded by default and can be mounted as files or used as environment variables in pods.

  • 将 MYSQL_ROOT_PASSWORD 放入 secret,其中 yaml 中填写的值是 base64 处理后的结果,type: Opaque 表示 Secret 中的数据是一个字典,其中包含了任意类型的键值对数据。这意味着在 Opaque Secret 中,可以存储任何类型的数据,例如密码、证书、Token 等,而这些数据以 base64 编码的形式存储在 Secret 对象中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    apiVersion: v1
    kind: Pod
    metadata:
    name: webapp-with-db
    labels:
    app: my-webapp
    spec:
    containers:
    - name: webapp
    image: nginx:v1.27.0
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: webapp-config
    - name: database
    image: mysql:v9.0.1
    env:
    - name: MYSQL_ROOT_PASSWORD
    valueFrom:
    secretKeyRef:
    name: db-credentials
    key: password
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp-service
    spec:
    selector:
    app: my-webapp
    ports:
    - protocol: TCP
    port: 80
    nodePort: 30001
    type: NodePort
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: webapp-config
    data:
    WEBAPP_ENV: "production"
    ---
    apiVersion: v1
    kind: Secret
    metadata:
    name: db-credentials
    type: Opaque
    data:
    password: MTIz

版本6——Volume

  • 以上版本,实现了配置文件和敏感信息的写入,但是对于 DB 数据的持久化还没有实现,并且Pod 中不同容器间也无法共享数据

  • k8s 提供了 Volume 的概念,用于存储数据持久化和数据共享

    A Kubernetes Volume is a directory that is accessible to all containers in a pod. It decouples the storage from the containers, ensuring that data persists even if a container is restarted or rescheduled.

  • 以下使用 PersistentVolumeClaim 用于请求持久存储资源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    apiVersion: v1
    kind: Pod
    metadata:
    name: webapp-with-db
    labels:
    app: my-webapp
    spec:
    containers:
    - name: webapp
    image: nginx:v1.27.0
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: webapp-config
    - name: database
    image: mysql:v9.0.1
    env:
    - name: MYSQL_ROOT_PASSWORD
    valueFrom:
    secretKeyRef:
    name: db-credentials
    key: password
    volumeMounts:
    - name: db-data
    mountPath: /var/lib/mysql
    volumes:
    - name: db-data
    persistentVolumeClaim:
    claimName: database-pvc
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp-service
    spec:
    selector:
    app: my-webapp
    ports:
    - protocol: TCP
    port: 80
    nodePort: 30001
    type: NodePort
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: webapp-config
    data:
    WEBAPP_ENV: "production"
    ---
    apiVersion: v1
    kind: Secret
    metadata:
    name: db-credentials
    type: Opaque
    data:
    password: MTIz
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: database-pvc
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Mi
  • 可以通过 kubectl get pvc 查看 PVC 的状态

    image-20240728145240123

  • 可以通过以下方式验证 PVC 的有效性

    1. 生成 pod,进入 database,创建数据库和数据表

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      kubectl apply -f webapp-with-db-pod.yaml

      create database hello;
      use hello;
      CREATE TABLE students (
      id INT AUTO_INCREMENT PRIMARY KEY,
      name VARCHAR(50) NOT NULL,
      age INT,
      gender ENUM('Male', 'Female', 'Other'),
      grade FLOAT
      );
      INSERT INTO students (name, age, gender, grade) VALUES
      ('Alice', 20, 'Female', 85.5),
      ('Bob', 22, 'Male', 78.3),
      ('Charlie', 21, 'Male', 92.0);
    2. 销毁 pod: kubectl delete po webapp-with-db

    3. 重新生成 pod,进入 database,查看数据是否存在

版本7——deployment

  • 上述版本已经较好的实现了一个服务从启动、对外提供服务、配置信息和敏感信息存储,已经数据持久化工作。但是当 pod 异常退出时,目前没有自愈能力,包括流量变化时,也没有扩缩容能力

  • k8s 提供了 Deployment 的概念,用于无状态应用的生命周期管理

    A Kubernetes Deployment is a higher-level abstraction that manages a set of identical pods. It’s ideal for stateless applications where individual pods are interchangeable. Deployments provide features like rolling updates, rollback, and scaling, making them suitable for web servers, APIs, and microservices.

  • 配置如下

    • replicas: 3:指定了部署的 Pod 副本数量为 3
    • selector: 定义了用于选择哪些 Pod 属于这个 Deployment 的标签选择器
    • template labels: app: my-webapp:为新创建的 Pod 添加了标签,用于匹配选择器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: webapp-deployment
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: my-webapp
    template:
    metadata:
    labels:
    app: my-webapp
    spec:
    containers:
    - name: webapp
    image: nginx:v1.27.0
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: webapp-config
    - name: database
    image: mysql:v9.0.1
    env:
    - name: MYSQL_ROOT_PASSWORD
    valueFrom:
    secretKeyRef:
    name: db-credentials
    key: password
    volumeMounts:
    - name: db-data
    mountPath: /var/lib/mysql
    volumes:
    - name: db-data
    persistentVolumeClaim:
    claimName: database-pvc
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp-service
    spec:
    selector:
    app: my-webapp
    ports:
    - protocol: TCP
    port: 80
    nodePort: 30001
    type: NodePort
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: webapp-config
    data:
    WEBAPP_ENV: "production"
    ---
    apiVersion: v1
    kind: Secret
    metadata:
    name: db-credentials
    type: Opaque
    data:
    password: MTIz
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: database-pvc
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Mi
  • apiVersion: apps/v1 和 apiVersion: v1 区别

    • apiVersion: apps/v1
      • apps/v1 是 Kubernetes 中用于管理应用程序工作负载的 API 版本。
      • apps/v1 版本中,包含了 DeploymentDaemonSetStatefulSet 等控制器对象的定义。
      • 通常用于创建部署、守护进程集、有状态集等应用程序控制器
    • apiVersion: v1
      • v1 是 Kubernetes 中核心 API 对象的稳定版本。
      • v1 版本中,包含了核心对象如 PodServiceNamespaceConfigMapSecret 等的定义。
      • 用于创建核心资源对象,如 Pod、Service、ConfigMap 等
  • 此时删除 pod,deployment 会自动再次生成 pod

    image-20240728153759439

  • 如果需要彻底删除

    1. 使用 kubectl get deployments 拿到 <deployment-name>
    2. 彻底删除 kubectl delete deployment <deployment-name>,将触发 Kubernetes 控制器删除该 Deployment 中的所有 Pod,删除有时间间隔,可以通过 kubectl get events 查看

版本8——StatefulSet

  • 既然无状态应用有 deployment,那么像 DB 这样的有状态应用,也有对应的处理方式

  • k8s 提供了 StatefulSet 的概念,用于有状态应用的生命周期管理

    On the other hand, a Kubernetes StatefulSet is used for stateful applications where each pod has a unique identity and persistent storage. StatefulSets provide stable network identities and are suitable for databases, key-value stores, and other applications that require unique persistent storage and ordered scaling.

  • 配置如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: webapp-deployment
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: my-webapp
    template:
    metadata:
    labels:
    app: my-webapp
    spec:
    containers:
    - name: webapp
    image: nginx:v1.27.0
    ports:
    - containerPort: 80
    envFrom:
    - configMapRef:
    name: webapp-config
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
    name: database-statefulset
    spec:
    serviceName: database
    replicas: 1
    selector:
    matchLabels:
    app: database
    template:
    metadata:
    labels:
    app: database
    spec:
    containers:
    - name: database
    image: mysql:v9.0.1
    env:
    - name: MYSQL_ROOT_PASSWORD
    valueFrom:
    secretKeyRef:
    name: db-credentials
    key: password
    volumeMounts:
    - name: db-data
    mountPath: /var/lib/mysql
    volumes:
    - name: db-data
    persistentVolumeClaim:
    claimName: database-pvc
    volumeClaimTemplates:
    - metadata:
    name: database-data
    spec:
    accessModes: [ "ReadWriteOnce" ]
    resources:
    requests:
    storage: 10Mi
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: webapp-service
    spec:
    selector:
    app: my-webapp
    ports:
    - protocol: TCP
    port: 80
    nodePort: 30001
    type: NodePort
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: webapp-config
    data:
    WEBAPP_ENV: "production"
    ---
    apiVersion: v1
    kind: Secret
    metadata:
    name: db-credentials
    type: Opaque
    data:
    password: MTIz
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: database-pvc
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Mi

    image-20240728162254921

k8s error

pod 报错 ErrImagePull 或 ImagePullBackOff

  • 通过 kubectl describe pods [pod name] 可查看详细信息,从 Events 可看到报错信息

    1
    Warning  Failed     11m                   kubelet            Failed to pull image "nginx:latest": failed to pull and unpack image "docker.io/library/nginx:latest": failed to copy: httpReadSeeker: failed open: failed to do request: Get "https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/a7/a72860cb95fd59e9c696c66441c64f18e66915fa26b249911e83c3854477ed9a/data?verify=1722076145-hCjnrHPwpwV1nad%2F30IHJ5M0EIE%3D": dial tcp 173.231.12.107:443: connect: connection refused
  • 可以看到 k8s 无法拉取镜像,是因为 kind 需要将 image 加载到 cluster 中(否则会去官网 pull 镜像),并且 kind 不建议使用 latest,因此使用指定 tag 加载进 cluster,可见 Unable to pull images

    NOTE: The Kubernetes default pull policy is IfNotPresent unless the image tag is :latest or omitted (and implicitly :latest) in which case the default policy is Always. IfNotPresent causes the Kubelet to skip pulling an image if it already exists. If you want those images loaded into node to work as expected, please:

    • don’t use a :latest tag

    and / or:

    • specify imagePullPolicy: IfNotPresent or imagePullPolicy: Never on your container(s).

    See Kubernetes imagePullPolicy for more information.

  • 将镜像加载到 cluster: kind load docker-image nginx:v1.27.0 mongo:v7.0.12 --name example1

  • 查看指定 node 已有的 image: docker exec -it my-node-name crictl images

    Where my-node-name is the name of the Docker container (e.g. kind-control-plane).

k8s 常用操作

  • 查看特定 node 上的 pod: kubectl get pods --field-selector spec.nodeName=<node-name>

附 centos 安装错误

  1. kind create cluster 运行报错: docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: cgroup namespaces aren't enabled in the kernel: unknown.

    image-20240721155039042

    • 尝试解决,无果,相关问题 kind: v0.20.0 cannot create clusters on RHEL 7

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      # 1. 检查内核配置, 如果输出中显示CONFIG_CGROUPS=n,则表示cgroup未启用
      sudo cat /boot/config-$(uname -r) | grep CONFIG_CGROUPS

      # 2. 修改参数
      sudo vi /etc/default/grub
      # 如果存在 在最后附加
      GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

      # 3. 生效
      # 查看引导方式
      grub-install --version
      grub2-install --version

      # 对于GRUB引导
      sudo update-grub
      # 对于GRUB2引导
      sudo grub2-mkconfig -o /boot/grub2/grub.cfg
      # 对于Systemd-boot引导
      sudo bootctl update

      # 4. 重启系统
      sudo reboot

      image-20240721155842722

参考