在我作为一名SRE(网站可靠性工程师)的经历中,我不得不将一些应用程序从虚拟机迁移到Kubernetes中。所以,我想特别谈谈这个,一步步解释如何在Kubernetes集群中迁移应用程序。为此,我将分享一些最佳操作,实现一步一步成功迁移。显然,即使我努力尝试过,但也无法谈论关于迁移的所有案例。
应用知识
首先,你需要了解你的应用程序,至少有一个人必须要非常了解它。如果没有人了解应用程序,你将需要大量时间来回答这些问题:
它是无状态的还是有状态的?
有状态的应用程序意味着它会根据用户的操作而变化,例如,一款用于存储文件的应用程序将是有状态的,这些文件是用户通过操作在文件系统中创建的。
这个答案很重要,无状态应用程序的迁移比有状态应用程序的迁移容易得多。在无状态服务的情况下,你无需执行任何操作。而在另一种情况下(有状态应用程序),你需要知道哪些文件是有状态的以及它们存储在文件系统中的什么位置。因此,对应用程序有很好的了解是必要的。
那么,你有两种选择:
- 修改代码以获得无状态应用程序;
- 保持当前状态并使用基础设施组件来管理应用程序状态。
第一种选择是不考虑业务环境的理想方式。为什么?因为维护一个有状态的组件并迁移它更难。
以下是无状态服务的优点:
- 你将拥有一个完全适应Kubernetes的应用程序;
- 你将减少维护它的时间。
以下是它的缺点:
- 项目迁移可能要另行安排(迁移准备需更多时间);
- 向无状态组件的转变可能既困难又漫长。
第二个选择并不理想,但它也有一些优点,比如:
- 你现在就可以开始迁移项目;
- 它比代码重构花费更少的时间。
它也有两个缺点:
- 维护应用程序会更难(但并非不可能,别担心!);
- 你必须利用其他基础架构组件来管理应用程序状态。
为了管理状态,稍后你将看到有哪些不同的可能性。总之,如果你有时间重构你的应用程序,那就去做吧!从长远来看,这将对你有利。但是如果你没有时间,请选择第二个选项。
依赖项有哪些?
了解代码架构及其依赖项可以让你预测潜在的问题,检查应用程序和依赖项之间的所有网络连接是否可行。
要迁移的组件有哪些?
根据你要迁移的组件列表,迁移的方式会有所不同。比如,你是否需要将数据库(在另一个地方)和应用程序一起迁移?
你的应用程序是否使用了session stickiness?
session stickiness是在用户和应用程序实例之间创建亲和性的过程,不在集群中执行session stickiness机制会给用户带来一些麻烦,因为你的应用程序将有多个副本。不过不用担心,你可以在基于Kubernetes的基础架构中管理它们。
你如何配置你的应用程序?
了解如何配置应用程序很重要,它是否使用环境变量?是在一个还是多个应用程序文件夹中?
最好是使用环境变量,因为它更容易在Kubernetes中管理。
应用程序是如何启动的?
你需要所有与你的服务相关的命令,比如:
- 启动应用程序的命令
- 构建应用程序包的命令
- 其他如数据库迁移命令。(其他命令,如数据库迁移的命令)
信息的收集已经完成!现在可以容器化你的应用程序!
应用程序的容器化
现在,让我们将它容器化!以下是容器化的步骤:
构建安装应用程序包的镜像
容器化应用程序的最佳操作之一是优化镜像大小。
你可以将Dockerfile拆分为两个步骤(一个用于安装包,一个用于启动服务器)以压缩镜像。
因此,让我们从第一个镜像安装包开始。你可以在应用程序存储库上创建Doekerfile。
现在,按照以下步骤创建它:
选择基础镜像
首先,选择你将选择安装软件包的基础镜像。此镜像选择不是很重要,因为它不会包含在最终镜像中。你可以选择一个已经安装好且安装软件时必须使用的管理应用程序包(如npm、yarn、composer等)。
FROM 节点:17.6.0-slim
- 安装你需要的系统依赖;
- 复制安装应用程序包所需的文件;
- 执行命令安装包。
FROM node:1760-sPm
WORKDIR /、pp
COPY package.json ./package.json
COPY package.lock ./package.lock
RUN npm install
RUN npm run build
为你的应用创建最终镜像
这个选择对最终的镜像大小有很大的影响,最佳操作是使用尽可能小的镜像。这将取决于应用程序语言,如果是GO语言,你可以使用scratch镜像,因为它会生成一个非常小的独立的二进制文件。
如果你不能使用scratch镜像或其他非常小的镜像(如distroless),请使用的alpine镜像。
- 【必要时】安装你需要的系统依赖;
- 复制你需要的应用程序文件;
- 创建用户
注意:不是出于安全目的而使用root用户
现在,你将使用第一个镜像来检索软件包,且只能从这个镜像中检索。
FROM node:17.6.0-alpine3.15
# Install system packages
RUN apk add PACKAGE_1 \
PACKAGE_2 \
…
WORKDIR app/
# Create the app user
RUN adduser app
# Create the group and attach the app user to this group
RUN addgroup app app
# Copy all application files needed
COPY –chown=app:app src/ ./src
COPY –chown=app:app migrations ./migrations
COPY ….
# Retrieve the application packages from
COPY --chown=app:app /app/nodes_module ./build
COPY --chown=app:app /app/build ./build
FROM node:17.6.0-slim
WORKDIR /app
COPY package.json ./package.json
COPY package.lock ./package.lock
RUN npm install
RUN npm run build
FROM node:17.6.0-alpine3.15
# Install system packages
RUN apk add PACKAGE_1 \
PACKAGE_2 \
…
WORKDIR app/
# Create the app user
RUN useradd app
# Copy all application files needed
COPY –chown=app:app src/ ./src
COPY –chown=app:app migrations ./migrations
COPY ….
# Retrieve the application packages from
COPY --chown=app:app /app/nodes_module ./build
COPY --chown=app:app /app/build ./build
USER app
CMD [“npm”, “start”]
你现在有一个Dockerfile文件!现在,为了确保你拥有符合最佳操作的Dockerfile,让我们检查一些内容:
首先,比较重要的是不要进行与环境相关的设置。所有环境配置都将在Kubemetes manifests中配置,而不是在Dockerfile中。所以应
用程序配置和环境变量不能在这个文件中。
也许你想知道如果你不能定义你的应用程序配置,你该如何测试你的Dockerfile!为此,我们的方法是使用docker-compose,好处是这样做可以拥有一个接近Kubernetes环境的本地环境。
version: "3.9"
services:
app:
build:
context: .
dockerfile: ./Dockerfile
ports:
- "8080:8080"
env_file:
- configuration.env
- secrets.env
这个docker-compose在你计算机的8080端口上公开你的服务,然后使用自定义环境变量的文件构建和启动镜像。要执行docker- compose,请使用docker-compose命令。
其次,在没有docker-compose的情况下测试Dockerfile是很重要的,因为你需要确保在没有它的情况下也可以启动镜像。否则,你的镜像
将无法在Kubernetes集群中运行。
总而言之,测试使用和不使用*docker-compose*这两个选择可以让你对镜像的必要性更有信心。现在要将你的镜像放入镜像仓储中。
准备你的环境和迁移
在谈论Kubernetes manifests之前,你需要准备环境。所以很明显,你需要创建Kubernetes集群,至于是EKS还是GKE则取决于你选择的云供应商。然后,如有必要,部署一些其他依赖项:
- 一个数据库(如果您也必须迁移它)
- 管理持久数据的工具(特别是有状态服务)
你可以使用托管服务来执行此操作,例如AWS EFS的执行效果就很好,但是还有其他取决于你的云供应商的可能性选择,所以它不是唯一的选择,你还可以使用管理有状态组件的Kubernetes StafefulSet资源。
如果需要,你可以在此负载均衡器上配置。现在,是时候制定迁移策略了!
每个基础架构都可能具有会使迁移复杂化的特定配置,因此你可能需要进一步检查,包括以下有两个选择:
在第一种情况下,你的应用程序将停机,因为你不想在迁移期间丢失数据。
该方法由以下步骤组成:
- 在Kubernetes集群中部署你的应用程序;
- 创建维护页面;
- 将流量重定向到此维护页面(使用DNS开关或仅通过维护页面更改你的应用程序)
- 创建数据库转储;
- [如果你的应用是有状态的]检索有状态的卷;
- 恢复新数据库中的数据;
- [如果你的应用是有状态的]在新应用中恢复有状态的卷;
- 使用DNS记录将流量重定向到新应用程序。
而第二个选择下,你不会有停机时间,因为你使用的是同一个数据库,方法如下:
- 在集群中部署新应用程序;
- 更改DNS记录以将流量重定向到新应用程序。
创建Kubernetes manifests
要部署你的应用程序,你必须创建Kubernetes manifests。我主要使用Helm来部署它,但你也有其他选择,例如使用Kustomize或仅使用YAML文件。我不会比较哪个选择更好,因为这不是本文的目的。
你需要的第一个资源是管理容器的组件。如果服务是无状态的,你可以使用deployment;如果服有是有状态的,你可以使用StatefulSet。如 果你使用的是利用AWS EFS等外部服务(pods之间共享卷)的有状态应用程序,那么请使用deployment;否则,请使用Statefulset
。我将专注于deployment,因为与StrfulSet 资源相比,我在这方面拥有更多的专业知识。不用担心,除了定义此资源类型的卷管理,其他生态系统基本相同。
以下是deployment的标准架构示例:
显然,除了 deployment资源之外,StatefulSet的架构是相同的。
对于每个组件,我将对其进行解释,并向你讲述一些避免典型错误的最佳操作
- 关于deployment或 StatefulSet 资源
在这些资源中,你可以定义pos配置,包括:
探针对于你的deployment很重要,主要有3种类型:存活探针、就绪探针和启动探针。存活探针主要是定期检查容器是否处于活动状态, 如果容器崩溃,则重新启动它。第二种探针主要检查容器是否准备好接受流量,如果没有,则阻止到此Pod的流量。第三种探针是检查pod 是否已正确启动。因此,你可以使用这些探针,每使用一个探针就要在应用程序中定义一条路径。
你可以定义应用程序使用的内存和CPU。资源定义对于避免pods被驱逐和保证pods可用性有重要的作用。
它们是存储应用程序配置所必需的,例如环境变量。如果此配置必须存储在应用容器中的文件里,你可以像容器中的卷一样挂载configmap或secret
如有必要,你可以挂载secret和configmap,将它们合并到容器中。
该服务允许你访问集群内的应用程序容器,而无需使用pod的IP地址,用使用标签将流量重定向到正确的地方。你需要在服务参数spec.selector``中添加 pod标签。
入口管理来自集群外部的流量,它将外部流量重定向到Kubernetes服务。
- 关于Pod Disruption Budget (PDB)
PDB不允许你有超过X个pods不可用或确保至少有1个pod可用,它可以确保容器的可用性真的很棒..
- 关于Horizontal Pod Autoscaler (HPA)
HPA是Pods的自动调节器。总体而言,它根据内存和/或CPU使用率的平均值来扩展pods的数量,但也可以参考其他指标,例如用户流量
如果需要运行数据库迁移,可以使用Kubernetes作业来完成。总的来说,我通常使用Helm hooks注释在每次代码更新之前进行迁移。
你可以在Kubernetes中为您的应用程序创建cronjobs,频率的定义类似于Linux的定时 任务。
迁移
现在,你的服务在你的Kubernetes集群中运行!在迁移之前,最好考虑调整程序大小以适应您的生产流量,从而避免停机。为此,你可以采取以下两种方法:
- 像在虚拟机上一样调整应用程序的大小;
- 大小取决于负载测试。
显然,第二个方法更理想,因为你在新环境中测试应用程序,可以模拟比生产更大的流量。因此,你将确切地知道,你的应用程序可以容纳多少用户。
为此,你可以使用Gatling或K6。
而另一种方法,与负载测试相比,你对应用程序的了解会更少。该方法的最大优势是迁移的持续时间,因为负载测试在迁移之前需要一定的时间。
因此,如果你没有时间进行负载测试,它不会阻止迁移,但要小心使用另一种方法,以确保你的应用程序可以管理生产流量。
现在,你可以进行迁移了!最佳方法是在预生产环境中对其进行测试,以确保迁移操作没有问题。
在迁移期间,你必须检查一些事项:
- 应用程序日志和指标;
- 基础设施指标(CP U使用率、自动缩放等);
- 流量重定向到Kubernetes集群。
结论
应用程序迁移到Kubernetes并不容易,但也不是不可能!最重要的是一步一步去了解应用程序操作如何影响Kubernetes集群中的deployment
(如deployment和和StatefulSet之间的选择)。我希望这篇文章可以帮助你学会将程序迁移到Kubernetes这个很棒的工具中!
本文为翻译文章,原文链接:https://www.padok.fr/en/blog/application-migration-kubernetes