statefulset 是为了解决有状态服务的问题,而产生的一种资源类型,当然,如果你使用单节点的数据库,你可以使用 deployment 资源类型,但如果你的有状态服务是集群,节点之间有主备或者主从之分,且每个节点分片存储的情况下,deployment 就不适合这种场景了,因为 deployment 不会保证 pod 的有序性,集群通常需要主节点先启动,从节点再加入集群,statefulset 则可以保证,其次,deployment 资源的 pod 内的 pvc 是共享的,而 statefulset 下的 pod 内 pvc 是不共享存储的,每个 pod 拥有自己独立的存储空间,正好满足分片需求,实现分片的需求的前提是 statefulset 可以保证 pod 重新调度后还是能访问到相同的持久化数据。
适用 statefulset 常用的服务有 ES 集群、mongodb 集群、redis 集群、mysql 集群等
应用场景
- 稳定的不共享持久化存储:每个 pod 的存储资源是不共享的,且 pod 重新调度后还是能访问到相同的持久化数据,基于 pvc 实现
- 稳定的网络标记:pod 重新调度后其 PodName 和 HostName 不变,且 PodName 和 HostName 是相同的,基于 Headless Service 来实现的
- 有序部署,有序扩展:pod 是有顺序的,在部署或者扩展的时候是根据定义的顺序依次部署的(即从 0 到 N-1,在下一个 pod 运行之前,所有之前的 pod 必须都是 Running 或者 Ready 状态),是基于 init conntainers 来实现的
- 有序收缩:在 pod 删除时是从最后一个依次往前删除,即从 N-1 到 0
部署 StatefulSet 类型的 Mongodb
首先是 mongo.yml
apiVersion: apps/v1 | |
kind: StatefulSet | |
metadata: | |
name: mongodb | |
spec: | |
replicas: 3 | |
serviceName: mongodb | |
selector: | |
matchLabels: | |
app: mongodb | |
template: | |
metadata: | |
labels: | |
app: mongodb | |
spec: | |
containers: | |
- name: mongo | |
image: mongo:4.4 | |
# IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错 | |
imagePullPolicy: IfNotPresent | |
--- | |
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: mongodb | |
spec: | |
selector: | |
app: mongodb | |
type: ClusterIP | |
# HeadLess | |
clusterIP: None | |
ports: | |
- port: 27017 | |
targetPort: 27017 |
部署 mongo
kubectl apply -f mongo.yml |
StatefulSet 特性
- Service 的 CLUSTER-IP 是空的,Pod 名字也是固定的
- Pod 创建和销毁是有序的,创建是顺序的,销毁是逆序的
- Pod 重建不会改变名字,除了 IP,所以不要使用 IP 直连
Endpoints 会多一个 hostname
kubectl get endpoints mongodb -o yaml |
访问时,如果直接使用 Service 名字连接,会随机转发请求,要连接指定 pod,可以使用 pod-name.service-name
运行一个临时 pod 连接来查看 mongo 数据测试一下,可以使用
kubectl run mongo-client --rm --tty -i --restart='Never' --image=mongo:4.4 --command -- bash |
你可以先在 docker 提前拉取 mongo:4.4 镜像
我们首先查看 mongo 里的数据
[root@master ~]# kubectl run mongo-client --rm --tty -i --restart='Never' --image=mongo:4.4 --command -- bash | |
If you don't see a command prompt, try pressing enter. | |
root@mongo-client:/# mongo --host mongodb-0.mongodb | |
MongoDB shell version v4.4.18 | |
connecting to: mongodb://mongodb-0.mongodb:27017/?compressors=disabled&gssapiServiceName=mongodb | |
Implicit session: session { "id" : UUID("f86e1d9a-cd62-4f20-ad58-e7d2ee18f7e5") } | |
MongoDB server version: 4.4.18 | |
Welcome to the MongoDB shell. | |
For interactive help, type "help". | |
For more comprehensive documentation, see | |
https://docs.mongodb.com/ | |
Questions? Try the MongoDB Developer Community Forums | |
https://community.mongodb.com | |
--- | |
The server generated these startup warnings when booting: | |
2023-02-03T17:07:21.490+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted | |
2023-02-03T17:07:21.491+00:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never' | |
2023-02-03T17:07:21.491+00:00: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. We suggest setting it to 'never' | |
--- | |
--- | |
Enable MongoDB's free cloud-based monitoring service, which will then receive and display | |
metrics about your deployment (disk utilization, CPU, operation statistics, etc). | |
The monitoring data will be available on a MongoDB website with a unique URL accessible to you | |
and anyone you share the URL with. MongoDB may use this information to make product | |
improvements and to suggest MongoDB products and deployment options to you. | |
To enable free monitoring, run the following command: db.enableFreeMonitoring() | |
To permanently disable this reminder, run the following command: db.disableFreeMonitoring() | |
--- | |
> show dbs | |
admin 0.000GB | |
config 0.000GB | |
local 0.000GB |
里面只有默认的数据,然后我们部署一个服务,服务里连接 mongo,以下是 app.yml
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
# 部署名字 | |
name: test-k8s | |
spec: | |
replicas: 2 | |
# 用来查找关联的 Pod,所有标签都匹配才行 | |
selector: | |
matchLabels: | |
app: test-k8s | |
# 定义 Pod 相关数据 | |
template: | |
metadata: | |
labels: | |
app: test-k8s | |
spec: | |
# 定义容器,可以多个 | |
containers: | |
- name: test-k8s # 容器名字 | |
image: docker.io/leayos/testweb:0.1 # 镜像 | |
imagePullPolicy: IfNotPresent | |
# 等待 mongodb 起来后才启动 | |
initContainers: | |
- name: wait-mongo | |
image: busybox:1.28 | |
command: ['sh', '-c', "until nslookup mongodb; do echo waiting for mongo; sleep 2; done"] | |
--- | |
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: test-k8s | |
spec: | |
selector: | |
app: test-k8s | |
# 默认 ClusterIp 集群内可访问,NodePort 节点可访问,LoadBalancer 负载均衡模式(需要负载均衡器才可用) | |
type: NodePort | |
ports: | |
- nodePort: 31000 # 节点端口,范围固定 30000 ~ 32767 | |
port: 5000 # 本 Service 的端口 | |
targetPort: 5000 # 容器端口 |
服务的镜像我简单添加了 mongo 连接的 Flask 项目并上传到了 docker hub,mongo 的连接方式为:mongodb://mongodb-0.mongodb:27017,因为我经过测试,貌似修改了 imagePullPolicy 还是不行,它还是会在远端拉取镜像
imagePullPolicy
- Always:总是拉取镜像
- IfNotPresent:本地有则使用本地镜像,不拉取
- Never:只是用本地镜像,从不拉取,即使本地没有
- 如果忽略,策略默认为 Always
- 官方文档:https://kubernetes.io/docs/concepts/containers/images
部署服务
[root@master statefulset]# kubectl get pod | |
NAME READY STATUS RESTARTS AGE | |
mongo-client 1/1 Running 0 99s | |
mongodb-0 1/1 Running 0 8m39s | |
mongodb-1 1/1 Running 0 8m38s | |
mongodb-2 1/1 Running 0 8m37s | |
[root@master statefulset]# kubectl apply -f app.yaml | |
deployment.apps/test-k8s created | |
service/test-k8s created | |
[root@master statefulset]# kubectl get pod | |
NAME READY STATUS RESTARTS AGE | |
mongo-client 1/1 Running 0 119s | |
mongodb-0 1/1 Running 0 8m59s | |
mongodb-1 1/1 Running 0 8m58s | |
mongodb-2 1/1 Running 0 8m57s | |
test-k8s-54999476cd-2x8gs 1/1 Running 0 3s | |
test-k8s-54999476cd-bl7tf 1/1 Running 0 3s |
OK,这时候部署没问题,我们暴漏端口访问测试一下,我们请求网页,然后查询 mongo
[root@master statefulset]# kubectl port-forward --address 0.0.0.0 test-k8s-54999476cd-2x8gs 5000:5000 | |
Forwarding from 0.0.0.0:5000 -> 5000 |
我们添加一条数据
然后通过 mongo client 查询,可以发现多了一个新的数据库
切换数据库,查询集合的所有数据
我请求了几遍
可以看到,数据成功添加了
这是一个简单的案例,起了 3 个 mongo,你可以作为主从来使用,一个负责写入,其它负责读取