Author:rab
在 Kubernetes(k8s)中,有状态服务和无状态服务是两种不同类型的应用程序部署方式,它们在容器编排和管理方面有一些关键区别。
1、无状态服务(Stateless Services)
2、有状态服务(Stateful Services)
比如我们可以举个 Nginx 负载均衡的例子,那此时这个负载均衡可以归为无状态服务
,任何一个 Nginx 实例都可以处理来自客户端的请求。因此我们可以采用 Kubernetes 中的 Deployment 进行部署。
vim Stateless.yml
apiVersion: v1
kind: Namespace
metadata:
name: myweb
labels:
name: ops
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: myweb
labels:
app: stateless
spec:
replicas: 3
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx:1.21.4
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-srv
namespace: myweb
spec:
selector:
app: demo
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30333
type: NodePort
注意:Service 的 selector 标签选择器的值一定要和 Deployment 的 selector 标签选择器的值保持一致,否则外部 Client 的请求就无法路由到 Pod 资源。
kubectl apply -f Stateless.yml
本地浏览器访问:
http://192.168.56.160:30333/
扩容的方式有很多,以下是常见的扩容方式。
1、修改 yml 文件方式
vim Stateless.yml # 只需修改replicas值即可
...
spec:
replicas: 5
...
修改完成后执行扩展(更新)
kubectl apply -f Stateless.yml
2、Shell 命令行方式
法1
kubectl scale --replicas=5 deploy nginx -n myweb
# 说明:deploy是deployment的缩写,写deployment也是没问题的
法2
kubectl edit deployment nginx -n myweb
# 这会以交互式的方式进入yml配置文件,进去后修改replicas的值后保存退出即可实现扩容
说明:与直接修改 yml 文件不同的是,该命令执行完后即可升级,而无需执行 kubectl apply -f Stateless.yml
与扩容没有任何区别,操作一模一样,所谓的缩容就是当你设置的 replicas 值小于当前 replicas 值时就是缩容的概念。
注意:但要注意的是,我们说动态扩容和缩容不会触发上线(即不会产生新的 replicaset)
。
注意的是,这个暂停和恢复不是说暂停了我的程序就不对外提供服务了,而是用于当你使用 kubectl set ...
命令来更新时使用。其大概流程就是:当你暂停后,允许你执行多个 kubectl set ...
更新命令,但每条指令执行完后不是立即生效,而是当你恢复 deployment 时你执行的那些 kubectl set ...
才会生效。
1、暂停服务
kubectl rollout pause deployment nginx -n myweb
2、执行第一条更新指令
kubectl set image deploy nginx nginx=nginx:1.20.0 -n myweb
3、执行第二条更新指令
kubectl set resources deploy nginx -c nginx --limits=cpu=200m,memory=128Mi --requests=cpu=10m,memory=16Mi -n myweb
如下图,以上这两条更新指令都是未生效的,从 nginx 镜像版本没变化就可证明。
那要如何是我们执行过的 kubectl set ...
指令生效呢?恢复即可。
kubectl rollout resume deploy nginx -n myweb
如下图,恢复后我们在暂停期间执行的 kubectl set ...
指令就生效了。
当然了,如果你想使用 kubectl set 来更新服务,但又不想暂停,你完全可以直接执行 kubectl set 指令即可,执行完后,就会立马更新。
kubectl set image deploy nginx nginx=nginx:1.18.0 -n myweb
如果我们以上的一系列更新最终的测试验证出问题了,需要回滚到最初(或更新前)的状态或中间某个版本状态,那此时就需要使用到 K8s 的回滚功能了,具体的操作如下。
1、先查看更新的版本
默认情况下,K8s 只会保留近 10 个的
revision
,我们可以在 Deployment 配置文件中使用revisionHistoryLimit
字段来指定 revision 的保留数量。
kubectl rollout history deploy nginx -n myweb
# 下图中这三个版本编号中,1表示最初版本状态,3表示当前的版本状态
2、再查看每个版本对应的详情(因为上图根本看不出来)
kubectl rollout history deployment nginx --revision=1 -n myweb
# 我们来看看第一个版本详情
3、回滚到我们指定的版本状态
kubectl rollout undo deployment nginx --to-revision=1 -n myweb
4、最后来验证一下是否回滚成功
kubectl get deployment nginx -o wide -n myweb
这个时候我们再来看看历史版本号:
你会发现,历史版本为 1 的编号消失了,为什么呢?因为已经被我们回滚了(此时你就要记住了,如果你又执行了多次更新操作后,又想回到最初的状态的话,只能回到 4 这个版本,因为 4 此时才是我们最初的状态,而 2 并不是)。
上面说到,像 MySQL、RabbitMQ 等这类服务实际上就是属于有状态服务,对于这类服务我们一般会要求每个 Pod 具有唯一的标识,以及稳定的网络标识。例如 MySQL 就是一个典型的有状态应用程序,因为每个 MySQL 实例需要唯一的标识,并且通常依赖于稳定的网络标识。
vim Stateful.yml
apiVersion: v1
kind: Namespace
metadata:
name: myweb-2
labels:
name: ops
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
namespace: myweb-2
labels:
app: stateful
spec:
serviceName: myweb-2
replicas: 3
selector:
matchLabels:
app: demo-2
template:
metadata:
labels:
app: demo-2
spec:
containers:
- name: nginx
image: nginx:1.21.4
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-srv
namespace: myweb-2
spec:
selector:
app: demo-2
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30334
type: NodePort
kubectl apply -f Stateful.yml
对于如何查看 deployment 资源,我想你已经会了,那 statefulset 资源如何查看呢?其实都是一样的套路:
kubectl get statefulset -n myweb-2
# 或
kubectl get sts -n myweb-2
本地浏览器访问:
http://192.168.56.160:30334/
kubectl scale --replicas=5 sts nginx -n myweb-2
kubectl scale --replicas=2 sts nginx -n myweb-2
说明:暂停、恢复、回滚套路和 deployment 保持一致的,这里就不再重复造轮子了。
无状态服务适用于可以随意扩展并处理请求而不依赖本地状态的应用程序,而有状态服务适用于需要稳定的标识和数据的应用程序。
在 Kubernetes 中,我们可以使用不同的控制器(例如 Deployment 和 StatefulSet)来适应这两种不同类型的服务。
有状态服务通常需要考虑持久性存储和数据备份策略,以确保数据的可靠性和可用性。
说明:以上案例均未配置持久化存储,后面会提到。
—END