背景
k8s 在部署带状态的程序相对来说比较麻烦,一个高可用的程序,应该可以在每一个node上跑,所以在程序的开发阶段就应该要考虑到部署阶段带来的问题,可以降低程序容器化
- 配置信息尽量写入DB,那么程序起来的时候直接从DB加载,无论从提高部署效率或者运营的角度来说,入库都是最好的选择,更新配置之后,replace pod就ok
- 无法入库的大文件配置,通过k8s的pv pvc来处理,参考
- 小文件配置,build docker的时候写入容器
k8s 支持的persistent volumes
目前支持的持久卷类型如下:(前面几项目前的网络环境难以实现)
- GCEPersistentDisk
- AWSElasticBlockStore
- AzureFile
- AzureDisk
- FC (Fibre Channel)**
- FlexVolume
- Flocker
- NFS
- iSCSI
- RBD (Ceph Block Device)
- CephFS
- Cinder (OpenStack block storage)
- Glusterfs
- VsphereVolume
- Quobyte Volumes
- HostPath (Single node testing only – local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)
- Portworx Volumes
- ScaleIO Volumes
- StorageOS
hostPath,也就是node的文件系统(貌似我最初接触docker挂在volume的时候就是这种方式,但是现在看来,在可用性真的弱)
cephfs和glusterfs都是linux生态上比较有名的分布式文件系统,使用上比nfs要复杂些,这里不展开。下面我使用的nfs,nfs相对于ceph和gluster,可用性略弱,搭建测试环境还是可以的。
https://kubernetes.io/docs/tasks/run-application/run-single-instance-stateful-application/
以mysql为例的带状态程序部署
由于这篇文章是部署完成之后写的,有一些时间数据可以能有差异,我当时使用的是default namespace,实际上namespace管理也很重要,最好在apply yaml的时候指定。
需要准备
- 一个nfs服务,每个节点都要安装nfs client
- 一个kubernetes 集群,带dashboard更佳(观察部署服务状态真的很方便)
- 外网环境!!
- kubectl operation
Step1: 准备pv pvc
我的k8s 环境如下
$kubectl get nodes
NAME STATUS ROLES AGE VERSION
s01 Ready master 7d v1.10.3
s02 Ready <none> 7d v1.10.3
s03 Ready <none> 7d v1.10.3
mysql-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv01
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /opt/nfs_data/mysql
server: 192.168.242.133
mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
在对应的namespace apply一下配置,就可以看到下面状态
kubectl apply --namespace=targetNamespace -f *.yaml
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mysql-pv01 20Gi RWO Recycle Bound default/mysql-pv-claim 8h
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-pv-claim Bound mysql-pv01 20Gi RWO 8h
当你看到bound状态的时候,说明pv和pvc已经bind好,这样才可以进行下一步deployment
Note:关于pv和pvc是如何绑定的我最初看的官网的几个例子,但是都没发现有key相关的绑定,参考官方bind说明,大约就是说,容量大小和读取模式匹配就断匹配到了,我感觉bind算法说得也不是很清楚,可能要看代码才知道
Step2: deploy
mysql-deployment.yaml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
# Use secret in real usage
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
apply 一下,等拉取镜像完成,容器创建完成后,可以看到,it may take a few minutes,
$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
mysql 1 1 1 1 8h
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-648d67bf7c-r9ftr 1/1 Running 0 8h
到这步,mysql就基本部署完成了,pod已经起来,
$ kubectl exec -it mysql-648d67bf7c-r9ftr -- mysql -uroot -ppassword
到这一步,就可以看到非常熟悉的mysql console啦
Step3: expose
这里使用nodePort来暴露内部mysql的端口,这样在内网环境就可以访问到容器内的pod,由于这个是单点部署,需要知道mysql这个pod运行在哪个node,才能在内网环境下访问
mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
type: NodePort
sessionAffinity: ClientIP
selector:
app: mysql
ports:
- port: 3306
nodePort: 30010
mysql 这个pod运行在s02节点上,对应的IP:192.168.242.134,内网环境下可以直接通过s02节点的ip访问
mysql -h192.168.242.134 -P30010 -uroot -ppassword
接下来就可以看到mysql console了
以上步骤的最终效果
注意
以上部署是单点部署,只是为了跑通带状态程序的部署,在高可用性上非常弱
暴露的ip,要知道mysql运行在哪个node上,才能决定连接的ip,后续可以使用clusterIp方案。
安全问题,内网环境需要隔离