Kubernetes入门学习(下)
文章目录
- Kubernetes入门学习(下)
- 运行有状态的应用
- ConfigMap与Secret
- ConfigMap
- Secret
- 卷(Volume)
- StatefulSet(有状态应用集)
- Headless Service(无头服务)
- Mysql主从复制
- Port-forward端口转发
- Helm
- 参考
运行有状态的应用
我们以MySQL数据库为例,在kubernetes集群中运行一个有状态的应用。
部署数据库几乎覆盖了kubernetes中常见的对象和概念:
- 配置文件–ConfigMap
- 保存密码–Secret
- 数据存储–持久卷(PV)和持久卷声明(PVC)
- 动态创建卷–存储类(StorageClass)
- 部署多个实例–StatefulSet
- 数据库访问–Headless Service
- 主从复制–初始化容器和sidecar
- 数据库调试–port-forward
- 部署Mysql集群–helm
ConfigMap与Secret
ConfigMap
在Docker中,我们一般通过绑定挂载的方式将配置文件挂载到容器里。
在Kubernetes集群中,容器可能被调度到任意节点,配置文件需要能在集群任意节点上访问、分发和更新。
-
概述
ConfigMap 用来在键值对数据库(etcd)中保存非加密数据。一般用来保存配置文件。
ConfigMap 可以用作环境变量、命令行参数或者存储卷。
ConfigMap 将环境配置信息与 容器镜像 解耦,便于配置的修改。
ConfigMap 在设计上不是用来保存大量数据的。
在 ConfigMap 中保存的数据不可超过 1 MiB。
超出此限制,需要考虑挂载存储卷或者访问文件存储服务。
-
用法
- 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: # 类属性键;每一个键都映射到一个简单的值 # 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
-
概述
Secret 用于保存机密数据的对象。一般由于保存密码、令牌或密钥等。
data
字段用来存储 base64 编码数据。stringData
存储未编码的字符串。Secret 意味着你不需要在应用程序代码中包含机密数据,减少机密数据(如密码)泄露的风险。
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
卷(Volume)
-
概述
将数据存储在容器中,一旦容器被删除,数据也会被删除。
卷是独立于容器之外的一块存储区域,通过**挂载(Mount)**的方式供Pod中的容器使用。
- 使用场景
- 卷可以在多个容器之间共享数据。
- 卷可以将容器数据存储在外部存储或云存储上。
- 卷更容易备份或迁移。
- 使用场景
-
常见卷类型
-
临时卷(Ephemeral Volume):与 Pod 一起创建和删除,生命周期与 Pod 相同
- emptyDir - 作为缓存或存储日志
- configMap 、secret、 downwardAPI - 给Pod注入数据
-
持久卷(Persistent Volume):删除Pod后,持久卷不会被删除
- 本地存储 - hostPath、 local
- 网络存储 - NFS
-
分布式存储 - Ceph(cephfs文件存储、rbd块存储)
-
投射卷(Projected Volumes):projected 卷可以将多个卷映射到同一个目录上
-
-
后端存储
一个集群中可以包含多种存储(如
local
、NFS
、Ceph
或云存储)。每种存储都对应一个存储类(StorageClass) ,存储类用来创建和管理持久卷,是集群与存储服务之间的桥梁。
管理员创建持久卷(PV)时,通过设置不同的StorageClass来创建不同类型的持久卷。
-
临时卷(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对象。
configMap卷和Secret卷是一种特殊类型的卷,kubelet引用configMap和Secret中定义的内容,在Pod所在节点上生成一个临时卷,将数据注入到Pod中。删除Pod,临时卷也会被删除。
临时卷位于Pod所在节点的
/var/lib/kubelet/pods
目录下。
-
-
持久卷(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
的。 -
-
存储类(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用来有状态应用。
-
创建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
-
稳定的存储
在 StatefulSet 中使用 VolumeClaimTemplate,为每个 Pod 创建持久卷声明(PVC)。
每个 Pod 将会得到基于local-path 存储类动态创建的持久卷(PV)。 Pod 创建(或重新调度)时,会挂载与其声明相关联的持久卷。
请注意,当 Pod 或者 StatefulSet 被删除时,持久卷声明和关联的持久卷不会被删除。 -
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用来做读操作的负载均衡。
数据库之间通过同步程序保持数据一致。
初始化容器(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
可以将本地端口的连接转发给容器。
此命令在前台运行,命令终止后,转发会话将结束。
#主机端口在前,容器端口在后
#如果主机有多个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 文件,我们只需要设置自己的参数,就可以实现自动化的快速部署应用。
-
安装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
-
三大概念
-
Chart 代表着 Helm 包。
- 它包含运行应用程序需要的所有资源定义和依赖,相当于模版。
- 类似于maven中的
pom.xml
、Apt中的dpkb
或 Yum中的RPM
。
-
Repository(仓库) 用来存放和共享 charts。
- 不用的应用放在不同的仓库中。
-
Release 是运行 chart 的实例。
一个 chart 通常可以在同一个集群中安装多次。
每一次安装都会创建一个新的 release,
**release name**
不能重复。 -
-
Helm仓库
Helm有一个跟docker Hub类似的应用中心(https://artifacthub.io/),我们可以在里面找到我们需要部署的应用。
-
安装单节点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
-
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