- 使用StatefulSet部署有状态应用
- 部署Zookeeper
- 部署kafka
- 参考
使用StatefulSet部署有状态应用
StatefulSet 这个对象是专门用来部署用状态应用的,可以为Pod提供稳定的身份标识,包括hostname、启动顺序、DNS名称等。
下面以在kubernetes1.6版本中部署zookeeper和kafka为例讲解StatefulSet的使用,其中kafka依赖于zookeeper。
Dockerfile和配置文件见 zookeeper 和 kafka。
注:所有的镜像基于CentOS系统的JDK制作,为我的私人镜像,外部无法访问,yaml中没有配置持久化存储。
部署Zookeeper
Dockerfile中从远程获取zookeeper的安装文件,然后在定义了三个脚本:
- zkGenConfig.sh:生成zookeeper配置文件
- zkMetrics.sh:获取zookeeper的metrics
- zkOk.sh:用来做ReadinessProb
我们在来看下这三个脚本的执行结果:
zkGenConfig.sh
zkMetrics.sh脚本实际上执行的是下面的命令:
$ echo mntr | nc localhost $ZK_CLIENT_PORT >& 1zk_version 3.4.6-1569965, built on 02/20/2014 09:09 GMTzk_avg_latency 0zk_max_latency 5zk_min_latency 0zk_packets_received 427879zk_packets_sent 427890zk_num_alive_connections 3zk_outstanding_requests 0zk_server_state leaderzk_znode_count 18zk_watch_count 3zk_ephemerals_count 4zk_approximate_data_size 613zk_open_file_descriptor_count 29zk_max_file_descriptor_count 1048576zk_followers 1zk_synced_followers 1zk_pending_syncs 0
zkOk.sh脚本实际上执行的是下面的命令:
$ echo ruok | nc 127.0.0.1 $ZK_CLIENT_PORTimok
zookeeper.yaml
下面是启动三个zookeeper实例的yaml配置文件:
---apiVersion: v1kind: Servicemetadata:name: zk-svclabels:app: zk-svcspec:ports:- port: 2888name: server- port: 3888name: leader-electionclusterIP: Noneselector:app: zk---apiVersion: v1kind: ConfigMapmetadata:name: zk-cmdata:jvm.heap: "1G"tick: "2000"init: "10"sync: "5"client.cnxns: "60"snap.retain: "3"purge.interval: "0"---apiVersion: policy/v1beta1kind: PodDisruptionBudgetmetadata:name: zk-pdbspec:selector:matchLabels:app: zkminAvailable: 2---apiVersion: apps/v1beta1kind: StatefulSetmetadata:name: zkspec:serviceName: zk-svcreplicas: 3template:metadata:labels:app: zkspec:affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: "app"operator: Invalues:- zktopologyKey: "kubernetes.io/hostname"containers:- name: k8szkimagePullPolicy: Alwaysimage: harbor-001.jimmysong.io/library/zookeeper:3.4.6resources:requests:memory: "2Gi"cpu: "500m"ports:- containerPort: 2181name: client- containerPort: 2888name: server- containerPort: 3888name: leader-electionenv:- name : ZK_REPLICASvalue: "3"- name : ZK_HEAP_SIZEvalueFrom:configMapKeyRef:name: zk-cmkey: jvm.heap- name : ZK_TICK_TIMEvalueFrom:configMapKeyRef:name: zk-cmkey: tick- name : ZK_INIT_LIMITvalueFrom:configMapKeyRef:name: zk-cmkey: init- name : ZK_SYNC_LIMITvalueFrom:configMapKeyRef:name: zk-cmkey: tick- name : ZK_MAX_CLIENT_CNXNSvalueFrom:configMapKeyRef:name: zk-cmkey: client.cnxns- name: ZK_SNAP_RETAIN_COUNTvalueFrom:configMapKeyRef:name: zk-cmkey: snap.retain- name: ZK_PURGE_INTERVALvalueFrom:configMapKeyRef:name: zk-cmkey: purge.interval- name: ZK_CLIENT_PORTvalue: "2181"- name: ZK_SERVER_PORTvalue: "2888"- name: ZK_ELECTION_PORTvalue: "3888"command:- sh- -c- zkGenConfig.sh && zkServer.sh start-foregroundreadinessProbe:exec:command:- "zkOk.sh"initialDelaySeconds: 10timeoutSeconds: 5livenessProbe:exec:command:- "zkOk.sh"initialDelaySeconds: 10timeoutSeconds: 5securityContext:runAsUser: 1000fsGroup: 1000
我们再主要下上面那三个脚本的用途。
部署kafka
Kafka的docker镜像制作跟zookeeper类似,都是从远程下载安装包后,解压安装。
与zookeeper不同的是,只要一个脚本,但是又依赖于我们上一步安装的zookeeper,kafkaGenConfig.sh用来生成kafka的配置文件。
我们来看下这个脚本。
#!/bin/bashHOST=`hostname -s`if [[ $HOST =~ (.*)-([0-9]+)$ ]]; thenNAME=${BASH_REMATCH[1]}ORD=${BASH_REMATCH[2]}elseecho "Failed to extract ordinal from hostname $HOST"exit 1fiMY_ID=$((ORD+1))sed -i s"/broker.id=0/broker.id=$MY_ID/g" /opt/kafka/config/server.propertiessed -i s'/zookeeper.connect=localhost:2181/zookeeper.connect=zk-0.zk-svc.brand.svc:2181,zk-1.zk-svc.brand.svc:2181,zk-2.zk-svc.brand.svc:2181/g' /opt/kafka/config/server.properties
该脚本根据statefulset生成的pod的hostname的后半截数字部分作为broker ID,同时再替换zookeeper的地址。
Kafka.yaml
下面是创建3个kafka实例的yaml配置。
---apiVersion: v1kind: Servicemetadata:name: kafka-svclabels:app: kafkaspec:ports:- port: 9093name: serverclusterIP: Noneselector:app: kafka---apiVersion: policy/v1beta1kind: PodDisruptionBudgetmetadata:name: kafka-pdbspec:selector:matchLabels:app: kafkaminAvailable: 2---apiVersion: apps/v1beta1kind: StatefulSetmetadata:name: kafkaspec:serviceName: kafka-svcreplicas: 3template:metadata:labels:app: kafkaspec:affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: "app"operator: Invalues:- kafkatopologyKey: "kubernetes.io/hostname"podAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 1podAffinityTerm:labelSelector:matchExpressions:- key: "app"operator: Invalues:- zktopologyKey: "kubernetes.io/hostname"terminationGracePeriodSeconds: 300containers:- name: k8skafkaimagePullPolicy: Alwaysimage: harbor-001.jimmysong.io/library/kafka:2.10-0.8.2.1resources:requests:memory: "1Gi"cpu: 500menv:- name: KF_REPLICASvalue: "3"ports:- containerPort: 9093name: servercommand:- /bin/bash- -c- "/opt/kafka/bin/kafkaGenConfig.sh && /opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/server.properties"env:- name: KAFKA_HEAP_OPTSvalue : "-Xmx512M -Xms512M"- name: KAFKA_OPTSvalue: "-Dlogging.level=DEBUG"readinessProbe:tcpSocket:port: 9092initialDelaySeconds: 15timeoutSeconds: 1
参考
- https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/
- kubernetes contrib - statefulsets
