和之前同一文档,这节是讲在阿里的K8S上部署StatefulSet有状态服务方法,在自建群集上部署的话方法类似;更新的文档上是采用自建+NFS存储类方式。
存储类StorageClass
新测试环境与开发环境(原测试)使用同一个NAS文件系统,以节省资源;原环境配置:
其在nfs格式的NAS根目录下创建了nfsroot目录并在k8s中进行了类配置使用。
点开NAS,可以在“挂载使用”中看到其挂载示例命令:
现在在一台ECS上将其挂载上,并创建另一个目录,以作为新的测试环境挂载点使用:
yum install -y nfs-utils mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport xxxx.cn-hangzhou.nas.aliyuncs.com:/ /mnt/nas cd /mnt/nas mkdir nasroot touch nasroot/测试环境k8s
创建连接到NAS的存储类StorageClass:
新版的存储插件为CSI,之前老版本的为Flexvolume,相关区别见官网。阿里NAS存储卷在K8S上的挂载使用也可参见文档。
mkdir yamls cd yamls vim alicloud-nas.yml
apiVersion: storage.k8s.io/v1 kind: StorageClass # https://help.aliyun.com/document_detail/144398.html metadata: name: alicloud-nas mountOptions: - 'nolock,tcp,noresvport' - vers=3 parameters: volumeAs: subpath server: 'xxx.cn-hangzhou.nas.aliyuncs.com:/nasroot/' provisioner: nasplugin.csi.alibabacloud.com reclaimPolicy: Retain volumeBindingMode: Immediate
kubectl create -f alicloud-nas.yml kubectl get storageclasses.storage.k8s.io
可以查看添加后生成的YAML内容:
存储类是表明k8s群集中支持的存储类型,一般不直接使用;通常会创建“存储声明”PVC(PersistentVolumeClaim),以及“存储卷”PV(PersistentVolume)来使用。
目前的回收策略有:
- Retain — 手动回收
- Recycle — 基本擦除 (rm -rf /thevolume/*) 回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态供应。
- Delete — 诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除
持久卷(PersistentVolume,PV)是集群中的一块存储,可以由管理员事先供应,或者 使用存储类(Storage Class)来动态供应。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样,也是使用 卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC)表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。
Redis部署
PVC会在部署文件中以volumeClaimTemplates方式进行声明,PV也会自动创建,故不需要单独创建。
下面会把Redis放在public的命名空间中运行,故要先行创建。
按之前方式部署,这里Redis采用主从方式部署。
配置项ConfigMap
由于Pod不会存储数据,而PV也是在Pod启动进挂载使用;程序启动的配置文件一般也都放在ConfigMap之中,创建ConfigMap的YAML:
vim redis-config.yaml
apiVersion: v1 kind: ConfigMap data: master.conf: |- dir /data appendonly yes rename-command FLUSHDB "" rename-command FLUSHALL "" redis.conf: |- dir /data appendonly yes # Disable RDB persistence, AOF persistence already enabled. save "" replica.conf: |- dir /data slave-read-only yes rename-command FLUSHDB "" rename-command FLUSHALL "" metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default namespace: public
kubectl create -f redis-config.yml kubectl -n public get configmaps
ConfigMap中不光可以用来储存应用的配置文件,也可以用来储存脚本文件,供容器中使用。
vim redis-scripts.yml
apiVersion: v1 kind: ConfigMap data: ping_liveness_local.sh: |- response=$( timeout -s 9 $1 \ redis-cli \ -a $REDIS_PASSWORD --no-auth-warning \ -h localhost \ -p $REDIS_PORT \ ping ) if [ "$response" != "PONG" ] && [ "$response" != "LOADING Redis is loading the dataset in memory" ]; then echo "$response" exit 1 fi ping_liveness_local_and_master.sh: |- script_dir="$(dirname "$0")" exit_status=0 "$script_dir/ping_liveness_local.sh" $1 || exit_status=$? "$script_dir/ping_liveness_master.sh" $1 || exit_status=$? exit $exit_status ping_liveness_master.sh: >- response=$( timeout -s 9 $1 \ redis-cli \ -a $REDIS_MASTER_PASSWORD --no-auth-warning \ -h $REDIS_MASTER_HOST \ -p $REDIS_MASTER_PORT_NUMBER \ ping ) if [ "$response" != "PONG" ] && [ "$response" != "LOADING Redis is loading the dataset in memory" ]; then echo "$response" exit 1 fi ping_readiness_local.sh: |- response=$( timeout -s 9 $1 \ redis-cli \ -a $REDIS_PASSWORD --no-auth-warning \ -h localhost \ -p $REDIS_PORT \ ping ) if [ "$response" != "PONG" ]; then echo "$response" exit 1 fi ping_readiness_local_and_master.sh: |- script_dir="$(dirname "$0")" exit_status=0 "$script_dir/ping_readiness_local.sh" $1 || exit_status=$? "$script_dir/ping_readiness_master.sh" $1 || exit_status=$? exit $exit_status ping_readiness_master.sh: |- response=$( timeout -s 9 $1 \ redis-cli \ -a $REDIS_MASTER_PASSWORD --no-auth-warning \ -h $REDIS_MASTER_HOST \ -p $REDIS_MASTER_PORT_NUMBER \ ping ) if [ "$response" != "PONG" ]; then echo "$response" exit 1 fi metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default-health namespace: public
kubectl create -f redis-scripts.yml kubectl -n public get configmaps
保密字典Secert
创建yaml文件并应用:
vim redis-secret.yml
apiVersion: v1 kind: Secret data: redis-password: WnMyMDE5MTAwMQo= metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default namespace: public type: Opaque
kubectl create -f redis-secret.yml kubectl -n public get secrets
密码值需使用base64编码格式;上面的密码解码即为Zs20191001。
Secret的类型:
网络入口Headless服务
Headless service是StatefulSet实现稳定网络标识的基础,需要提前创建。
vim redis-default-headless.yml
apiVersion: v1 kind: Service metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default-headless namespace: public spec: clusterIP: None ports: - name: redis port: 6379 protocol: TCP targetPort: redis selector: app: redis release: redis-default
kubectl create -f redis-default-headless.yml kubectl -n public get svc
“普通” 服务(除了无头服务)会以 my-svc.my-namespace.svc.cluster-domain.example 这种名字的形式被分配一个 DNS A 或 AAAA 记录,取决于服务的 IP 协议族。 该名称会解析成对应服务的集群 IP。
“无头(Headless)” 服务(没有集群 IP)也会以 my-svc.my-namespace.svc.cluster-domain.example 这种名字的形式被指派一个 DNS A 或 AAAA 记录, 具体取决于服务的 IP 协议族。 与普通服务不同,这一记录会被解析成对应服务所选择的 Pod 集合的 IP。 客户端要能够使用这组 IP,或者使用标准的轮转策略从这组 IP 中进行选择。
主库
创建生成Redis主库的YAML文件:
vim redis-default-master.yml
apiVersion: apps/v1 kind: StatefulSet metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default-master namespace: public spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: redis release: redis-default role: master serviceName: redis-default-headless template: metadata: labels: app: redis chart: redis-9.5.5 release: redis-default role: master spec: containers: - command: - /bin/bash - '-c' - | if [[ ! -f /opt/bitnami/redis/etc/master.conf ]];then cp /opt/bitnami/redis/mounted-etc/master.conf /opt/bitnami/redis/etc/master.conf fi if [[ ! -f /opt/bitnami/redis/etc/redis.conf ]];then cp /opt/bitnami/redis/mounted-etc/redis.conf /opt/bitnami/redis/etc/redis.conf fi ARGS=("--port" "${REDIS_PORT}") ARGS+=("--requirepass" "${REDIS_PASSWORD}") ARGS+=("--include" "/opt/bitnami/redis/etc/redis.conf") ARGS+=("--include" "/opt/bitnami/redis/etc/master.conf") /run.sh ${ARGS[@]} env: - name: REDIS_REPLICATION_MODE value: master - name: REDIS_PASSWORD valueFrom: secretKeyRef: key: redis-password name: redis-default - name: REDIS_PORT value: '6379' image: 'docker.io/bitnami/redis:5.0.7-debian-9-r0' imagePullPolicy: IfNotPresent livenessProbe: exec: command: - sh - '-c' - /health/ping_liveness_local.sh 5 failureThreshold: 5 initialDelaySeconds: 5 periodSeconds: 5 successThreshold: 1 timeoutSeconds: 5 name: redis-default ports: - containerPort: 6379 name: redis protocol: TCP readinessProbe: exec: command: - sh - '-c' - /health/ping_readiness_local.sh 5 failureThreshold: 5 initialDelaySeconds: 5 periodSeconds: 5 successThreshold: 1 timeoutSeconds: 1 resources: {} securityContext: runAsUser: 1001 terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /health name: health - mountPath: /data name: redis-data - mountPath: /opt/bitnami/redis/mounted-etc name: config - mountPath: /opt/bitnami/redis/etc/ name: redis-tmp-conf dnsPolicy: ClusterFirst restartPolicy: Always securityContext: fsGroup: 1001 serviceAccount: default serviceAccountName: default volumes: - configMap: defaultMode: 0530 name: redis-default-health name: health - configMap: name: redis-default name: config - emptyDir: {} name: redis-tmp-conf updateStrategy: type: RollingUpdate volumeClaimTemplates: - metadata: labels: app: redis component: master heritage: Tiller release: redis-default name: redis-data spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi storageClassName: alicloud-nas
kubectl create -f redis-default-master.yml kubectl -n public get statefulsets.apps
可以看到,其已运行。
其会自动创建PVC和PV并且映射:
kubectl -n public get pvc kubectl get pv
创建服务:
vim redis-default-master-svc.yml
apiVersion: v1 kind: Service metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default-master namespace: public spec: ports: - name: redis port: 6379 protocol: TCP targetPort: redis selector: app: redis release: redis-default role: master
kubectl create -f redis-default-master-svc.yml kubectl -n public get svc
从库
用于创建从库的YAML文件:
vim redis-default-slave.yml
apiVersion: apps/v1 kind: StatefulSet metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default-slave namespace: public spec: replicas: 2 revisionHistoryLimit: 10 selector: matchLabels: app: redis release: redis-default role: slave serviceName: redis-default-headless template: metadata: labels: app: redis chart: redis-9.5.5 release: redis-default role: slave spec: containers: - command: - /bin/bash - '-c' - | if [[ ! -f /opt/bitnami/redis/etc/replica.conf ]];then cp /opt/bitnami/redis/mounted-etc/replica.conf /opt/bitnami/redis/etc/replica.conf fi if [[ ! -f /opt/bitnami/redis/etc/redis.conf ]];then cp /opt/bitnami/redis/mounted-etc/redis.conf /opt/bitnami/redis/etc/redis.conf fi ARGS=("--port" "${REDIS_PORT}") ARGS+=("--slaveof" "${REDIS_MASTER_HOST}" "${REDIS_MASTER_PORT_NUMBER}") ARGS+=("--masterauth" "${REDIS_MASTER_PASSWORD}") ARGS+=("--requirepass" "${REDIS_PASSWORD}") ARGS+=("--include" "/opt/bitnami/redis/etc/redis.conf") ARGS+=("--include" "/opt/bitnami/redis/etc/replica.conf") /run.sh ${ARGS[@]} env: - name: REDIS_REPLICATION_MODE value: slave - name: REDIS_MASTER_HOST value: >- redis-default-master-0.redis-default-headless.public.svc.cluster.local - name: REDIS_PORT value: '6379' - name: REDIS_MASTER_PORT_NUMBER value: '6379' - name: REDIS_PASSWORD valueFrom: secretKeyRef: key: redis-password name: redis-default - name: REDIS_MASTER_PASSWORD valueFrom: secretKeyRef: key: redis-password name: redis-default image: 'docker.io/bitnami/redis:5.0.7-debian-9-r0' imagePullPolicy: IfNotPresent livenessProbe: exec: command: - sh - '-c' - /health/ping_liveness_local_and_master.sh 5 failureThreshold: 5 initialDelaySeconds: 30 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 5 name: redis-default ports: - containerPort: 6379 name: redis protocol: TCP readinessProbe: exec: command: - sh - '-c' - /health/ping_readiness_local_and_master.sh 5 failureThreshold: 5 initialDelaySeconds: 5 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 10 securityContext: runAsUser: 1001 terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /health name: health - mountPath: /data name: redis-data - mountPath: /opt/bitnami/redis/mounted-etc name: config - mountPath: /opt/bitnami/redis/etc name: redis-tmp-conf dnsPolicy: ClusterFirst restartPolicy: Always securityContext: fsGroup: 1001 serviceAccount: default serviceAccountName: default volumes: - configMap: defaultMode: 0530 name: redis-default-health name: health - configMap: name: redis-default name: config - emptyDir: {} name: sentinel-tmp-conf - emptyDir: {} name: redis-tmp-conf updateStrategy: type: RollingUpdate volumeClaimTemplates: - metadata: labels: app: redis component: slave heritage: Tiller release: redis-default name: redis-data spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi storageClassName: alicloud-nas
kubectl create -f redis-default-slave.yml kubectl -n public get statefulsets.apps
Tips:这里没有使用其自带的run.sh启动脚本,而是使用生成的命令启动;因为经过反复测试发现使用脚本启动的命令带的args会有断行现象,会导致同步及密码会有问题:
同样其会自动创建相关PVC、PV,通过命令可查看,也可在控制台查看:
创建服务:
vim redis-default-slave-svc.yml
apiVersion: v1 kind: Service metadata: labels: app: redis chart: redis-9.5.5 heritage: Tiller release: redis-default name: redis-default-slave namespace: public spec: ports: - name: redis port: 6379 protocol: TCP targetPort: redis selector: app: redis release: redis-default role: slave
kubectl create -f redis-default-slave-svc.yml kubectl -n public get svc