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. 用法

    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. 用法

    echo -n '123456' | base64
    echo 'MTIzNDU2' | base64 --decode
    
    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

      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对象。

      image.png

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

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

      image.png

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

    持久卷(PV)

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

    • 本地存储

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

      挂载hostPath

      hostPath的type值:

      DirectoryOrCreate目录不存在则自动创建。
      Directory挂载已存在目录。不存在会报错。
      FileOrCreate文件不存在则自动创建。 不会自动创建文件的父目录,必须确保文件路径已经存在。
      File挂载已存在的文件。不存在会报错。
      Socket挂载 UNIX 套接字。例如挂载/var/run/docker.sock进程
      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)

    本地存储类示例

    # 创建本地存储类
    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卷示例

    # 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示例

    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卷示例

    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的。

    image.png

  6. 存储类(StorageClass)

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

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

    image.png

    卷绑定模式

    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配置模版

    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一致

# 为 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用来做读操作的负载均衡。

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

image.png

image.png

初始化容器(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)。

image.png

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

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

Port-forward端口转发

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

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

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

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

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

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

img

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

    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

    #添加仓库
    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中的值优先级更高。

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

参考

https://www.yuque.com/wukong-zorrm/qdoy5p/zrvene
https://kubernetes.io/zh-cn/docs/concepts

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值