Kubernetes中有几种不同的方式发布应用,所以为了让应用在升级期间依然平稳提供服务,选择一个正确的发布策略就非常重要了。

选择正确的部署策略是要依赖于我们的业务需求的,下面我们列出了一些可能会使用到的策略:

  • 重建(recreate):停止旧版本部署新版本

  • 滚动更新(rolling-update):一个接一个地以滚动更新方式发布新版本

  • 蓝绿(blue/green):新版本与旧版本一起存在,然后切换流量

  • 金丝雀(canary):将新版本面向一部分用户发布,然后继续全量发布

  • A/B测(a/b testing):以精确的方式(HTTP 头、cookie、权重等)向部分用户发布新版本。A/B测实际上是一种基于数据统计做出业务决策的技术。在 Kubernetes 中并不原生支持,需要额外的一些高级组件来完成改设置(比如Istio、Linkerd、Traefik、或者自定义 Nginx/Haproxy 等)。

你可以在Kubernetes集群上来对上面的这些策略进行测试,下面的仓库中有需要使用到的资源清单:https://github.com/ContainerSolutions/k8s-deployment-strategies

接下来我们来介绍下每种策略,看看在什么场景下面适合哪种策略。

重建(Recreate) - 最好在开发环境

策略定义为RecreateDeployment,会终止所有正在运行的实例,然后用较新的版本来重新创建它们。

spec:
replicas:
strategy:
type: Recreate
 

Recreate

重新创建策略是一个虚拟部署,包括关闭版本A,然后在关闭版本A后部署版本B. 此技术意味着服务的停机时间取决于应用程序的关闭和启动持续时间。

我们这里创建两个相关的资源清单文件,app-v1.yaml:

apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
type: NodePort
ports:
- name: http
port:
targetPort: http
selector:
app: my-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
replicas:
selector:
matchLabels:
app: my-app
strategy:
type: Recreate
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
version: v1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: ""
spec:
containers:
- name: my-app
image: containersol/k8s-deployment-strategies
ports:
- name: http
containerPort:
- name: probe
containerPort:
env:
- name: VERSION
value: v1.0.0
livenessProbe:
httpGet:
path: /live
port: probe
initialDelaySeconds:
periodSeconds:
readinessProbe:
httpGet:
path: /ready
port: probe
periodSeconds:

app-v2.yaml 文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
replicas:
strategy:
type: Recreate
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
version: v2.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: ""
spec:
containers:
- name: my-app
image: containersol/k8s-deployment-strategies
ports:
- name: http
containerPort:
- name: probe
containerPort:
env:
- name: VERSION
value: v2.0.0
livenessProbe:
httpGet:
path: /live
port: probe
initialDelaySeconds:
periodSeconds:
readinessProbe:
httpGet:
path: /ready
port: probe
periodSeconds:

上面两个资源清单文件中的 Deployment 定义几乎是一直的,唯一不同的是定义的环境变量VERSION值不同,接下来按照下面的步骤来验证Recreate策略:

  1. 版本1提供服务
  2. 删除版本1
  3. 部署版本2
  4. 等待所有副本准备就绪

首先部署第一个应用:

$ kubectl apply -f app-v1.yaml
service "my-app" created
deployment.apps "my-app" created
 

测试版本1是否部署成功:

$ kubectl get pods -l app=my-app
NAME READY STATUS RESTARTS AGE
my-app-7b4874cd75-m5kct / Running 19m
my-app-7b4874cd75-pc444 / Running 19m
my-app-7b4874cd75-tlctl / Running 19m
$ kubectl get svc my-app
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-app NodePort 10.108.238.76 <none> :/TCP 5m
$ curl http://127.0.0.1:32532
Host: my-app-7b4874cd75-pc444, Version: v1.0.0
 

可以看到版本1的应用正常运行了。为了查看部署的运行情况,打开一个新终端并运行以下命令:

$ watch kubectl get po -l app=my-app

然后部署版本2的应用:

$ kubectl apply -f app-v2.yaml

