Pod控制器
Pod控制器:
RC、RS控制器的核心:
- 期望的副本数(replicas)
- 标签选择器(selector)
- pod模板(template)
注意:
控制器本身有一个spec配置,而template是为控制器提供的模板pod(创建多个副本时,直接获取模板的信息生成)
发布方式控制:
RS默认使用的滚动发布,但RS自身只能手动控制,要自动实现需要Deployment控制器
手动停一个,再创建一个,重复操作是滚动
手动停一个,再创建一个,后续不动是金丝雀
手动创建一批,再把service转向新RS,是蓝绿
RS控制器:
RC的升级版
RC、RS已过时
kubectl explain rs.spec
例1: 用rs创建pod
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-xx #控制器的name
spec:
replicas: 2
selector:
matchLabels: #匹配的pod标签
app: rs-t1
xxx: qqqq
template:
metadata:
name: rs-t1-tmp
labels: #pod模板的标签
app: rs-t1
xxx: qqqq
spec:
containers:
- name: ngx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
Deployment控制器:
调用RS,基于RS控制pod,并提供额外功能。pod对象本身支持的方法,deploy都支持
只能用于无状态的应用,如果是mysql的主从、redis集群就不行
语法格式:
kubectl explain deploy.spce #查看详细语法
spce:
replicas: <ini> #pod创建的副本数
minReadySeconds: int #新建pod认为准备就绪的秒数,默认0秒,认为创建完成即准备就绪
paused: 布尔值 #暂停部署,默认false。如果指定true,则任务将不会执行,并且创建的任务将不会被视为任务的生成或被评估。如果指定任何其他值,则将导致任务被暂停。
selector: #标签选择器,部署时,deployment控制器会根据此标签来部署
matchLabels: <map[string]string> #匹配标签条件,部分版本控制器会使用matchExpressions条
标签: 值 #匹配的标签
matchExpressions: <[]Object>
key: <string> -required-
operator: <string> -required-
#In
#NotIn
#Exists
#DoesNotExist
values: <[]string>
template: #发布模板
metadata: #定义模板元数据,标签与选择器写的标签对应,否则没有pod控制器来创建这个模板
标签: 值 #对应标签选择器的内容
spec: #定义pod相关内容
containers: #容器相关配置,大部分与pod相同,是直接调用的pod控制器本身
env: #环境变量相关
strategy <Object> #自动更新策略
type: 策略 #更新策略
#Recreate,重建更新(删一个建一个)
#RollingUpdate,滚动更新
rollingUpdate <Object> #控制滚动更新的粒度,仅滚动更新使用
maxSurge #更新时,最多能超出定义的副本数几个,取值是:int、百分比。最多和最小只能一个为0,默认25%
maxUnavailable #更新时,最多有几个不可用(删除几个),默认25%
revisionHistoryLimit <int> #保留几个历史版本,默认10个
progressDeadlineSeconds: int
案例:
例1: deploy创建pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: ngx-dep
lables:
app: ngx-dep
spec:
replicas: 3
selector:
matchLabels:
app: ngx
template:
metadata:
labels:
app: ngx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
env:
- name: "passwd"
value: "123456"
- name: "xxx"
value: "123456"
resources:
requests:
cpu: "100m"
memory: "100Mi"
limits:
cpu: "0.3"
memory: "100Mi"
DaemonSet
用于系统级的pod控制器,如logstash,保证每个node上运行一个收集器
用于控制无状态的应用,且必须是守护进程方式运行,任何时刻多无需停止
常用于运行:
logstash
filebeat
普罗米休斯
语法格式:
kubectl explain daemonset.spec
spec: #参数与Deployment大部分相同
updateStrategy #设置滚动更新
type: 更新策略
#RollingUpdate,滚动更新,默认
#OnDelete,删除时更新,等于重建更新
rollingUpdate <Object> #滚动更新粒度
maxUnavailable #最多不可以
案例:
例1: 运行filebeat
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: filebeat-ds
spec:
selector:
matchLabels:
app: filebeat
template:
metadata:
labels:
app: filebeat
spec:
containers:
- name: filebeat
image: elastic/filebeat:7.16.0
imagePullPolicy: IfNotPresent
Job
用于启动一次性的任务
Cronjob
StatefulSet
有状态应用控制器,用于管理有状态应用,如redis集群、zk集群、mysql主从等
有状态应用运行要求:
- 稳定且唯一的网络标识符
- 稳定且持久的存储
- 有序、平滑的部署和扩展
- 有序、平滑的终止和删除
- 有序的滚动更新
组件:
- headless service(无头服务)
如redis集群的每个节点名称都是唯一的,所以需要用无头服务,访问时直接请求pod - statefulset(控制器)
deployment控制器创建pod时,每个pod名称都是随机生成的,这不符合redis集群节点的唯一性,所以需要把每个pod名称固定 - volumeClaimtemplate(存储卷申请模板)
如redis集群,每个节点的槽位不一样,数据也不一样,所以不能使用同一个存储卷,需要每个节点单独使用一个存储卷,创建时只能用一个存储模板,每个pod启动时单独申请一个pvc和pv
pvc模板的保留策略
默认情况下,sts创建的pod在删除时,模板创建的对应pvc不会自动删除,而是保留,需要手动删除或helm工具管理
sts控制器对pvc的策略有2种情况:
- whenDeleted(删除sts时),控制器删除
- whenScaled(缩容sts时),pod删除
4种策略: #目前是Alpha特性
- whenDeleted、**whenScaled **都是:Retain
- 默认的保留策略。它适用于 StatefulSet 卷上的数据是不可替代的且只能手动删除的情况
- whenDeleted 是 Delete ,whenScaled 是 Retain
- sts删除时pvc删除,缩容时会保留,扩容时,可以从前一个被删除副本重新连接所有数据
- whenDeleted、whenScaled 都是: Delete
- 缩容时,pvc立即删除;但更新调度、pod转移到其他节点而不是缩容时,pvc会保留继续使用。当创建一个新的副本时, 来自以前副本的任何数据都不可用。适用于数据生命周期短于副本生命周期的情况
- whenDeleted 是 Retain ,whenScaled 是 Delete
- sts删除时保留,缩容时删除。如Elasticsearch集群,减少副本时,pod的数据不再需要,pvc因此可以删除。但临时关闭Elasticsearch 集群进行维护时数据是希望保留,以供下次启动继续使用。 如果需要使 Elasticsearch 系统脱机,可重新创建sts控制器来恢复 Elasticsearch 集群。 保存 Elasticsearch 数据的 PVCs 不会被删除,新的副本将自动使用它们
语法格式:
kubectl explain sts.spec
spec:
replicas #创建pod数量
selector #pod标签选择器
serviceName #无头svc的名称
template #定义pod模板
updateStrategy #更新策略
rollingUpdate #自定义更新策略
partition: 数量 #手动指定更新pod数量,如总共5个pod,0-4下标,指定分区=4,则更新下标是4的pod,等于灰度。默认是0,也就是全部
volumeClaimTemplates: <[]Object> #pvc自动申请模板
- metadata:
name
spec:
accessModes: 读写权限
resources:
requests:
storage: 大小
podManagementPolicy: 策略 #创建、缩减pod时的策略
#OrderedReady,默认策略,按顺序创建,顺序删除
#Parallel,平行创建无先后顺序,缩减时全部删除
revisionHistoryLimit: int #pod修改的历史记录数,默认记录10个历史版本
persistentVolumeClaimRetentionPolicy: #pvc的保留策略,1.22版本开始的alpha特性,需手动为api-server传递选项--feature-gates=StatefulSetAutoDeletePVC=true 开启
whenDeleted: 策略
#Retain,默认保留
#Delete
whenScaled: 策略
#Retain,默认保留
#Delete
案例:
例1: sts创建ngx,做扩容,缩容
1)准备nfs
mkdir -p /data/nfs/v{1..3}
vim /etc/exports
/data/xx/v1 2.2.0.0/16(rw,no_root_squash)
/data/xx/v2 2.2.0.0/16(rw,no_root_squash)
/data/xx/v3 2.2.0.0/16(rw,no_root_squash)
exportfs -av
2)2创建pv
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01
spec:
nfs:
path: /data/nfs/v1
server: 2.2.2.30
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv02
spec:
nfs:
path: /data/nfs/v2
server: 2.2.2.30
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv03
spec:
nfs:
path: /data/nfs/v3
server: 2.2.2.30
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 1Gi
3)使用statefulset创建pod
apiVersion: v1
kind: Service
metadata:
name: ngx-svc
spec:
clusterIP: None
ports:
- port: 80
name: web
selector:
app: ngx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ngx-sts
spec:
replicas: 2
selector:
matchLabels:
app: ngx
serviceName: ngx-svc
template:
metadata:
labels:
app: ngx
spec:
containers:
- name: ngx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: html
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
persistentVolumeClaimRetentionPolicy:
whenDeleted: Delete
whenScaled: Delete
4)测试扩容、缩容
#扩容
kubectl scale sts --replicas=3 ngx-sts
#缩容
kubectl patch sts ngx-sts -p '{"spec":{"replicas":2}}'
kubectl get po
例2: 指定sts更新策略,实现灰度发布
1)先实现金丝雀更新
#指定副本更新,从最后一个更新到第一个
kubectl patch sts ngx-sts -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
#更新
kubectl set image statfulset ngx-sts ngx=nginx:1.18
2)查看更新变化
#查看pod的镜像版本变更,只是ngx-sts-2变化
kubectl get pod ngx-sts-2 -o custom-columns=CONTAINER:.spec.containers[0].name,IMAGE:.spec.containers[0].image
#第一个pod的版本还没有更新
kubectl get pod ngx-sts-0 -o custom-columns=CONTAINER:.spec.containers[0].name,IMAGE:.spec.containers[0].image
3)再全部发布
kubectl patch sts ngx-sts -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'
例3: 实现mysql的主从复制
生成配置
在启动 Pod 规约中的任何容器之前,Pod 首先按顺序运行所有的初始化容器
第一个名为 `init-mysql` 的初始化容器根据序号索引生成特殊的 MySQL 配置文件。
该脚本通过从 Pod 名称的末尾提取索引来确定自己的序号索引,而 Pod 名称由 `hostname` 命令返回。 然后将序数(带有数字偏移量以避免保留值)保存到 MySQL `conf.d` 目录中的文件 `server-id.cnf`。 这一操作将 StatefulSet 所提供的唯一、稳定的标识转换为 MySQL 服务器 ID
将内容复制到conf.d中,`init-mysql` 容器中的脚本也可以应用 ConfigMap 中的 `primary.cnf` 或 `replica.cnf`。 由于示例部署结构由单个 MySQL 主节点和任意数量的副本节点组成, 因此脚本仅将序数 `0` 指定为主节点,而将其他所有节点指定为副本节点。
与 StatefulSet 控制器的部署顺序保证相结合, 可以确保 MySQL 主服务器在创建副本服务器之前已准备就绪,以便它们可以开始复制。
克隆现有数据
通常,当新 Pod 作为副本节点加入集合时,必须假定 MySQL 主节点可能已经有数据。 还必须假设复制日志可能不会一直追溯到时间的开始。
这些保守的假设是允许正在运行的 StatefulSet 随时间扩大和缩小而不是固定在其初始大小的关键。
第二个名为 `clone-mysql` 的初始化容器,第一次在带有空 PersistentVolume 的副本 Pod 上启动时,会在从属 Pod 上执行克隆操作。 这意味着它将从另一个运行中的 Pod 复制所有现有数据,使此其本地状态足够一致, 从而可以开始从主服务器复制。
MySQL 本身不提供执行此操作的机制,因此本示例使用了一种流行的开源工具 Percona XtraBackup。 在克隆期间,源 MySQL 服务器性能可能会受到影响。 为了最大程度地减少对 MySQL 主服务器的影响,该脚本指示每个 Pod 从序号较低的 Pod 中克隆。 可以这样做的原因是 StatefulSet 控制器始终确保在启动 Pod `N + 1` 之前 Pod `N` 已准备就绪。
开始复制
初始化容器成功完成后,应用容器将运行。MySQL Pod 由运行实际 `mysqld` 服务的 `mysql` 容器和充当辅助工具的xtrabackup 容器组成。
`xtrabackup` sidecar 容器查看克隆的数据文件,并确定是否有必要在副本服务器上初始化 MySQL 复制。 如果是这样,它将等待 `mysqld` 准备就绪,然后使用从 XtraBackup 克隆文件中提取的复制参数执行 `CHANGE MASTER TO` 和 `START SLAVE` 命令。
一旦副本服务器开始复制后,它会记住其 MySQL 主服务器,并且如果服务器重新启动或连接中断也会自动重新连接。 另外,因为副本服务器会以其稳定的 DNS 名称查找主服务器(`mysql-0.mysql`), 即使由于重新调度而获得新的 Pod IP,它们也会自动找到主服务器。
最后,开始复制后,`xtrabackup` 容器监听来自其他 Pod 的连接,处理其数据克隆请求。 如果 StatefulSet 扩大规模,或者下一个 Pod 失去其 PersistentVolumeClaim 并需要重新克隆, 则此服务器将无限期保持运行
1)准备nfs
mkdir -p /data/nfs/sql-{1..3}
exportfs -av
2)创建pv
apiVersion: v1
kind: PersistentVolume
metadata:
name: sql-1
spec:
nfs:
path: /data/nfs/sql-1
server: 2.2.2.30
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: sql-2
spec:
nfs:
path: /data/nfs/sql-2
server: 2.2.2.30
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: sql-3
spec:
nfs:
path: /data/nfs/sql-3
server: 2.2.2.30
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity:
storage: 10Gi
3)创建cm
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
app.kubernetes.io/name: mysql
data:
primary.cnf: |
[mysqld]
log-bin
replica.cnf: |
[mysqld]
super-read-only
3)创建无头服务
做了读写分离svc
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mysql
---
apiVersion: v1
kind: Service
metadata:
name: mysql-read
labels:
app: mysql
app.kubernetes.io/name: mysql
readonly: "true"
spec:
ports:
- name: mysql
port: 3306
selector:
app: mysql
4)创建mysql主从复制
官方文档的此配置清单有问题, 此为修改后
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
app.kubernetes.io/name: mysql
serviceName: mysql
replicas: 3
template:
metadata:
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
volumes:
- name: conf
emptyDir: {}
- name: config-map
configMap:
name: mysql
initContainers:
- name: init-mysql
image: mysql:5.7
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
command:
- bash
- "-c"
- |
set -ex
#从pod序号索引生成 mysql server-id
[[ `cat /etc/hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
#添加偏移量以避免使用 server-id=0 这一保留值
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
#将合适的conf.d文件从config-map复制到emptyDir
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/primary.cnf /mnt/conf.d/
else
cp /mnt/config-map/replica.cnf /mnt/conf.d/
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
- name: clone-mysql
image: ist0ne/xtrabackup:1.0
command:
- bash
- "-c"
- |
set -ex
#如果已有数据,则跳过克隆
[[ -d /var/lib/mysql/mysql ]] && exit 0
#跳过主实例(序号索引 0)的克隆
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
[[ $ordinal -eq 0 ]] && exit 0
#从原来的对等节点克隆数据
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
#准备备份,对数据进行整理
xtrabackup --prepare --target-dir=/var/lib/mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 200m
memory: 500Mi
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command: ["mysql", "-e", "SELECT 1"]
initialDelaySeconds: 15
periodSeconds: 2
timeoutSeconds: 1
- name: xtrabackup
image: ist0ne/xtrabackup:1.0
ports:
- containerPort: 3307
command:
- bash
- "-c"
- |
set -ex
cd /var/lib/mysql
#如果有克隆数据后产生的文件,从此文件确定binlog位置
if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
#XtraBackup已经生成了部分的“CHANGE MASTER TO”查询,需要删除末尾的分号
cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
#生成语句后,原来的就不再需要
rm -f xtrabackup_slave_info xtrabackup_binlog_info
elif [[ -f xtrabackup_binlog_info ]]; then
#直接从主实例进行克隆。解析binlog位置
[[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
rm -f xtrabackup_binlog_info xtrabackup_slave_info
echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
fi
#检查是否需要主从复制
if [[ -f change_master_to.sql.in ]]; then
echo "Waiting mysqld start"
#循环查看mysql状态,直到mysqld启动完成,可以连接上
until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
echo "Initializing replication from clone position"
#从库执行前面生成的sql,完成主从复制
mysql -h 127.0.0.1 \
-e "$(<change_master_to.sql.in), \
MASTER_HOST='mysql-0.mysql', \
MASTER_USER='root', \
MASTER_PASSWORD='', \
MASTER_CONNECT_RETRY=10; \
START SLAVE;" || exit 1
#如果容器重新启动,最多尝试一次
mv change_master_to.sql.in change_master_to.sql.orig
fi
#当对等点请求时,启动服务器发送备份
exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
"xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
resources:
requests:
cpu: 100m
memory: 100Mi
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi