K8S学习-第2部分-创建MySQL数据库

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

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

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

目录

创建MySQL数据库

ConfigMap与Secret

ConfigMap

1.ConfigMap用法

Secret

Secret用法

卷(Volume)

1.临时卷(EV)

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

创建持久卷声明(PVC)

使用PVC作为卷

绑定

存储类(StorageClass)

StatefulSet(有状态应用集)

稳定的存储

Pod 标识

部署和扩缩保证


创建MySQL数据库

●配置环境变量
○使用MySQL镜像创建Pod,需要使用环境变量设置MySQL的初始密码。
环境变量配置示例

●挂载卷
○将数据存储在容器中,一旦容器被删除,数据也会被删除。
○将数据存储到卷(Volume)中,删除容器时,卷不会被删除。
hostPath卷
hostPath 卷将主机节点上的文件或目录挂载到 Pod 中。
hostPath配置示例
hostPath的type值:

DirectoryOrCreate

目录不存在则自动创建。

Directory

挂载已存在目录。不存在会报错。

FileOrCreate

文件不存在则自动创建。
不会自动创建文件的父目录,必须确保文件路径已经存在。

File

挂载已存在的文件。不存在会报错。

Socket

挂载 UNIX 套接字。例如挂载/var/run/docker.sock进程

# 创建mysql目录
mkdir /home/mysql

# 创建mysql-pod.yaml
cd /home/mysql
vi mysql-pod.yaml

mysql-pod.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

注意:hostPath 仅用于在单节点集群上进行开发和测试,不适用于多节点集群;

例如,当Pod被重新创建时,可能会被调度到与原先不同的节点上,导致新的Pod没有数据。

在多节点集群使用本地存储,可以使用local卷。

使用 kubectl apply -f mysql-pod.yaml 再 kubectl get pod -owide 查看是否创建成功

ConfigMap与Secret

在Docker中,我们一般通过绑定挂载的方式将配置文件挂载到容器里。
在Kubernetes集群中,容器可能被调度到任意节点,配置文件需要能在集群任意节点上访问、分发和更新。

ConfigMap

ConfigMap 用来在键值对数据库(etcd)中保存非加密数据。一般用来保存配置文件。
ConfigMap 可以用作环境变量、命令行参数或者存储卷。
ConfigMap 将环境配置信息与 容器镜像 解耦,便于配置的修改。
ConfigMap 在设计上不是用来保存大量数据的。
在 ConfigMap 中保存的数据不可超过 1 MiB。
超出此限制,需要考虑挂载存储卷或者访问文件存储服务。


1.ConfigMap用法

ConfigMap配置示例
Pod中使用ConfigMap

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:
  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
# 修改configMap,配置文件会被自动更新
kubectl edit cm mysql-config

Secret

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

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

stringData存储未编码的字符串。

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

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


Secret用法

# 加密
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

环境变量中使用secret并且secret修改后 环境变量不会自动更新 需要重启pod

卷(Volume)

将数据存储在容器中,一旦容器被删除,数据也会被删除。
卷是独立于容器之外的一块存储区域,通过挂载(Mount)的方式供Pod中的容器使用。


●使用场景
        ○卷可以在多个容器之间共享数据。
        ○卷可以将容器数据存储在外部存储或云存储上。
        ○卷更容易备份或迁移。


常见的卷类型
●临时卷(Ephemeral Volume):与 Pod 一起创建和删除,生命周期与 Pod 相同
        ○emptyDir - 作为缓存或存储日志
        ○configMapsecretdownwardAPI - 给Pod注入数据
●持久卷(Persistent Volume):删除Pod后,持久卷不会被删除
        ○本地存储 - hostPathlocal
        ○网络存储 - NFS
        ○分布式存储 - Ceph(cephfs文件存储、rbd块存储)
●投射卷(Projected Volumes):projected 卷可以将多个卷映射到同一个目录上


后端存储
一个集群中可以包含多种存储(如local、NFS、Ceph或云存储)。
每种存储都对应一个存储类(StorageClass) ,存储类用来创建和管理持久卷,是集群与存储服务之间的桥梁。
管理员创建持久卷(PV)时,通过设置不同的StorageClass来创建不同类型的持久卷。

1.临时卷(EV)

●临时卷(Ephemeral Volume)
○与 Pod 一起创建和删除,生命周期与 Pod 相同
emptyDir - 初始内容为空的本地临时目录
configMap - 为Pod注入配置文件
secret - 为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卷和secret卷
注意:这里的configMap和secret代表的是卷的类型,不是configMap和secret对象。
删除Pod并不会删除ConfigMap对象和secret对象。


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

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

●持久卷(Persistent Volume):删除Pod后,卷不会被删除
○本地存储
        ■hostPath - 节点主机上的目录或文件
        (仅供单节点测试使用;多节点集群请用 local 卷代替)
        ■local - 节点上挂载的本地存储设备(不支持动态创建卷)
○网络存储
        ■NFS - 网络文件系统 (NFS)
○分布式存储
        ■Ceph(cephfs文件存储、rbd块存储)

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

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

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

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

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

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

创建持久卷(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
  # 节点亲和性 避免pod和存储调度到不同节点
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - k8s-worker1

去worker1节点创建 /mnt/disks/ssd1 目录

主节点使用 kubectl apply -f local-storage.yaml

使用 kubectl get pv前后对比

创建持久卷声明(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: 3Gi

使用PVC作为卷

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

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

PVC卷示例

修改mysql-pod.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
      # hostpath 改为下面的
      persistentVolumeClaim:
        claimName: local-pv-claim

重新创建mysql-pod 使用kubectl describe pod mysql-pod查看持久卷声明

绑定

创建持久卷声明(PVC)之后,集群会查找满足要求的持久卷(PV),将 PVC 绑定到该 PV上。

PVC与PV之间的绑定是一对一的映射关系,绑定具有排他性,一旦绑定关系建立,该PV无法被其他PVC使用。

PVC可能会匹配到比声明容量大的持久卷,但是不会匹配比声明容量小的持久卷。

例如,即使集群上存在多个 50 G大小的 PV ,他们加起来的容量大于100G,也无法匹配100 G大小的 PVC。

找不到满足要求的 PV ,PVC会无限期地处于未绑定状态(Pending) , 直到出现了满足要求的 PV时,PVC才会被绑定。

访问模式

  • ReadWriteOnce
    • 卷可以被一个节点以读写方式挂载,并允许同一节点上的多个 Pod 访问。
  • ReadOnlyMany
    • 卷可以被多个节点以只读方式挂载。
  • ReadWriteMany
    • 卷可以被多个节点以读写方式挂载。
  • ReadWriteOncePod
    • 卷可以被单个 Pod 以读写方式挂载。 集群中只有一个 Pod 可以读取或写入该 PVC。
    • 只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。

卷的状态

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

卷模式

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

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

  • Filesystem(文件系统)

默认的卷模式。

  • Block(块)

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

存储类(StorageClass)

创建持久卷(PV)
●静态创建
        ○管理员预先手动创建
        ○手动创建麻烦、不够灵活(local卷不支持动态创建,必须手动创建PV)
        ○资源浪费(例如一个PVC可能匹配到比声明容量大的卷)
        ○对自动化工具不够友好
●动态创建
        ○根据用户请求按需创建持久卷,在用户请求时自动创建
        ○动态创建需要使用存储类(StorageClass)
        ○用户需要在持久卷声明(PVC)中指定存储类来自动创建声明中的卷。
        ○如果没有指定存储类,使用集群中默认的存储类。
存储类(StorageClass)
一个集群可以存在多个存储类(StorageClass)来创建和管理不同类型的存储。
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件创建持久卷。 该字段必须指定。


Local Path Provisioner


K3s自带了一个名为local-path的存储类(StorageClass),它支持动态创建基于hostPath或local的持久卷。
创建PVC后,会自动创建PV,不需要再去手动的创建PV。
删除PVC,PV也会被自动删除。

创建local-path-pvc.yaml文件

当文件中内容为

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-pvc-1
spec:
  storageClassName: local-path # 与PV中的storageClassName一致
  # 访问模式
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

kubectl apply -f  ocal-path-pvc.yaml时kubectl get pvc 状态为pending, kubectl get pv 为空

 这是因为local-path的卷的绑定模式为WaitForFirstConsumer只有被挂载到pod中创建pod时才会被创建

卷绑定模式
volumeBindingMode用于控制什么时候动态创建卷和绑定卷。
  ●Immediate立即创建
创建PVC后,立即创建PV并完成绑定。
  ●WaitForFirstConsumer 延迟创建
当使用该PVC的 Pod 被创建时,才会自动创建PV并完成绑定。


将mysql-pod.yaml里的 volumes下持久卷声明改为刚才创建的lcoal-pvc-1

  volumes:
    - name: conf-volume
      configMap:
        name: mysql-config
    - name: data-volume
      persistentVolumeClaim: 
        # 将下面改为刚刚创建的local-pvc-1
        claimName: local-pvc-1

 再次创建mysql-pod查看pvc与pv 自动创建了pv绑定成功

 注意删除pod后pvc与pv不会自动删除 当手动删除pvc后pv会自动释放删除(默认情况下删除声明也会自动删除声明关联的持久卷, 可以在回收策略中设置是否自动删除)

回收策略(Reclaim Policy)

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

  • 删除(Delete)

如果没有指定,默认为Delete

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

  • 保留(Retain)

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

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

StatefulSet(有状态应用集)

StatefulSet
如果我们需要部署多个MySQL实例,就需要用到StatefulSet。
StatefulSet 是用来管理有状态的应用。一般用于管理数据库、缓存等。
Deployment 类似, StatefulSet用来管理 Pod 集合的部署和扩缩。
Deployment用来部署无状态应用。StatefulSet用来有状态应用。
创建StatefulSet
StatefulSet配置模版c

创建statefulset.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-sts
spec:
  selector:
    matchLabels:
      app: mysql # 必须匹配 .spec.template.metadata.labels
  serviceName: mysql-svc
  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: data-volume
  volumeClaimTemplates:
    - metadata:
        name: data-volume
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: local-path
        resources:
          requests:
            storage: 2Gi

用两个窗口查看pod创建过程statefulset是依次创建的只有当第一个创建完毕才会创建第二个,删除和缩容则是逆序进行

稳定的存储

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

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

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


Pod 标识

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

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

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


部署和扩缩保证

  • 对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0..N-1。
  • 当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0。
  • 在将扩缩操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
  • 在一个 Pod 终止之前,所有的继任者必须完全关闭。

在上面的mysql示例被创建后,会按照 mysql-0、mysql-1、mysql-2 的顺序部署三个 Pod。

在 mysql-0 进入 Running 和 Ready 状态前不会部署 mysql-1。

在 mysql-1 进入 Running 和 Ready 状态前不会部署 mysql-2。

如果 mysql-1 已经处于 Running 和 Ready 状态,而 mysql-2 尚未部署,在此期间发生了 mysql-0 运行失败,那么 mysql-2 将不会被部署,要等到 mysql-0 部署完成并进入 Running 和 Ready 状态后,才会部署 mysql-2。

如果用户想将示例中的 StatefulSet 扩缩为 replicas=1,首先被终止的是 mysql-2。

在 mysql-2 没有被完全停止和删除前,mysql-1 不会被终止。

当 mysql-2 已被终止和删除、mysql-1 尚未被终止,如果在此期间发生 mysql-0 运行失败, 那么就不会终止 mysql-1,必须等到 mysql-0 进入 Running 和 Ready 状态后才会终止 web-1。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值