这个时候可以观察上面新开的终端中的 Pod 列表的变化,可以看到之前的3个 Pod 都会先处于Terminating状态,并且3个 Pod 都被删除后才开始创建新的 Pod。

然后测试第二个版本应用的部署进度:

$ while sleep 0.1; do curl http://127.0.0.1:32532; done
curl: () Failed connect to 127.0.0.1:; Connection refused
curl: () Failed connect to 127.0.0.1:; Connection refused
......
Host: my-app-f885c8d45-sp44p, Version: v2.0.0
Host: my-app-f885c8d45-t8g7g, Version: v2.0.0
Host: my-app-f885c8d45-sp44p, Version: v2.0.0

可以看到最开始的阶段服务都是处于不可访问的状态,然后到第二个版本的应用部署成功后才正常访问,可以看到现在访问的数据是版本2了。

最后,可以执行下面的命令来清空上面的资源对象:

$ kubectl delete all -l app=my-app

结论:

  • 应用状态全部更新

  • 停机时间取决于应用程序的关闭和启动消耗的时间

滚动更新(rolling-update)

滚动更新通过逐个替换实例来逐步部署新版本的应用,直到所有实例都被替换完成为止。它通常遵循以下过程:在负载均衡器后面使用版本 A 的实例池,然后部署版本 B 的一个实例,当服务准备好接收流量时(Readiness Probe 正常),将该实例添加到实例池中,然后从实例池中删除一个版本 A 的实例并关闭,如下图所示:

ramped

下图是滚动更新过程应用接收流量的示意图:

rolling-update requests

下面是 Kubernetes 中通过 Deployment 来进行滚动更新的关键参数:

spec:
replicas:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: # 一次可以添加多少个Pod
maxUnavailable: # 滚动更新期间最大多少个Pod不可用

现在仍然使用上面的 app-v1.yaml 这个资源清单文件,新建一个定义滚动更新的资源清单文件 app-v2-rolling-update.yaml,文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
replicas:
# maxUnavailable设置为0可以完全确保在滚动更新期间服务不受影响,还可以使用百分比的值来进行设置。
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge:
maxUnavailable:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
version: v2.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: ""
spec:
containers:
- name: my-app
image: containersol/k8s-deployment-strategies
ports:
- name: http
containerPort:
- name: probe
containerPort:
env:
- name: VERSION
value: v2.0.0
livenessProbe:
httpGet:
path: /live
port: probe
initialDelaySeconds:
periodSeconds:
readinessProbe:
httpGet:
path: /ready
port: probe
# 初始延迟设置高点可以更好地观察滚动更新过程
initialDelaySeconds:
periodSeconds:

上面的资源清单中我们在环境变量中定义了版本2,然后通过设置strategy.type=RollingUpdate来定义该 Deployment 使用滚动更新的策略来更新应用,接下来我们按下面的步骤来验证滚动更新策略:

  1. 版本1提供服务
  2. 部署版本2
  3. 等待直到所有副本都被版本2替换完成

同样,首先部署版本1应用:

$ kubectl apply -f app-v1.yaml
service "my-app" created
deployment.apps "my-app" created

测试版本1是否部署成功:

$ kubectl get pods -l app=my-app
NAME READY STATUS RESTARTS AGE
my-app-7b4874cd75-h8c4d / Running 47s
my-app-7b4874cd75-p4l8f / Running 47s
my-app-7b4874cd75-qnt7p / Running 47s
$ kubectl get svc my-app
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-app NodePort 10.109.99.184 <none> :/TCP 1m
$ curl http://127.0.0.1:30486
Host: my-app-7b4874cd75-qnt7p, Version: v1.0.0

同样,在一个新终端中执行下面命令观察 Pod 变化:

$ watch kubectl get pod -l app=my-app

然后部署滚动更新版本2应用:

$ kubectl apply -f app-v2-rolling-update.yaml
deployment.apps "my-app" configured

这个时候在上面的 watch 终端中可以看到多了很多 Pod,还在创建当中,并没有一开始就删除之前的 Pod,同样,这个时候执行下面命令,测试应用状态:

$ while sleep 0.1; do curl http://127.0.0.1:30486; done
Host: my-app-7b4874cd75-vrlj7, Version: v1.0.0
......
Host: my-app-7b4874cd75-vrlj7, Version: v1.0.0
Host: my-app-6b5479d97f-2fk24, Version: v2.0.0
Host: my-app-7b4874cd75-p4l8f, Version: v1.0.0
......
Host: my-app-6b5479d97f-s5ctz, Version: v2.0.0
Host: my-app-7b4874cd75-5ldqx, Version: v1.0.0
......
Host: my-app-6b5479d97f-5z6ww, Version: v2.0.0

我们可以看到上面的应用并没有出现不可用的情况,最开始访问到的都是版本1的应用,然后偶尔会出现版本2的应用,直到最后全都变成了版本2的应用,而这个时候看上面 watch 终端中 Pod 已经全部变成10个版本2的应用了,我们可以看到这就是一个逐步替换的过程。

如果在滚动更新过程中发现新版本应用有问题,我们可以通过下面的命令来进行一键回滚:

$ kubectl rollout undo deploy my-app
deployment.apps "my-app"

如果你想保持两个版本的应用都存在,那么我们也可以执行 pause 命令来暂停更新:

$ kubectl rollout pause deploy my-app
deployment.apps "my-app" paused
 

这个时候我们再去循环访问我们的应用就可以看到偶尔会出现版本1的应用信息了。

如果新版本应用程序没问题了,也可以继续恢复更新:

$ kubectl rollout resume deploy my-app
deployment.apps "my-app" resumed

最后,可以执行下面的命令来清空上面的资源对象:

$ kubectl delete all -l app=my-app

结论:

  • 版本在实例之间缓慢替换
  • rollout/rollback 可能需要一定时间
  • 无法控制流量

蓝/绿(blue/green) - 最好用来验证 API 版本问题

蓝/绿发布是版本2 与版本1 一起发布,然后流量切换到版本2,也称为红/黑部署。蓝/绿发布与滚动更新不同,版本2(绿) 与版本1()一起部署,在测试新版本满足要求后,然后更新更新 Kubernetes 中扮演负载均衡器角色的 Service 对象,通过替换 label selector 中的版本标签来将流量发送到新版本,如下图所示:

blug/green

下面是蓝绿发布策略下应用方法的示例图:

blue/green request

在 Kubernetes 中,我们可以用两种方法来实现蓝绿发布,通过单个 Service 对象或者 Ingress 控制器来实现蓝绿发布,实际操作都是类似的,都是通过 label 标签去控制。

实现蓝绿发布的关键点就在于 Service 对象中 label selector 标签的匹配方法,比如我们重新定义版本1 的资源清单文件 app-v1-single-svc.yaml,文件内容如下:

apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
type: NodePort
ports:
- name: http
port:
targetPort: http
# 注意这里我们匹配 app 和 version 标签,当要切换流量的时候,我们更新 version 标签的值,比如:v2.0.0
selector:
app: my-app
version: v1.0.0
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
labels:
app: my-app
spec:
replicas:
selector:
matchLabels:
app: my-app
version: v1.0.0
template:
metadata:
labels:
app: my-app
version: v1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: ""
spec:
containers:
- name: my-app
image: containersol/k8s-deployment-strategies
ports:
- name: http
containerPort:
- name: probe
containerPort:
env:
- name: VERSION
value: v1.0.0
livenessProbe:
httpGet:
path: /live
port: probe
initialDelaySeconds:
periodSeconds:
readinessProbe:
httpGet:
path: /ready
port: probe
periodSeconds:

上面定义的资源对象中,最重要的就是 Service 中 label selector 的定义:

selector:
app: my-app
version: v1.0.0

