Kubernetes入门学习(下)

Kubernetes入门学习(下)

文章目录

运行有状态的应用

我们以MySQL数据库为例,在kubernetes集群中运行一个有状态的应用。

部署数据库几乎覆盖了kubernetes中常见的对象和概念:

  • 配置文件--ConfigMap
  • 保存密码--Secret
  • 数据存储--持久卷(PV)和持久卷声明(PVC)
  • 动态创建卷--存储类(StorageClass)
  • 部署多个实例--StatefulSet
  • 数据库访问--Headless Service
  • 主从复制--初始化容器和sidecar
  • 数据库调试--port-forward
  • 部署Mysql集群--helm

ConfigMap与Secret

ConfigMap

在Docker中,我们一般通过绑定挂载的方式将配置文件挂载到容器里。

在Kubernetes集群中,容器可能被调度到任意节点,配置文件需要能在集群任意节点上访问、分发和更新。

  1. 概述

    ConfigMap 用来在键值对数据库(etcd)中保存非加密数据。一般用来保存配置文件。

    ConfigMap 可以用作环境变量、命令行参数或者存储卷。

    ConfigMap 将环境配置信息与 容器镜像 解耦,便于配置的修改。

    ConfigMap 在设计上不是用来保存大量数据的。

    在 ConfigMap 中保存的数据不可超过 1 MiB。

    超出此限制,需要考虑挂载存储卷或者访问文件存储服务。

  2. 用法

    yaml 复制代码
    apiVersion: v1
    kind: Pod
    metadata:
      name: mysql-pod
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "123456"
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: data-volume
            - mountPath: /etc/mysql/conf.d
              name: conf-volume
              readOnly: true
      volumes:
        - name: conf-volume
          configMap:
            name: mysql-config
        - name: data-volume
          hostPath:
            # directory location on host
            path: /home/mysql/data
            # this field is optional
            type: DirectoryOrCreate
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: mysql-config
    data:
      # 类属性键;每一个键都映射到一个简单的值
      # player_initial_lives: "3"
      # ui_properties_file_name: "user-interface.properties"
    
      # 类文件键
      mysql.cnf: |
        [mysqld]
        character-set-server=utf8mb4
        collation-server=utf8mb4_general_ci
        init-connect='SET NAMES utf8mb4'
    
        [client]
        default-character-set=utf8mb4
    
        [mysql]
        default-character-set=utf8mb4

    通过kubectl edit cm mysql-config更改相关配置,可以在mysql-pod中/etc/mysql/conf.d中查看变化

Secret

  1. 概述

    Secret 用于保存机密数据的对象。一般由于保存密码、令牌或密钥等。

    data字段用来存储 base64 编码数据。

    stringData存储未编码的字符串。

    Secret 意味着你不需要在应用程序代码中包含机密数据,减少机密数据(如密码)泄露的风险。

    Secret 可以用作环境变量、命令行参数或者存储卷文件。

  2. 用法

    bash 复制代码
    echo -n '123456' | base64
    echo 'MTIzNDU2' | base64 --decode
    yaml 复制代码
    apiVersion: v1
    kind: Secret
    metadata:
      name: mysql-password
    type: Opaque
    data:
      PASSWORD: MTIzNDU2Cg==
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: mysql-pod
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-password
                  key: PASSWORD
                  optional: false # 此值为默认值;表示secret已经存在了
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: data-volume
            - mountPath: /etc/mysql/conf.d
              name: conf-volume
              readOnly: true
      volumes:
        - name: conf-volume
          configMap:
            name: mysql-config
        - name: data-volume
          hostPath:
            # directory location on host
            path: /home/mysql/data
            # this field is optional
            type: DirectoryOrCreate
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: mysql-config
    data:
      mysql.cnf: |
        [mysqld]
        character-set-server=utf8mb4
        collation-server=utf8mb4_general_ci
        init-connect='SET NAMES utf8mb4'
    
        [client]
        default-character-set=utf8mb4
    
        [mysql]
        default-character-set=utf8mb4

