• K8s挂了找不到Docker镜像该怎么整?
  • 发布于 2个月前
  • 153 热度
    0 评论
想象这样一个场景:
某天K8s集群不知道什么原因挂了,然后DevOps工程师重新部署一套K8s,然后将应用部署到新的集群。当DevOps工程师通过容器平台自动化部署服务X到K8s时,服务X起不来。经查,原因是该node下没有服务X的v30版本Docker镜像,而且远程的制品仓库也没有。再深层次的原因是业务开发人员遵从了“降本增效”的建议,在制品仓库中设置了自动清理3个月自动清理的功能。v30版本在3个月前就已经被制品库删除。

这时,业务开发人员开始想办法从以前部署的代码版本(v30版本对应的commit)中重新构建一个Docker镜像。但是他们发现怎么构建都不成功。经查,原因是前端代码依赖的一个库Y,最近做了一个不兼容老版本的改动。再深层次原因是前端代码的依赖管理文件package.json中定义的依赖,在定义时使用了“自动升级”子版本的功能。如下:
{
  "dependencies": {
    "a": "^1.81.0",
    "b": "^5.2.0",
  }
}
而业务开发人员也不知道依赖Y是在哪个版本做了不兼容的改动。所以业务人员需要花“大力气”才能构建出v30版本的镜像。当然,作为SRE,应该还要想到另一个办法来补救:服务X运行过的节点上应该还保留着该镜像版本,可以将其导出,再手工重新上传到镜像仓库中。

以上场景为什么会发生?事情就是这么巧合!K8s挂了,部署应用又刚好找不到Docker镜像,Docker镜像又刚好被自动化清理了,某个应用又刚好无法进行镜像重建。

我们的DevOps平台应该如何设计以避免以上场景的发生呢?

个人总结下来,发生以上场景的根本原因有两个:
1. 制品缺少生命周期管理,或者生命周期管理存在缺陷;
2. 应用代码没有遵从可重复构建原则:使用同样的源代码,每次构建出来的结果应该是一致的。

制品生命周期管理
DevOps平台对制品的生命周期管理存在缺陷,是出现上文所提到的事故的重要原因之一。为什么?我们不应该设置3个月自动清理吗?答案是确定的。错误的是不应该清理运行环境中正在运行的制品版本。这时,我们就应该清楚真正的问题出现在哪里了:制品库并不知道哪些制品是正在被使用的。为什么会不知道呢?因为通常DevOps平台下的制品平台、发布平台、代码托管平台、测试平台是由不同的团队分别建设的,它们是一个个的信息孤岛。

那么,我们应该通过什么方式来解决制品不知道哪些制品正在被使用的呢?简单地说就给制品打标签。各个平台在制品平台上给制品打标签,制品平台根据标签而采用不同的处理方式。

比如,当发布平台部署服务X v41版本后,就自动在制品平台上给服务X的v41版本打一个while_using:prod,如果是灰度发布,还可以打:while_using: prod_grey。同时将之前v40版本上的while_using标签移除。而制品平台在执行3个月自动清理时,就自动跳过有标签以"while_using"开头的制品。

但是,这个机制依然可能存在问题。就是在v41版本被自动清理后,某天业务开发需要使用v40进行问题复现,怎么办?
这时就要求我们的团队要做到可重复构建了。

可重复构建
在持续集成领域,有一个重要原则叫做“可重复构建”(Repeatable Builds)。它的意思是,使用同样的源代码,每次构建出来的结果应该是一致的,不应该有不同。它也代表了团队的一种工程化能力。前端代码是不可重复构建的典范。看看npm的官方案例,就知道了,大量使用自动升级依赖版本策略。

我们的DevOps平台该如何避免出现不可重复构建的现象呢?也就是通过平台来实现团队的可重复构建的能力。

可以在几个层面实现:
1. 在写代码的层面。当用户在IDE或者VSCODE中定义依赖时,相关插件,就提示不可重复构建;
2. pre-commit层面。当用户在本地commit时,就运行pre-commit脚本检测,检测到不可重复构建,就禁止用户提交代码;
3. 在CI流水线层面。出现不可重复构建,就直接停止构建。

在2,3实现不可重复构建功能,成本是相对较低的。
用户评论