版本2 的应用定义和以前一样,新建文件 app-v2-single-svc.yaml,文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
labels:
app: my-app
spec:
replicas:
selector:
matchLabels:
app: my-app
version: v2.0.0
template:
metadata:
labels:
app: my-app
version: v2.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: ""
spec:
containers:
- name: my-app
image: containersol/k8s-deployment-strategies
ports:
- name: http
containerPort:
- name: probe
containerPort:
env:
- name: VERSION
value: v2.0.0
livenessProbe:
httpGet:
path: /live
port: probe
initialDelaySeconds:
periodSeconds:
readinessProbe:
httpGet:
path: /ready
port: probe
periodSeconds:

然后按照下面的步骤来验证使用单个 Service 对象实现蓝/绿部署的策略:

  1. 版本1 应用提供服务
  2. 部署版本2 应用
  3. 等到版本2 应用全部部署完成
  4. 切换入口流量从版本1 到版本2
  5. 关闭版本1 应用

首先,部署版本1 应用:

$ kubectl apply -f app-v1-single-svc.yaml
service "my-app" created
deployment.apps "my-app-v1" created

测试版本1 应用是否部署成功:

$ kubectl get pods -l app=my-app
NAME READY STATUS RESTARTS AGE
my-app-v1-7b4874cd75-7xh6s / Running 41s
my-app-v1-7b4874cd75-dmq8f / Running 41s
my-app-v1-7b4874cd75-t64z7 / Running 41s
$ kubectl get svc -l app=my-app
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-app NodePort 10.106.184.144 <none> :/TCP 50s
$ curl http://127.0.0.1:31539
Host: my-app-v1-7b4874cd75-7xh6s, Version: v1.0.0

同样,新开一个终端,执行如下命令观察 Pod 变化:

$ watch kubectl get pod -l app=my-app

然后部署版本2 应用:

$ kubectl apply -f app-v2-single-svc.yaml
deployment.apps "my-app-v2" created

然后在上面 watch 终端中可以看到会多3个my-app-v2开头的 Pod,待这些 Pod 部署成功后,我们再去访问当前的应用:

$ while sleep 0.1; do curl http://127.0.0.1:31539; done
Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0
Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0
......

我们会发现访问到的都是版本1 的应用,和我们刚刚部署的版本2 没有任何关系,这是因为我们 Service 对象中通过 label selector 匹配的是version=v1.0.0这个标签,我们可以通过修改 Service 对象的匹配标签,将流量路由到标签version=v2.0.0的 Pod 去:

$ kubectl patch service my-app -p '{"spec":{"selector":{"version":"v2.0.0"}}}'
service "my-app" patched

然后再去访问应用,可以发现现在都是版本2 的信息了:

$ while sleep 0.1; do curl http://127.0.0.1:31539; done
Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0
Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0
......

如果你需要回滚到版本1,同样只需要更改 Service 的匹配标签即可:

$ kubectl patch service my-app -p '{"spec":{"selector":{"version":"v1.0.0"}}}'

如果新版本已经完全符合我们的需求了,就可以删除版本1 的应用了:

$ kubectl delete deploy my-app-v1

最后,同样,执行如下命令清理上述资源对象:

$ kubectl delete all -l app=my-app

结论:

  • 实时部署/回滚

  • 避免版本问题,因为一次更改是整个应用的改变

  • 需要两倍的资源

  • 在发布到生产之前,应该对整个应用进行适当的测试

金丝雀(Canary) - 让部分用户参与测试

金丝雀部署是让部分用户访问到新版本应用,在 Kubernetes 中,可以使用两个具有相同 Pod 标签的 Deployment 来实现金丝雀部署。新版本的副本和旧版本的一起发布。在一段时间后如果没有检测到错误,则可以扩展新版本的副本数量并删除旧版本的应用。

如果需要按照具体的百分比来进行金丝雀发布,需要尽可能的启动多的 Pod 副本,这样计算流量百分比的时候才方便,比如,如果你想将 1% 的流量发送到版本 B,那么我们就需要有一个运行版本 B 的 Pod 和 99 个运行版本 A 的 Pod,当然如果你对具体的控制策略不在意的话也就无所谓了,如果你需要更精确的控制策略,建议使用服务网格(如 Istio),它们可以更好地控制流量。

Canary

在下面的例子中,我们使用 Kubernetes 原生特性来实现一个穷人版的金丝雀发布,如果你想要对流量进行更加细粒度的控制,请使用豪华版本的 Istio。下面是金丝雀发布的应用请求示意图:

canary requests

接下来我们按照下面的步骤来验证金丝雀策略:

  1. 10个副本的版本1 应用提供服务
  2. 版本2 应用部署1个副本(意味着小于10%的流量)
  3. 等待足够的时间来确认版本2 应用足够稳定没有任何错误信息
  4. 将版本2 应用扩容到10个副本
  5. 等待所有实例完成
  6. 关闭版本1 应用

首先,创建版本1 的应用资源清单,app-v1-canary.yaml,内容如下:

apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
type: NodePort
ports:
- name: http
port:
targetPort: http
selector:
app: my-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
labels:
app: my-app
spec:
replicas:
selector:
matchLabels:
app: my-app
version: v1.0.0
template:
metadata:
labels:
app: my-app
version: v1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: ""
spec:
containers:
- name: my-app
image: containersol/k8s-deployment-strategies
ports:
- name: http
containerPort:
- name: probe
containerPort:
env:
- name: VERSION
value: v1.0.0
livenessProbe:
httpGet:
path: /live
port: probe
initialDelaySeconds:
periodSeconds:
readinessProbe:
httpGet:
path: /ready
port: probe
periodSeconds:
 

其中核心的部分也是 Service 对象中的 label selector 标签,不在具有版本相关的标签了,然后定义版本2 的资源清单文件,app-v2-canary.yaml,文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
labels:
app: my-app
spec:
replicas:
selector:
matchLabels:
app: my-app
version: v2.0.0
template:
metadata:
labels:
app: my-app
version: v2.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: ""
spec:
containers:
- name: my-app
image: containersol/k8s-deployment-strategies
ports:
- name: http
containerPort:
- name: probe
containerPort:
env:
- name: VERSION
value: v2.0.0
livenessProbe:
httpGet:
path: /live
port: probe
initialDelaySeconds:
periodSeconds:
readinessProbe:
httpGet:
path: /ready
port: probe
periodSeconds:

版本1 和版本2 的 Pod 都具有一个共同的标签app=my-app,所以对应的 Service 会匹配两个版本的 Pod。

首先,部署版本1 应用:

$ kubectl apply -f app-v1-canary.yaml
service "my-app" created
deployment.apps "my-app-v1" created

然后测试版本1 应用是否正确部署了:

$ kubectl get svc -l app=my-app
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-app NodePort 10.105.133.213 <none> :/TCP 47s
$ curl http://127.0.0.1:30760
Host: my-app-v1-7b4874cd75-tsh2s, Version: v1.0.0

同样,新开一个终端,查看 Pod 的变化:

$ watch kubectl get pod

然后部署版本2 应用:

$ kubectl apply -f app-v2-canary.yaml
deployment.apps "my-app-v2" created

然后在 watch 终端页面可以看到多了一个 Pod,现在一共 11 个 Pod,其中只有1 个 Pod 运行新版本应用,然后同样可以循环访问该应用,查看是否会有版本2 的应用信息:

$ while sleep 0.1; do curl http://127.0.0.1:30760; done
Host: my-app-v1-7b4874cd75-bhxbp, Version: v1.0.0
Host: my-app-v1-7b4874cd75-wmcqc, Version: v1.0.0
Host: my-app-v1-7b4874cd75-tsh2s, Version: v1.0.0
Host: my-app-v1-7b4874cd75-ml58j, Version: v1.0.0
Host: my-app-v1-7b4874cd75-spsdv, Version: v1.0.0
Host: my-app-v2-f885c8d45-mc2fx, Version: v2.0.0
......

正常情况下可以看到大部分都是返回的版本1 的应用信息,偶尔会出现版本2 的应用信息,这就证明我们的金丝雀发布成功了,待确认了版本2 的这个应用没有任何问题后,可以将版本2 应用扩容到10 个副本:

$ kubectl scale --replicas= deploy my-app-v2
deployment.extensions "my-app-v2" scaled

