从 DevOps 角度来看容器与无服务器
免责声明:Tracetest赞助了这篇博文。使用可观察性可以将测试创建和故障排除的时间和精力减少 80%。
两个流行词走进酒吧……不?是的,我们别去那里。如果你有这种感觉,那你就来对地方了。容器和无服务器都是过去几年里炙手可热的新兴技术,而且它们的热度丝毫没有减弱的迹象。
在继续之前,我先澄清一下。不,你不会因为无服务器而丢掉 DevOps 的工作。我说完了。我们现在可以继续了吗?谢谢。
TL;DR
- 为什么这很重要?
- 什么是容器?
- 容器的优缺点
- 容器用例
- 将容器化的 Node.js 应用程序部署到 AWS 上的 Kubernetes 集群
- 什么是无服务器?
- 无服务器的优缺点
- 无服务器用例
- 将无服务器 Node.js 应用程序部署到 AWS
注意:如果您想立即查看最终结果,代码示例已经在 GitHub 上,这里和这里。
为什么这很重要?
我想告诉你,自己管理容器和让无服务器技术帮你管理容器的利弊。这种“部落战争”必须停止。让我们就几个事实达成共识。这两种技术都有很棒的用例,也都有各自的痛点。我只是想告诉你,什么时候该用什么。
针对这个问题,有几个因素需要考虑。对于初创公司来说,最主要的、最突出的确实是开发速度和上市时间。但是,深入研究后,你会发现还有几个重要的因素需要考虑,比如复杂的部署场景和部署应用程序所需的时间。供应商锁定也是你需要考虑的另一个关键点,尽管我认为这不是什么大问题。但成本才是关键。如果你负责在月底支付基础设施费用,你就会关心你的支出。
准备好学习新知识了吗?让我们开始吧。
什么是容器?
简而言之,容器是隔离的、无状态的环境。容器是一个轻量级的、独立的、可执行的软件包,包含运行该软件所需的一切,包括代码、运行时、系统工具、系统库、设置等。
通过将应用程序及其依赖项容器化,操作系统分布和底层基础设施的差异就被抽象出来了。
我喜欢说它就像一个微型虚拟机,但其实并非如此。大多数开发者都理解虚拟机的概念。我们习惯在虚拟机中运行应用程序。它们模拟真实的机器,拥有真实机器的所有功能。其实,在容器中运行应用程序也是一样的,除了一些重要的架构差异。主要是容器运行在相同的操作系统内核上。让我来演示一下……
这里您可以看到一个清晰的概览。虚拟机使用一种称为虚拟机管理程序的东西。它管理主机上的每个虚拟机。正如您所见,每个虚拟机都有自己的操作系统。而容器共享主机操作系统。这使得容器的体积显著减小,创建和删除速度也更快。
容器的优缺点
在比较容器和无服务器时,根据你的开发者背景和个人情况,其优缺点可能有所不同。不过,我相信可以达成一个双方都能接受的数值。
使用容器意味着默认情况下您将无法使用任何自动扩展功能。您需要自行设置。幸运的是,像 AWS Auto Scaling 这样的供应商专用工具可以简化这一过程。这样做的好处是,您可以完全控制您的资源,并掌控扩展,这意味着理论上您可以拥有无限的可扩展性。当然,只要您的提供商允许,您就能拥有无限的可扩展性。
你拥有的所有控制权和权力确实暴露了一个重大缺陷——它带来的复杂性。你需要了解生态系统和各种可用的工具。对很多人来说,这是一个陡峭的学习曲线,因为最终部署和管理应用程序的是你。为了拥有更多的自由和控制权,你必须接受一个事实:应用程序会因各种活动部件而变得复杂。遗憾的是,这会带来更高的成本。毕竟,无论是否有流量,你都在为资源付费。
但并非一切都那么糟糕。它带来的一大好处是,你可以使用众多监控和调试工具。生态系统如此完善,你无需费心设置必要的工具。最后,有了容器,无论你的团队使用哪种操作系统,都能拥有相同的开发环境。这使得大型团队能够轻松高效地工作。
容器用例
容器化应用的用例比无服务器应用更加广泛。主要是因为您可以轻松将现有的单体应用重构为基于容器的架构。但是,为了获得最大收益,您应该将单体应用拆分为独立的微服务。这些微服务将作为独立的容器部署,并配置它们彼此通信。
容器的常见应用包括 Web API、机器学习计算和长时间运行的进程。简而言之,任何你之前使用传统服务器的应用,都非常适合容器化。当你已经为服务器付费,无论负载如何,都要确保它们真正发挥作用。“全力以赴”这个词用得恰如其分。
将容器化的 Node.js 应用程序部署到 AWS 上的 Kubernetes 集群
我们需要关注几个步骤。首先,创建容器镜像并将其推送到仓库。之后,我们需要创建一个 Kubernetes 集群并为容器编写配置文件。最后一步是将所有内容部署到集群并确保其正常运行。
准备好了吗?深呼吸一两口气,这可不是件容易的事。
注意:请确保您的机器上安装了Docker,以便能够运行以下命令。
1. 创建容器镜像
这是一个简单的 Node.js/Express 应用程序。
// app.js
const express = require('express')
const app = express()
app.get('/', async (req, res, next) => {
res.status(200).send('Hello World!')
})
app.listen(3000, () => console.log('Server is running on port 3000'))
很熟悉吧?用这个创建镜像相当简单。首先,我们需要一个Dockerfile。
# Dockerfile
FROM node:alpine
# Create app directory
WORKDIR /usr/src/app
# COPY package.json .
# For npm@5 or later, copy package-lock.json as well
COPY package.json package-lock.json ./
# Install app dependencies
RUN npm install
# Bundle app source
COPY . .
EXPOSE 3000
# Start Node server
CMD [ "npm", "start" ]
这将配置我们的图像是什么样子,要安装的依赖项,它将公开什么端口以及在创建容器后要运行什么命令。
是时候构建图像了。
$ docker build . -t <docker_hub_username>/<image_name>
如果您之前没有构建过镜像,此命令将需要一些时间。完成后,您可以将其推送到容器存储库。我将向您展示 Docker Hub,但您可以使用任何您想要的。
$ docker push <docker_hub_username>/<image_name>
注意:运行此命令之前,请确保已验证您的身份。运行该$ docker login
命令。
推送镜像后,Docker Hub 配置文件会列出该镜像。它看起来类似这样。
第一步完成后,您已将镜像拉取到所选的 Kubernetes 集群。接下来,该创建集群了。
2.创建 Kubernetes 集群
在 AWS 上快速启动和运行 Kubernetes 的最简单方法是使用名为KOPS的工具。它是一个用于创建和管理基础设施资源的 CLI。
安装 KOPS 后,您将可以使用 CLI 命令与 Kubernetes 集群交互。以下是一组可快速启动并运行集群的命令。
$ export ORGANIZATION_NAME=your-org-name
# create state store
$ export BUCKET_NAME=${ORGANIZATION_NAME}-state-store
$ aws s3api create-bucket \
--bucket ${BUCKET_NAME} \
--region eu-central-1 \
--create-bucket-configuration LocationConstraint=eu-central-1
$ aws s3api put-bucket-versioning \
--bucket ${BUCKET_NAME} \
--versioning-configuration Status=Enabled
# create cluster
$ export KOPS_CLUSTER_NAME=${ORGANIZATION_NAME}.k8s.local
$ export KOPS_STATE_STORE=s3://${BUCKET_NAME}
# define cluster configuration
$ kops create cluster \
--master-count=1 --master-size=t2.micro \
--node-count=1 --node-size=t2.micro \
--zones=eu-central-1a \
--name=${KOPS_CLUSTER_NAME}
# if you want to edit config
$ kops edit cluster --name ${KOPS_CLUSTER_NAME}
# apply and create cluster
$ kops update cluster --name ${KOPS_CLUSTER_NAME} --yes
# validate cluster is running
$ kops validate cluster
一旦集群运行,您就可以创建用于部署容器映像的配置文件。
3.部署容器镜像
现在我们开始讲解 Kubernetes 的具体内容。使用kubectl命令,你将创建 Kubernetes 资源。为了快速上手,你需要一个部署 (Deployment) 和一个服务 (Service)。为了方便起见,我们创建两个 YAML 文件。一个用于部署,一个用于服务。
# node-deployment.yml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: node
spec:
selector:
matchLabels:
app: node
tier: backend
replicas: 9
template:
metadata:
labels:
app: node
tier: backend
spec:
containers:
- name: node
image: <docker_hub_username>/<image_name>
ports:
- containerPort: 3000
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
部署将创建 pod、副本集并确保它们正常工作,同时服务将部署暴露给外部流量。
# node-service.yml
apiVersion: v1
kind: Service
metadata:
name: node
labels:
app: node
tier: backend
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 3000
selector:
app: node
tier: backend
现在您可以运行kubectl命令。
$ kubectl apply -f node-deployment.yml
$ kubectl apply -f node-service.yml
这将创建 Pod、副本集、部署和服务。太棒了!现在你可以看到应用程序正在运行。理想情况下,一旦你将代码推送到代码仓库,整个过程就会在 CI/CD 流水线中自动完成。但是,即使对于之前做过这件事的人来说,这个过程也相当漫长。
让我们看看无服务器的比较情况。
注意:您可以在此 GitHub 仓库中查看此配置。如果您希望更多人看到它,请点个星标。
什么是无服务器?
从定义上讲,无服务器通常被认为是函数即服务 (FaaS)。但这并非完全正确。无服务器远不止于此。它应该被视为一个基于事件的代码运行系统。这意味着,您可以使用各种服务来创建业务逻辑,而无需关心任何服务器。您正在完全抽象基础设施。完美的例子包括将静态网站托管在 S3 上,使用 DynamoDB 或 Aurora Serverless 等无服务器数据库,当然还有使用 Lambda 无需管理服务器即可运行代码。
如果您遇到突发流量高峰,需要立即检测并处理,那么无服务器架构是理想之选。即使没有任何流量,应用程序也会完全关闭。您只需按实际使用的资源付费。无使用量,无需支付任何费用。
无服务器的优缺点
提到无服务器,首先想到的就是无需管理任何基础设施。无需安装操作系统更新,无需安全补丁,无需担心,因为提供商会帮您搞定一切。这比管理自己的基础设施和集群要简单得多。然而,这种魔力是有代价的。使用 Kubernetes 轻松为应用添加可观察性,但无服务器却无法做到这一点。您需要依赖 OpenTelemetry 等工具进行仪表盘和可观察性供应商,但更重要的是,需要使用Tracetest之类的工具进行故障排除和端到端测试。
对于我的许多开发者同行来说,自动伸缩功能带来的便利是无比的。它默认启用,无需任何配置,它就能正常工作。由于应用程序在没有流量时会完全关闭,因此成本非常低廉。但并非所有功能都完美无缺。您必须忍受明确的处理能力和内存限制,这迫使您编写更高效的代码,因为如果函数规模过大,可能会过载。这还可能导致可怕的噩梦——延迟。😞
关于延迟,FaaS 解决方案存在所谓的冷启动问题。函数的首次调用大约需要一两秒钟才能让容器启动。如果这是一个问题,你应该重新考虑是否使用 FaaS。
然而,部署的简易性才是无服务器架构的精髓所在。您只需将代码部署到提供商处即可运行。无需 Dockerfile 或 Kubernetes 配置。您的产品上市速度将非常快,而这正是初创公司最看重的。
无服务器用例
我相信你已经可以通过阅读这些优缺点得出关于用例的结论。无服务器非常适合微服务架构。这些微服务可以是简单的 Web API 或任务运行器。无服务器函数的短暂性使其成为处理数据流或图像的理想选择。
您还可以将它们用作 Cron 作业,安排函数每天在特定时间运行。无需让服务器始终运行,只需偶尔运行后台任务即可。请记住,FaaS 仅适用于短时间运行的进程。AWS Lambda 函数的最长运行时间为 15 分钟。如果您有一些繁重的计算任务,我建议您改用基于容器的设置。
将无服务器 Node.js 应用程序部署到 AWS
你会惊讶地发现,将 Node.js 应用部署到无服务器环境的步骤明显减少了许多吗?我当然希望你不会。
借助无服务器框架,您可以大大简化无服务器应用程序的开发流程。您可以在名为serverless.yml的文件中配置所有资源。它本质上会被转换为 CloudFormation 模板,部署到 AWS 并创建您指定的所有资源。代码本身会被打包成 .zip 文件并上传到 S3。之后,它将被部署到 Lambda。
无服务器框架的神奇之处在于,它实现了一步到位的自动化资源创建和代码部署。让我来演示一下。
注意:我假设您已经安装并配置了所需的框架模块和 IAM 角色。如果没有,请查看此处以开始使用。
# Framework
$ npm i -g serverless
# Express.js router proxy module
$ npm i serverless-http
1. 配置无服务器资源
以下是相同的 Node.js/Express 的外观,经过微小修改后可与 AWS Lambda 配合使用。
// app.js
const express = require('express')
const sls = require('serverless-http')
const app = express()
app.get('/', async (req, res, next) => {
res.status(200).send('Hello World!')
})
module.exports.server = sls(app)
唯一的区别是,你将其传递给了serverless-http模块。接下来,我想让你深入了解我们所需的实际资源,让我们查看一个示例serverless.yml文件。
# serverless.yml
service: express-sls-app
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: eu-central-1
functions:
app:
handler: app.server
events:
- http:
path: /
method: ANY
- http:
path: /{proxy+}
method: ANY
我们将部署一个app
函数,其函数处理程序指向app.jsserver
文件中的方法。此函数的事件触发器将是向任意路径发出的 HTTP 请求。实际的路由将在 Express 应用内部处理,因此我们只需添加设置即可。{proxy+}
2. 部署无服务器资源
猜猜看,将所有内容部署到 AWS 只需要一个命令。
$ serverless deploy
创建一个可行的 CI/CD 管道来运行单个命令比容器命令的荒野丛林要简单得多。
注意:您可以在此 GitHub 仓库中查看此配置。如果您希望更多人看到它,请点个星标。
总结
这里的关键点是什么?何时选择什么?当你需要灵活性和对系统的完全控制,或者需要迁移遗留服务时,我建议你选择容器和容器编排器,比如Kubernetes 。
当您需要更快的开发速度、自动扩展以及显著降低的运行时成本时,无服务器是更好的选择。无服务器还可以作为支持服务与遗留系统绑定,这些支持服务是在主代码库之外开发的,用于处理特定问题或业务逻辑。无服务器框架在这方面为您提供了极大的帮助。
有了容器,监控和适当的警报已经通过Prometheus等工具足够成熟,而无服务器则落后了。
无服务器社区里有一些很棒的文章,你可以看看。它们都解释了这两种技术的优势,并解释了为什么容器和无服务器之间的那些小争吵毫无意义。我建议你去看看!
如果您想阅读我之前的一些无服务器思考,请转到 我的个人资料 或 加入我的无服务器时事通讯!
我写这篇文章的时候非常开心,希望你们读起来和我写的时候一样开心。如果你们喜欢,就拍拍那只小独角兽,这样 dev.to 上会有更多人看到这篇文章。下次再见,保持好奇心,玩得开心。
注:我十月份会在 WebCampZagreb上就这个话题做个演讲。如果你在附近,欢迎来参加。😊
免责声明:Tracetest赞助了这篇博文。使用可观察性可以将测试创建和故障排除的时间和精力减少 80%。
文章来源:https://dev.to/adnanrahic/containers-vs-serverless-from-a-devops-standpoint-e4n