卷(Volume)

  1. 概述

    将数据存储在容器中,一旦容器被删除,数据也会被删除。

    卷是独立于容器之外的一块存储区域,通过**挂载(Mount)**的方式供Pod中的容器使用。

    • 使用场景
      • 卷可以在多个容器之间共享数据。
      • 卷可以将容器数据存储在外部存储或云存储上。
      • 卷更容易备份或迁移。
  2. 常见卷类型

    • 临时卷(Ephemeral Volume):与 Pod 一起创建和删除,生命周期与 Pod 相同

    • 持久卷(Persistent Volume):删除Pod后,持久卷不会被删除

    • 分布式存储 - Ceph(cephfs文件存储、rbd块存储)

    • 投射卷(Projected Volumes): projected 卷可以将多个卷映射到同一个目录上

  3. 后端存储

    一个集群中可以包含多种存储(如localNFSCeph或云存储)。

    每种存储都对应一个存储类(StorageClass) ,存储类用来创建和管理持久卷,是集群与存储服务之间的桥梁。

    管理员创建持久卷(PV )时,通过设置不同的StorageClass来创建不同类型的持久卷。

  4. 临时卷(EV)

    与 Pod 一起创建和删除,生命周期与 Pod 相同

    • emptyDir - 初始内容为空的本地临时目录

      emptyDir会创建一个初始状态为空的目录,存储空间来自本地的 kubelet 根目录或内存(需要将emptyDir.medium设置为"Memory")。

      通常使用本地临时存储来设置缓存、保存日志等。

      例如,将redis的存储目录设置为emptyDir

      yaml 复制代码
      apiVersion: v1
      kind: Pod
      metadata:
        name: redis-pod
      spec:
        containers:
        - name: redis
          image: redis
          volumeMounts:
          - name: redis-storage
            mountPath: /data/redis
        volumes:
        - name: redis-storage
          emptyDir: {}
    • configMap - 为Pod注入配置文件

    • secret - 为Pod注入加密数据

      注意:这里的configMap和secret代表的是卷的类型,不是configMap和secret对象。

      删除Pod并不会删除ConfigMap对象和secret对象。

      configMap卷和Secret卷是一种特殊类型的卷,kubelet引用configMap和Secret中定义的内容,在Pod所在节点上生成一个临时卷,将数据注入到Pod中。删除Pod,临时卷也会被删除。

      临时卷位于Pod所在节点的/var/lib/kubelet/pods目录下。

  5. 持久卷(PV)与持久卷声明(PVC)

    持久卷(PV)

    持久卷(Persistent Volume):删除Pod后,卷不会被删除

    • 本地存储

    • hostPath - 节点主机上的目录或文件(仅供单节点测试使用;多节点集群请用 local 卷代替)

      挂载hostPath

      hostPath的type值:

      DirectoryOrCreate 目录不存在则自动创建。
      Directory 挂载已存在目录。不存在会报错。
      FileOrCreate 文件不存在则自动创建。 不会自动创建文件的父目录,必须确保文件路径已经存在。
      File 挂载已存在的文件。不存在会报错。
      Socket 挂载 UNIX 套接字。例如挂载/var/run/docker.sock进程
      yaml 复制代码
      apiVersion: v1
      kind: Pod
      metadata:
        name: mysql-pod
      spec:
        containers:
          - name: mysql
            image: mysql:5.7
            env:
              - name: MYSQL_ROOT_PASSWORD
                value: "123456"
            ports:
              - containerPort: 3306
            volumeMounts:
              - mountPath: /var/lib/mysql #容器中的目录
                name: data-volume
        volumes:
          - name: data-volume
            hostPath:
              # 宿主机上目录位置
              path: /home/mysql/data
              type: DirectoryOrCreate
    • local - 节点上挂载的本地存储设备(不支持动态创建卷)

    • 网络存储:NFS - 网络文件系统 (NFS)

    • 分布式存储:Ceph(cephfs文件存储、rbd块存储)

    创建持久卷(PV)

    创建持久卷(PV)是服务端的行为,通常集群管理员会提前创建一些常用规格的持久卷以备使用。

    hostPath仅供单节点测试使用,当Pod被重新创建时,可能会被调度到与原先不同的节点上,导致新的Pod没有数据。多节点集群使用本地存储,可以使用local

    创建local类型的持久卷,需要先创建存储类(StorageClass)

    本地存储类示例

    bash 复制代码
    # 创建本地存储类
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: local-storage
    provisioner: kubernetes.io/no-provisioner
    volumeBindingMode: Immediate

    local卷不支持动态创建,必须手动创建持久卷(PV)。

    创建local类型的持久卷,必须 设置nodeAffinity(节点亲和性)。

    调度器使用nodeAffinity信息来将使用local卷的 Pod 调度到持久卷所在的节点上,不会出现Pod被调度到别的节点上的情况。

    注意:local卷也存在自身的问题,当Pod所在节点上的存储出现故障或者整个节点不可用时,Pod和卷都会失效,仍然会丢失数据,因此最安全的做法还是将数据存储到集群之外的存储或云存储上。

    创建PV PV示例/local卷示例

    yaml 复制代码
    # local-storage.yaml
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: local-pv-1
    spec:
      capacity:
        storage: 4Gi
      volumeMode: Filesystem
      accessModes:
      - ReadWriteOnce
      persistentVolumeReclaimPolicy: Delete
      storageClassName: local-storage #通过指定存储类来设置卷的类型
      local:
        path: /mnt/disks/ssd1 #手动创建
      nodeAffinity:
        required:
          nodeSelectorTerms:
          - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
              - k8s-node1

    卷的状态

    • Available(可用)-- 卷是一个空闲资源,尚未绑定到任何;
    • Bound(已绑定)-- 该卷已经绑定到某个持久卷声明上;
    • Released(已释放)-- 所绑定的声明已被删除,但是资源尚未被集群回收;
    • Failed(失败)-- 卷的自动回收操作失败。

    卷模式

    卷模式(volumeMode)是一个可选参数。

    针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):

    • Filesystem(文件系统)

    默认的卷模式。

    • Block(块)

    ​ 将卷作为原始块设备来使用。

    创建持久卷声明(PVC)

    持久卷声明(PVC)是用户端的行为,用户在创建Pod时,无法知道集群中PV的状态(名称、容量、是否可用等),用户也无需关心这些内容,只需要在声明中提出申请,集群会自动匹配符合需求的持久卷(PV)。

    Pod使用持久卷声明(PVC)作为存储卷。

    PVC示例

    yaml 复制代码
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: local-pv-claim
    spec:
      storageClassName: local-storage # 与PV中的storageClassName一致
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 2Gi

    访问模式

    • ReadWriteOnce:卷可以被一个节点以读写方式挂载,并允许同一节点上的多个 Pod 访问。
    • ReadOnlyMany:卷可以被多个节点以只读方式挂载。
    • ReadWriteMany:卷可以被多个节点以读写方式挂载
    • ReadWriteOncePod:卷可以被单个 Pod 以读写方式挂载。 集群中只有一个 Pod 可以读取或写入该 PVC。

    使用PVC作为卷

    Pod 的配置文件指定了 PersistentVolumeClaim,但没有指定 PersistentVolume。

    对 Pod 而言,PersistentVolumeClaim 就是一个存储卷。

    PVC卷示例

    yaml 复制代码
    apiVersion: v1
    kind: Pod
    metadata:
      name: mysql-pod
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "123456"
          ports:
            - containerPort: 3306
          volumeMounts:
            - mountPath: /var/lib/mysql #容器中的目录
              name: local-mysql-data
      volumes:
        - name: local-mysql-data
          persistentVolumeClaim:
            claimName: local-pv-claim

    持久卷(PV)持久卷声明(PVC)

    持久卷(PersistentVolume,PV) 是集群中的一块存储。可以理解为一块虚拟硬盘。

    持久卷可以由管理员事先创建, 或者使用存储类(Storage Class)根据用户请求来动态创建。

    持久卷属于集群的公共资源,并不属于某个namespace;


    持久卷声明(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。

    PVC声明好比申请单,它更贴近云服务的使用场景,使用资源先申请,便于统计和计费。

    Pod 将 PVC 声明当做存储卷来使用,PVC 可以请求指定容量的存储空间和访问模式 。PVC对象是带有namespace的。

  6. 存储类(StorageClass)

    一个集群可以存在多个**存储类(StorageClass)**来创建和管理不同类型的存储。

    每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件创建持久卷。 该字段必须指定。

    卷绑定模式

    volumeBindingMode用于控制什么时候动态创建卷和绑定卷

    • Immediate立即创建:创建PVC后,立即创建PV并完成绑定
    • WaitForFirstConsumer 延迟创建:当使用该PVC的 Pod 被创建时,才会自动创建PV并完成绑定

    回收策略(Reclaim Policy)

    回收策略告诉集群,当用户删除PVC 对象时, 从PVC中释放出来的PV将被如何处理。

    • 删除(Delete)

      如果没有指定,默认为Delete

      当PVC被删除时,关联的PV 对象也会被自动删除。

    • 保留(Retain)

      当 PVC 对象被删除时,PV 卷仍然存在,数据卷状态变为"已释放(Released)"。

      此时卷上仍保留有数据,该卷还不能用于其他PVC。需要手动删除PV。

StatefulSet(有状态应用集)

StatefulSet 是用来管理有状态的应用。一般用于管理数据库、缓存等。

Deployment 类似, StatefulSet用来管理 Pod 集合的部署和扩缩。

Deployment用来部署无状态应用。StatefulSet用来有状态应用。

  1. 创建StatefulSet

    StatefulSet配置模版

    yaml 复制代码
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: mysql
    spec:
      selector:
        matchLabels:
          app: mysql # 必须匹配 .spec.template.metadata.labels
      serviceName: db
      replicas: 3 # 默认值是 1
      minReadySeconds: 10 # 默认值是 0
      template:
        metadata:
          labels:
            app: mysql # 必须匹配 .spec.selector.matchLabels
        spec:
          terminationGracePeriodSeconds: 10
          containers:
            - name: mysql
              image: mysql:5.7
              env:
                - name: MYSQL_ROOT_PASSWORD
                  value: "123456"
              ports:
                - containerPort: 3306
              volumeMounts:
                - mountPath: /var/lib/mysql #容器中的目录
                  name: mysql-data
      volumeClaimTemplates:
        - metadata:
            name: mysql-data
          spec:
            accessModes:
              - ReadWriteOnce
            storageClassName: local-path
            resources:
              requests:
                storage: 2Gi
  2. 稳定的存储

    在 StatefulSet 中使用 VolumeClaimTemplate,为每个 Pod 创建持久卷声明(PVC)。

    每个 Pod 将会得到基于local-path 存储类动态创建的持久卷(PV)。 Pod 创建(或重新调度)时,会挂载与其声明相关联的持久卷。

    请注意,当 Pod 或者 StatefulSet 被删除时,持久卷声明和关联的持久卷不会被删除。

  3. Pod标识

    在具有 N 个副本的 StatefulSet中,每个 Pod 会被分配一个从 0 到 N-1 的整数序号,该序号在此 StatefulSet 上是唯一的。

    StatefulSet 中的每个 Pod 主机名的格式为StatefulSet名称-序号

    上例将会创建三个名称分别为 mysql-0、mysql-1、mysql-2 的 Pod。

Headless Service(无头服务)

之前我们创建了三个各自独立的数据库实例,mysql-0,mysql-1,mysql-2。

要想让别的容器访问数据库,我们需要将它发布为Service,但是Service带负载均衡功能,每次请求都会转发给不同的数据库,这样子使用过程中会有很大的问题。
无头服务(Headless Service)可以为 StatefulSet 成员提供稳定的 DNS 地址。

在不需要负载均衡的情况下,可以通过指定 Cluster IP的值为 "None" 来创建无头服务。
注意: StatefulSet中的ServiceName必须要跟Service中的metadata.name一致

yaml 复制代码
# 为 StatefulSet 成员提供稳定的 DNS 表项的无头服务(Headless Service)
apiVersion: v1
kind: Service
metadata:
  #重要!这里的名字要跟后面StatefulSet里ServiceName一致
  name: db
  labels:
    app: database
spec:
  ports:
  - name: mysql
    port: 3306
  # 设置Headless Service
  clusterIP: None
  selector:
    app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql # 必须匹配 .spec.template.metadata.labels
  serviceName: db  #重要!这里的名字要跟Service中metadata.name匹配
  replicas: 3 # 默认值是 1
  minReadySeconds: 10 # 默认值是 0
  template:
    metadata:
      labels:
        app: mysql # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "123456"
          ports:
            - containerPort: 3306
          volumeMounts:
            - mountPath: /var/lib/mysql
              name: mysql-data
  volumeClaimTemplates:
    - metadata:
        name: mysql-data
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: local-path
        resources:
          requests:
            storage: 2Gi

稳定的网络ID

StatefulSet 中的每个 Pod 都会被分配一个StatefulSet名称-序号格式的主机名。

集群内置的DNS会为Service分配一个内部域名db.default.svc.cluster.local,它的格式为 服务名称.命名空间.svc.cluster.local

Mysql主从复制

下面是部署一个读写分离Mysql数据库的示意图。

通过部署无头服务(Headless Service)将写操作指向固定的数据库。

部署一个Service用来做读操作的负载均衡。

数据库之间通过同步程序保持数据一致。

初始化容器(init containers)

初始化容器(Init Containers)是一种特殊容器,它在 Pod 内的应用容器启动之前运行。

初始化容器未执行完毕或以错误状态退出,Pod内的应用容器不会启动。

初始化容器需要在initContainers中定义,与containers同级。

基于上面的特性,初始化容器通常用于

  • 生成配置文件
  • 执行初始化命令或脚本
  • 执行健康检查(检查依赖的服务是否处于Ready或健康Health的状态)

在本例子中,有两个初始化容器。

  • init-mysql为MySQL实例分配server-id,并将mysql-0的配置文件设置为primary.cnf,其他副本设置为replica.cnf
  • clone-mysql从前一个Pod中获取备份的数据文件放到自己的数据目录下

边车Sidecar

Pod中运行了2个容器,MySQL 容器和一个充当辅助工具的 xtrabackup 容器,我们称之为边车(sidecar)。

sidecar容器负责将备份的数据文件发送给下一个Pod,并在副本服务器初次启动时,使用数据文件完成数据的导入。

MySQL使用bin-log同步数据,但是,当数据库运行一段时间后,产生了一些数据,这时候如果我们进行扩容,创建了一个新的副本,有可能追溯不到bin-log的源头(可能被手动清理或者过期自动删除),因此需要将现有的数据导入到副本之后,再开启数据同步,sidecar只负责数据库初次启动时完成历史数据导入,后续的数据MySQL会自动同步。

Port-forward端口转发

通常,集群中的数据库不直接对外访问。

但是,有时候我们需要图形化工具连接到数据库进行操作或者调试。

我们可以使用端口转发来访问集群中的应用。

kubectl port-forward可以将本地端口的连接转发给容器。

此命令在前台运行,命令终止后,转发会话将结束。

bash 复制代码
#主机端口在前,容器端口在后
#如果主机有多个IP,需要指定IP,如不指定IP,默认为127.0.0.1
kubectl port-forward pods/mysql-0 --address=192.168.56.109 33060:3306

Helm

Helm 是一个 Kubernetes 应用的包管理工具,类似于 Ubuntu 的 APT 和 CentOS 中的 YUM。

Helm使用chart 来封装kubernetes应用的 YAML 文件,我们只需要设置自己的参数,就可以实现自动化的快速部署应用。

  1. 安装Helm

    下载安装包:

    https://github.com/helm/helm/releases

    https://get.helm.sh/helm-v3.10.0-linux-amd64.tar.gz

    bash 复制代码
    mv linux-amd64/helm /usr/local/bin/helm
  2. 三大概念

    • Chart 代表着 Helm 包。

      • 它包含运行应用程序需要的所有资源定义和依赖,相当于模版。
      • 类似于maven中的pom.xml、Apt中的dpkb或 Yum中的RPM
    • Repository(仓库) 用来存放和共享 charts。

      • 不用的应用放在不同的仓库中。
    • Release 是运行 chart 的实例。

    一个 chart 通常可以在同一个集群中安装多次。

    每一次安装都会创建一个新的 release,**release name**不能重复。

  3. Helm仓库

    Helm有一个跟docker Hub类似的应用中心(https://artifacthub.io/),我们可以在里面找到我们需要部署的应用。

  4. 安装单节点Mysql

    bash 复制代码
    #添加仓库
    helm repo add bitnami https://charts.bitnami.com/bitnami
    #查看chart
    helm show chart bitnami/mysql 
    #查看默认值
    helm show values bitnami/mysql 
    
    #安装mysql
    helm install my-mysql \
    --set-string auth.rootPassword="123456" \
    --set primary.persistence.size=2Gi \
    bitnami/mysql
    
    #查看设置
    helm get values my-mysql
    #删除mysql
    helm delete my-release
  5. Helm部署MySQL集群

    安装过程中有两种方式传递配置数据:

    ●-f (或--values):使用 YAML 文件覆盖默认配置。可以指定多次,优先使用最右边的文件。

    ●--set:通过命令行的方式对指定项进行覆盖。

    如果同时使用两种方式,则 --set中的值会被合并到 -f中,但是 --set中的值优先级更高。

    yaml 复制代码
    # values.yaml
    auth:
      rootPassword: "123456"
    
    primary:
      persistence:
        size: 2Gi
        enabled: true
    
    secondary:
      replicaCount: 2
      persistence:
        size: 2Gi
        enabled: true
    
    architecture: replication
    bash 复制代码
    helm install my-db -f values.yaml bitnami/mysql

参考

https://www.yuque.com/wukong-zorrm/qdoy5p/zrvene

https://kubernetes.io/zh-cn/docs/concepts

相关推荐
Patrick_Wilson9 小时前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
探索云原生16 小时前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
云恒要逆袭18 小时前
运行你的第一个Docker容器
后端·docker·容器
Java之美2 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
程序员老赵2 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程
武子康5 天前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple
通信小呆呆8 天前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人
H__Rick8 天前
自动对焦学习-3
人工智能·学习·计算机视觉
Daisy Lee8 天前
量化学习-第1章-什么是量化金融
学习·金融·datawhale