其实这个时候访问应用的话新版本和旧版本的流量分配是1:1了,确认了版本2 正常后,就可以删除版本1 的应用了:

$ kubectl delete deploy my-app-v1
deployment.extensions "my-app-v1" deleted

最终留下的是 10 个新版本的 Pod 了,到这里我们的整个金丝雀发布就完成了。

同样,最后,执行下面的命令删除上面的资源对象:

$ kubectl delete all -l app=my-app

结论:

  • 部分用户获取新版本
  • 方便错误和性能监控
  • 快速回滚
  • 发布较慢
  • 流量精准控制很浪费(99%A / 1%B = 99 Pod A,1 Pod B)

如果你对新功能的发布没有信心,建议使用金丝雀发布的策略。

A/B测试(A/B testing) - 最适合部分用户的功能测试

A/B 测试实际上是一种基于统计信息而非部署策略来制定业务决策的技术,与业务结合非常紧密。但是它们也是相关的,也可以使用金丝雀发布来实现。

除了基于权重在版本之间进行流量控制之外,A/B 测试还可以基于一些其他参数(比如 Cookie、User Agent、地区等等)来精确定位给定的用户群,该技术广泛用于测试一些功能特性的效果,然后按照效果来进行确定。

我们经常可以在今日头条的客户端中就会发现有大量的 A/B 测试,同一个地区的用户看到的客户端有很大不同。

要使用这些细粒度的控制,仍然还是建议使用 Istio,可以根据权重或 HTTP 头等来动态请求路由控制流量转发。

ab test

下面是使用 Istio 进行规则设置的示例,因为 Istio 还不太稳定,以下示例规则将来可能会更改:

route:
- tags:
version: v1.0.0
weight:
- tags:
version: v2.0.0
weight:

关于在 Istio 中具体如何做 A/B 测试,我们这里就不再详细介绍了,我们在istio-book文档中有相关的介绍。

ab test request

结论:

  • 几个版本并行运行
  • 完全控制流量分配
  • 特定的一个访问错误难以排查,需要分布式跟踪
  • Kubernetes 没有直接的支持,需要其他额外的工具

总结

发布应用有许多种方法,当发布到开发/测试环境的时候,重建或者滚动更新通常是一个不错的选择。在生产环境,滚动更新或者蓝绿发布比较合适,但是新版本的提前测试是非常有必要的。如果你对新版本的应用不是很有信心的话,那应该使用金丝雀发布,将用户的影响降到最低。最后,如果你的公司需要在特定的用户群体中进行新功能的测试,例如,移动端用户请求路由到版本 A,桌面端用户请求路由到版本 B,那么你就看使用A/B 测试,通过使用 Kubernetes 服务网关的配置,可以根据某些请求参数来确定用户应路由的服务。

