Kubernetes:Pod 升级、回滚
<p>本篇主要讨论如何实现滚动更新和回滚,任意更换版本并且回滚以前的版本(版本更新),而下一章会讨论到 Pod 缩放,根据机器资源自动拓展和收缩应用(自动扩容实例)。</p><p>本文为作者的 Kubernetes 系列电子书的一部分,电子书已经开源,欢迎关注,电子书浏览地址:</p>
<p>https://k8s.whuanle.cn【适合国内访问】</p>
<p>https://ek8s.whuanle.cn 【gitbook】</p>
<h2 id="滚动更新和回滚">滚动更新和回滚</h2>
<h3 id="部署应用">部署应用</h3>
<p>首先我们来部署 nginx,使用 nginx 作为练习的镜像。</p>
<p>打开 https://hub.docker.com/_/nginx 可以查询 nginx 的镜像版本,笔者这里选择三个版本:<code>1.19.10</code>、<code>1.20.0</code>、<code>latest</code>,后续我们更新和回滚时,会在这几个版本之间选择。</p>
<blockquote>
<p><strong> 提示</strong></p>
<p>需要读者明确选择nginx 的三个不同版本,我们后面的升级回滚练习会在这三个版本中来回切换。</p>
<pre class="language-"><code>1.19.10 -> 1.20.0 -> latest
</code></pre>
</blockquote>
<p>首先,我们创建一个 Nginx 的 Deployment,副本数量为 3,首次部署的时候,跟之前的操作一致,不需要什么特殊的命令。这里我们使用旧一些的版本,笔者使用的是 1.19.0。</p>
<pre class="language-"><code>kubectl create deployment nginx --image=nginx:1.19.0 --replicas=3
# 或者
# kubectl create deployment nginx --image=nginx:1.19.0 --replicas=3 --record
</code></pre>
<blockquote>
<p><strong>注:</strong> 我们也可以加上 <code>--record</code> 标志将所执行的命令写入资源注解 <code>kubernetes.io/change-cause</code> 中。 这对于以后的检查是有用的。例如,要查看针对每个 Deployment 修订版本所执行过的命令,对于这个参数的作用,我们后面再解释。</p>
</blockquote>
<p>执行 <code>kubectl get pods</code>、<code>kubectl describe pods</code>可以观察到有有三个 Pod,每个 Pod 的 nginx 镜像版本都是 1.19.0。</p>
<pre class="language-"><code>NAME READY STATUS RESTARTS AGE
nginx-85b45874d9-7jlrv 1/1 Running 0 5s
nginx-85b45874d9-h22xv 1/1 Running 0 5s
nginx-85b45874d9-vthfb 1/1 Running 0 5s
</code></pre>
<pre class="language-"><code>Events:
Type Reason Age From Message
---- ------ -------- -------
NormalScheduled119sdefault-schedulerSuccessfully assigned default/nginx-85b45874d9-vthfb to instance-2
NormalPulled 117skubelet Container image "nginx:1.19.0" already present on machine
NormalCreated 117skubelet Created container nginx
NormalStarted 117skubelet Started container nginx
</code></pre>
<h3 id="更新版本">更新版本</h3>
<p>其实更新 Pod 是非常简单的,我们不需要控制每个 Pod 的更新,也不需要担心会不会对业务产生影响,K8S 会自动控制这些过程。我们只需要触发镜像<strong>版本更新事件</strong>,K8S 会自动为我们更新所有 Pod 的。</p>
<p><code>kubectl set image</code> 可以更新现有资源对象的容器镜像,对象包括 <code>Pod</code>、<code>Deployment</code>、<code>DaemonSet</code>、<code>Job</code>、<code>ReplicaSet</code>。在更新版本中,单个容器的 Pod,对于多个容器的 Pod 行为是差不多的,所以我们使用单容器 Pod 练习即可。</p>
<p>更新 Deployment 中的镜像版本,触发 Pod:</p>
<pre class="language-"><code>kubectl set image deployment nginx nginx=nginx:1.20.0
</code></pre>
<p>格式为:</p>
<pre class="language-"><code>kubectl set image deployment {deployment名称} {镜像名称}:={镜像名称}:{版本}
</code></pre>
<blockquote>
<p>此命令可以任意修改 Pod 中的其中一个容器的版本,只要某个容器的镜像版本变化,整个 Pod 都会重新部署。如果镜像版本没有变化,即使是执行了 <code>kubectl set image</code> ,也不会产生影响。</p>
</blockquote>
<div class="mermaid" data-processed="true">
<div id="dmermaidChart0"></div>
</div>
<p>我们可以查看 Pod 的详细信息:</p>
<pre class="language-"><code>kubectl describe pods
</code></pre>
<p>找到 Events 描述:</p>
<pre class="language-"><code>... ...
Events:
Type Reason Age From Message
---- ------ -------- -------
NormalScheduled66s default-schedulerSuccessfully assigned default/nginx-7b87485749-rlmcx to instance-2
NormalPulled 66s kubelet Container image "nginx:1.20.0" already present on machine
NormalCreated 66s kubelet Created container nginx
NormalStarted 65s kubelet Started container nginx
</code></pre>
<p>可以看到,现在现在创建的 Pod 实例为 1.20.0 版本。</p>
<blockquote>
<p>更新过程中,会创建新版本的 Pod,旧的 Pod 会被逐渐移除。</p>
</blockquote>
<p>我们在创建 Deployment 时,生成了三个 Pod ,而当我们触发镜像版本更新时,Pod 不会一次性更新,而是按照一定规则每次只重新部署一部分 Pod,Pod 更新替换过程类似下图所示(实际上 Pod 数量可能大于 3个):</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202112/1315495-20211203090116887-867223603.png" alt="pod更新替换过程"></p>
<p>另外,我们还可以通过 <code>kubectl edit yaml</code> 的方式方式更新 Pod。</p>
<p>执行:</p>
<pre class="language-"><code>kubectl edit deployment nginx
</code></pre>
<p>然后会弹出编辑 YAML 的界面,将 <code>.spec.template.spec.containers.image</code> 从 <code>nginx:1.19.0</code> 更改至 <code>nginx:1.20.0</code>,然后保存即可。</p>
<blockquote>
<p>为了记录版本更新信息,我们需要在 <code>kubectl create deployment</code>、<code>kubectl set image</code> 命令后面加上 <code>--record</code>。</p>
<p>别忘记了 <code>kubectl scale</code> 命令也可以更改副本数量。</p>
</blockquote>
<h3 id="上线">上线</h3>
<p>仅当 Deployment Pod 模板(即 <code>.spec.template</code>)发生改变时,例如模板的标签或容器镜像被更新, 才会触发 Deployment 上线。</p>
<p>其他更新(如对 Deployment 执行扩缩容的操作)不会触发上线动作,Deployment 的上线动作可以为我们更新 Pod 的版本(Pod 中的容器版本)。</p>
<p>这里提到的 上线/更新版本 是因为容器版本会发生变化,而更新一般指修改了 YAML 等,不一定会对容器产生影响。</p>
<p>当我们更新 Pod 版本时,K8S 会自动负载均衡,而不是把所有 pod 删除,再重新创建新版本 Pod,<strong>它会以稳健的方式逐渐替换 Pod 副本</strong>,所以叫<strong>滚动更新</strong>。</p>
<p>我们可以通过 <code>kubectl rollout status</code> 命令,查看 Pod 的上线状态,即 Pod 副本集的更替状态:</p>
<pre class="language-"><code>kubectl rollout status deployment nginx
</code></pre>
<p>输出结果一般有两种:</p>
<pre class="language-"><code># 已经完成时:
deployment "nginx-deployment" successfully rolled out
# 还在更新时:
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
</code></pre>
<p>我们也可以通过获取 Deployment 信息时,查看已更新的 pod 数量:</p>
<pre class="language-"><code>kubectl get deployment
</code></pre>
<pre class="language-"><code>NAME READY UP-TO-DATE AVAILABLE AGE
nginx 3/3 3 3 18m
</code></pre>
<p>UP-TO-DATE 字段可以看到成功更新的 pod 数量。</p>
<p>还可以查看 ReplicaSet 和 Pod:</p>
<pre class="language-"><code>kubectl get replicaset
kubectl get pods
</code></pre>
<p>输出类型于:</p>
<pre class="language-"><code>NAME DESIRED CURRENT READY AGE
nginx-7b87485749 0 0 0 20m
nginx-85b45874d9 3 3 3 21m
</code></pre>
<pre class="language-"><code>NAME READY STATUS RESTARTS AGE
nginx-85b45874d9-nrbg8 1/1 Running 0 12m
nginx-85b45874d9-qc7f2 1/1 Running 0 12m
nginx-85b45874d9-t48vw 1/1 Running 0 12m
</code></pre>
<p>可以看到有两个 ReplicaSet,nginx-7b87485749 是 1.19.0 版本,已经被全部更新到 1.20.0 版本 了,所以前者的数量为 0,我们也可以看到 Pod 中,所有 Pod 都是以 <code>nginx-85b45874d9</code> 作为前缀的。这几个关键信息,我们可以截图,后面再次对照。</p>
<h3 id="如何滚动更新">如何滚动更新</h3>
<p>我们更新 镜像版本时,旧的 Pod 版本会被替换,但是 ReplicaSet 副本记录是不会被删除的。实际上滚动更新就是控制副本数量,原本 1.19.0 的副本数量为 3,现在变成 0,1.20.0 的副本数量变成 3 。</p>
<p>如果我们的项目上线了,我们更新软件版本,如果一次性更新所有容器或者 pod,那么我们的软件会有一段时间处于不可用状态,直到所有 Pod 都完成更新。</p>
<p>Deployment 可确保在更新时仅关闭一定数量的 Pod,默认情况下,它确保至少所需 Pods 75% 处于运行状态,也就是说正在被更新的 Pod 比例不超过 25%。当然,只有两三个 pod 的 Deployment 不会按照这个比例限定。也就是说,Deployment 等处于滚动更新状态时,其始终可以保证有可用的 Pod 提供服务。</p>
<p>如果我们的 Pod 数量足够大,或者在更新 Deployment 时迅速输出上线状态,可以看到新旧的 Pod 数量加起来不一定就是 3 个,因为它不会杀死老 Pods,直到有足够的数量新的 Pods 已经出现。 在足够数量的旧 Pods 被杀死前并没有创建新 Pods。当副本数量为3个时,它确保至少 2 个 Pod 可用,同时 最多总共 4 个 Pod 存在(不同版本)。</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202112/1315495-20211203090116766-777799862.png" alt="新旧pod"></p>
<p>滚动更新过程如下图所示:</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202112/1315495-20211203090116894-1861858830.png" alt="滚动更新过程"></p>
<p>Deployment 确保仅所创建 Pod 数量只可能比期望 Pods 数高一点点。 默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 25%(最大峰值 25%)所以在自动更新 Deployment 时,观察到的 pod 可能为 4个,这是由 Deployment 的缩放配置决定的(下一章讲解)。另外,在 Deployment 更新时,除了可以更改镜像的版本,也可以更改 ReplicaSet 的数量。</p>
<p>执行 <code>kubectl describe deployment nginx</code> 查看 Deployment 详细信息,我们查看 Event 字段,也可以观察到新旧 Pod 的更替过程。</p>
<p><img src="https://img2020.cnblogs.com/blog/1315495/202112/1315495-20211203090116843-260265122.png" alt="新旧pod创建过程"></p>
<p>但是这些原理等知识我们都不需要记,也不需要深入,我们记得有这回事就行,有需要的时候也可以直接查看文档的,后面的章节还会详细介绍 ReplicaSet 的规则。</p>
<h3 id="查看上线记录">查看上线记录</h3>
<p>默认情况下, Deployment 的上线记录都会保留在系统中,以便可以随时回滚,前面我们也提到了查看 <code>kubectl get replicasets</code> 时出现的副本记录。</p>
<p>我们查看 Deployment 的上线历史记录:</p>
<pre class="language-"><code>kubectl rollout history deployment nginx
</code></pre>
<pre class="language-"><code>REVISIONCHANGE-CAUSE
1
2
# 带 --record 的话,输出是
REVISIONCHANGE-CAUSE
2 kubectl set image deployment nginx nginx=nginx:1.20.0 --record=true
</code></pre>
<p>可以看到有两个版本,但是<code>CHANGE-CAUSE</code> 为 <code><span class="token tag"><span class="token punctuation"><</span>none<span class="token punctuation">></span></span></code> 呢?这是因为笔者没有使用 <code>--record</code> 参数记录信息,如果没带上 <code>--record</code> 的话,我们看着这个历史记录,完全分不出到底是什么版本。</p>
<p>现在我们查看 版本2 的详细信息:</p>
<pre class="language-"><code>kubectl rollout history deployment nginx --revision=2
</code></pre>
<pre class="language-"><code>deployment.apps/nginx with revision #2
Pod Template:
Labels: app=nginx
pod-template-hash=85b45874d9
Containers:
nginx:
Image: nginx:1.20.0
Port:
Host Port:
Environment:
Mounts:
Volumes:
</code></pre>
<h3 id="回滚">回滚</h3>
<p>当部署的新版本程序发现严重 bug 影响平台稳定性时,你可能需要将项目切换为上一个版本。目前介绍了几个查看 Deployment 上线的历史记录的命令,下面介绍如果将 Pod 换到旧的版本。</p>
<p>回滚到上一个版本的命令:</p>
<pre class="language-"><code>root@master:~# kubectl rollout undo deployment nginx
deployment.apps/nginx rolled back
</code></pre>
<blockquote>
<p>例如当前是 版本2,那么会回滚到 版本1。</p>
</blockquote>
<p>再执行 <code>kubectl rollout history deployment nginx</code> 会发现 revision 变成 3 了。</p>
<pre class="language-"><code>root@master:~# kubectl rollout history deployment nginx
deployment.apps/nginx
REVISIONCHANGE-CAUSE
2
3
</code></pre>
<blockquote>
<p>revision 记录的是部署记录,与 Pod 的镜像版本无关,每次更新版本或进行回滚等操作时, revision 会自动递增 1。</p>
</blockquote>
<p>如果版本数量多了,我们还可以指定回滚到特点的版本。</p>
<pre class="language-"><code>kubectl rollout undo deployment nginx --to-revision=2
</code></pre>
<p>这里提一下 <code>--record</code>,在前面,我们创建和更新 Deployment 时,都没有使用到这个参数,其实这个参数很有用的,接下来我们每次执行滚动更新时都要带上这个参数才行。</p>
<p>更新镜像到指定版本:</p>
<pre class="language-"><code>kubectl set image deployment nginx nginx=nginx:1.19.0 --record
</code></pre>
<pre class="language-"><code>kubectl rollout history deployment nginx
</code></pre>
<p>输出:</p>
<pre class="language-"><code>REVISIONCHANGE-CAUSE
5
6 kubectl set image deployment nginx nginx=nginx:1.19.0 --record=true
</code></pre>
<p>但是我们这里目前来说,只有两个记录,我们明明提交了多次,虽然 revision 会变化,但是这里查询的只有两条记录,这时因为我们操作的时候,只用到了 1.19.0、1.20.0 两个版本,所以也就只有这两个版本的提交记录。多用几个版本,输出结果:</p>
<pre class="language-"><code>REVISIONCHANGE-CAUSE
7 kubectl set image deployment nginx nginx=nginx:1.19.0 --record=true
8 kubectl set image deployment nginx nginx=nginx:1.20.0 --record=true
9 kubectl set image deployment nginx nginx=nginx:latest --record=true
</code></pre>
<p>REVISION 字段的数字是会递增的,当我们触发上线动作(容器标签、版本等)时,会产生新的上线记录。</p>
<h3 id="暂停上线">暂停上线</h3>
<p>本小节需要水平缩放、比例缩放等知识,请先阅读 3.6 章关于缩放的内容。</p>
<p>如果在上线过程中,发现机器不够用了,或者需要调整一些配置等,可以暂停上线过程。</p>
<p><code>kubectl rollout pause</code> 命令可以让我们在 Deployment 的 Pod 版本时,暂停滚动更新。</p>
<p>命令:</p>
<pre class="language-"><code>kubectl rollout pause deployment nginx
</code></pre>
<p>在滚动更新过程中,会有一些现象需要我们留意。</p>
<p>先创建一个 Deployment 或者更新 Deployment 的 Pod 为 10 个副本。</p>
<pre class="language-"><code>kubectl create deployment --image=nginx:1.19.0 --replicas=10
</code></pre>
<p>我们执行 <code>kubectl edit deployment nginx</code> 修改缩放个数:</p>
<pre class="language-"><code>strategy:
rollingUpdate:
maxSurge: 3
maxUnavailable: 2
type: RollingUpdate
</code></pre>
<blockquote>
<p>设置了这个 maxSurge 和 maxUnavailable,可以让 Deployment 替换 Pod 时慢一些。</p>
</blockquote>
<p>之前我们已经使用了 <code>1.19.0</code>、<code>1.20.0</code> 两个版本进行演示,这里我们使用 <code>latest</code> 版本进行实践。</p>
<p>复制以下两条命令快速执行,可以快速卡住上线过程。我们暂停上线后,查看一些状态信息。</p>
<pre class="language-"><code>kubectl set image deployment nginx nginx=nginx:latest
kubectl rollout pause deployment nginx
</code></pre>
<p>执行 <code>kubectl get replicaset</code> 查看这些版本的数量。</p>
<pre class="language-"><code>NAME DESIRED CURRENT READY AGE
nginx-7b87485749 8 8 8 109m
nginx-85b45874d9 0 0 0 109m
nginx-bb957bbb5 5 5 5 52m
</code></pre>
<blockquote>
<p>可以看到,所有的 Pod 加起来数量大于 10,旧容器以每次 2 个的数量减少;新容器以每次 3 个的数量创建;暂停上线后,多次执行 <code>kubectl get replicaset</code> ,会发现副本数量不会变化。</p>
</blockquote>
<p>前面我们已经暂停了上线,如果我们执行上线命令换成别的版本:</p>
<pre class="language-"><code>kubectl set image deployment nginx nginx=nginx:1.19.0
</code></pre>
<p>会发现虽然提示更新了,但是实际上没有变化。执行 <code>kubectl rollout history deployment nginx</code> 也查不到我们提交的 <code>1.19.0</code> 的请求。这是因为在已经暂停上线的控制器对象中,执行新的上线动作是无效的。</p>
<p>暂停的时候,我们可以更新一些配置,例如限制 Pod 中的 nginx 容器使用的 CPU 和 资源:</p>
<pre class="language-"><code>kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
</code></pre>
<p>再恢复 Deployment 上线:</p>
<pre class="language-"><code>kubectl rollout resume deployment nginx</code></pre>
</div>
<div id="MySignature" role="contentinfo">
痴者工良(https://whuanle.cn)<br><br>
来源:https://www.cnblogs.com/whuanle/p/15636500.html
頁:
[1]