k8s-部署策略的更多相关文章

  1. K8S学习笔记之Kubernetes 部署策略详解

    0x00 概述 在Kubernetes中有几种不同的方式发布应用,所以为了让应用在升级期间依然平稳提供服务,选择一个正确的发布策略就非常重要了. 选择正确的部署策略是要依赖于我们的业务需求的,下面我们 ...

  2. 微服务实战(六):选择微服务部署策略 - DockOne.io

    原文:微服务实战(六):选择微服务部署策略 - DockOne.io [编者的话]这篇博客是用微服务建应用的第六篇,第一篇介绍了微服务架构模板,并且讨论了使用微服务的优缺点.随后的文章讨论了微服务不同 ...

  3. 使用k8s部署springboot+redis简单应用

    准备 本文将使用k8s部署一个springboot+redis应用,由于是示例,所以功能比较简单,只有设置值和获取值两个api. (1)设置值 (2)获取值 构建Web应用 (1)创建一个spring ...

  4. K8S 部署 SpringBoot 项目(一篇够用)

    现在比较多的互联网公司都在尝试将微服务迁到云上,这样的能够通过一些成熟的云容器管理平台更为方便地管理微服务集群,从而提高微服务的稳定性,同时也能较好地提升团队开发效率. 但是迁云存在一定的技术难点,今 ...

  5. <自动化测试方案_10>第十章、自动化测试部署策略

    第十章.自动化测试部署策略 按照以下顺序部署自动化框架 1,部署API自动化    时间:三周2,部署PC端UI自动化 时间:1个月 3,部署Sonar代码质量管理 时间:1个月 4,集成到Jenki ...

  6. Python服务Dokcer化并k8s部署实例

    这篇文章记录了我试验将一个基于python的服务docker化并k8s部署的过程. 服务介绍Docker化设计业务代码改造创建docker镜像K8S部署设计yaml文件运行服务介绍这是一个用 pyth ...

  7. .NET持续集成与自动化部署之路第三篇——测试环境到生产环境的一键部署策略(Windows)

    Jenkins测试环境到生产环境的一键部署策略(Windows) 一.前言     前面我们已经初步实现了开发集成环境.测试环境的持续集成(自动化构建.自动化测试.自动化部署).但生产环境自动化部署迟 ...

  8. ballerina 学习二十七 项目k8s部署&& 运行

    ballerina k8s 部署和docker 都是同样的简单,编写service 添加注解就可以了 参考项目 https://ballerina.io/learn/by-guide/restful- ...

  9. ArcGIS 10.3 for Server部署策略

    注:以下部署策略整理自官方文档,主要是为方便以后查阅各种部署方案. 1.单机部署与反向代理 1.1.反向代理 反向代理可采用ArcGIS Web Adaptor或者第三方的反向代理服务器. 1.2.部 ...

  10. Rancher+K8S部署手册

    目前创建K8S集群的安装程序最受欢迎的有Kops,Kubespray,kubeadm,rancher,以及个人提供的脚本集等. Kops和Kubespary在国外用的比较多,没有处理中国的网络问题,没 ...

随机推荐

  1. ICEM-三棱锥的一种画法(2D转3D)

    原视频下载地址:https://yunpan.cn/cqwC3eZHn5AvJ  访问密码 9456

  2. C# 控制反转

    主要目的:需要在业务逻辑层调用UI的弹框提示信息代码是在记事本中写的,没有运行调试过,不保证能直接使用,看下思路就好 //接口public interface IShowData { void Sho ...

  3. Redis Desktop Manager的下载及安装

    一.下载Redis Desktop Manager 1. Redis Desktop Manager 的下载路径 (1)https://pan.baidu.com/s/1Jvr9MbgFn4UJh4M ...

  4. Can I prevent the Firefox developer tools network panel from clearing on page reload?

    Can I prevent the Firefox developer tools network panel from clearing on page reload? I couldn't fin ...

  5. zabbix 定义触发器,并使用邮件,微信消息报警。

    触发器可根据监控项获取到的值来进行一些操作,如监控项获取到的values为0,触发器可判断为正常,如果获取到了1,就触发报警. 定义报警方式比较简单,但是用shell脚本实现起来,总是有格式问题,所以 ...

  6. C#实现获取当前文件路径的上级路径

    界面: 声明: textBox1.Text为指定文件路径:string path = @"F:\ABB-pragram\ABB工作站\ABB Station\Systems\Situatio ...

  7. jquery获取html中当前元素对象,以及父对象,相邻的上一个对象,或下一个对象

    jsp代码: <span><input type="hidden" value="1" id="newInfo">& ...

  8. List&Set

    List a.普通for循环, 使用get()逐个获取 b.调用iterator()方法得到Iterator, 使用hasNext()和next()方法 c.增强for循环, 只要可以使用Iterat ...

  9. Zend Studio汉化失败,如何给Zend Studio进行汉化

    首先,相信看我这篇博文的人也都遇到了和博主我一样的烦恼,就是汉化Zend Studio失败! 话不多说! 方案一,在线安装汉化包 Help–>Install New Software—>W ...

  10. PAT 甲级 1043 Is It a Binary Search Tree (25 分)(链表建树前序后序遍历)*不会用链表建树 *看不懂题

    1043 Is It a Binary Search Tree (25 分)   A Binary Search Tree (BST) is recursively defined as a bina ...