1. 作业5到底在考什么:先看懂题再动手
“云计算作业5”这个名字听起来很笼统,但如果你在修云计算课程,基本能猜到这一轮作业的核心套路——通常不会再让你去创建几台虚拟机和装个系统就交差,而是开始往“真实业务怎么在云上跑起来”这个方向走。我这次拿到的作业5,题目是:利用云平台资源,部署一个具备基本高可用能力的Web应用系统,并将应用容器化,要求体现负载均衡、弹性伸缩和对象存储的使用。
先说明一下场景设定:某高校的云计算课程作业系列,前四轮分别覆盖了云主机开通、网络配置、数据库搭建和监控告警,第五轮则是把这些能力串起来,做一个完整的应用交付。这个设定在不少学校都有类似版本,或者说,即使你的作业5题目不完全一样,核心考察点大概率也逃不开这几个方向:容器化交付、负载均衡调度、存储服务解耦、自动化伸缩。
拿到作业先别急着开干,我第一次做的时候上来就创建了一堆资源,结果费用跑了一周才发现架构设计有问题,重做了一遍。做这种综合型作业,第一件事是拆解题目到底在考什么能力,而不是看它写了什么功能。
我把这个作业的核心考点拆成了四块:
- 容器化能力:能不能把应用打成镜像,用容器的方式运行而不是直接在虚拟机上装环境。当前主流平台普遍采用容器和编排平台来承载业务,作业里体现这一点,说明你掌握了现代应用的交付方式。
- 负载均衡能力:能不能把用户请求分散到多台后端,避免单点故障,并在某一台挂了的时候自动摘除。这是高可用的基础。
- 弹性伸缩能力:能不能根据CPU、内存或者请求量动态增减后端实例数。这是云计算“按需分配”理念的直接体现。
- 对象存储集成:能不能把静态文件从应用服务器里拆出来,放到独立存储服务上,让应用无状态化。这一步做好了,前面几项能力的价值才真正发挥出来。
注意:如果你的作业还要求写实验报告,那么每一部分除了截图之外,务必保留操作命令和参数选择理由。评委老师最反感的就是只有截图没有解释的报告,哪怕功能实现了也可能被扣分。
2. 架构设计与服务选型:为什么这样搭
2.1 我最终选择的参考架构
作业的核心场景是一个带用户上传功能的轻量级Web应用。用户浏览器访问网站,可以浏览图片列表,也可以上传新图片。这个业务逻辑非常简单,但麻雀虽小五脏俱全——它天然涉及动态请求、静态资源存储、多实例部署三个关键模块。
整体架构我设计为三层:
第一层是负载均衡器,作为用户流量的统一入口。第二层是后端计算节点,跑容器化的Web应用,数量初始为2台,后续根据压力自动伸缩到4台甚至更多。第三层是对象存储和数据库,对象存储用来存放用户上传的图片文件,数据库存放图片的元数据信息和用户记录。
这个架构在云计算课程里很经典,但关键是你要知道每一层为什么必须这样设计。以对象存储为例,如果用户的图片直接存在Web应用所在服务器的本地磁盘上,那么当弹性伸缩把实例数量从2台缩到1台时,那台被销毁的机器上的用户图片就永久丢失了。这显然不是我们希望看到的。把图片放到独立的对象存储服务中,后端实例就变成了“无状态”——任何一台机器都能处理任何用户的请求,因为数据不落在本地。
2.2 服务选型的对比逻辑
课程环境里通常不会只允许用一种方案,老师一般会提供两到三条技术路线。以云主机服务为例,常见的选择有:使用传统虚拟机和容器实例。我评估了一下两者的差异:
- 传统虚拟机配置起来直接,和本机环境几乎一样,用命令手工装环境也很快,适合没有任何容器基础的学生。但缺点是交付不灵活,作业演示时如果评委想看自动化部署的过程,虚拟机方案很难体现。
- 容器实例方案需要写镜像文件,初次接触容器指令的话需要大概半天时间熟悉语法,但一旦镜像构建好,部署、扩容都很快,而且作业报告里可以大大方方写“采用容器化交付”,再加上一句“支持秒级启动”,这属于明显的加分项。
我最后选择了“容器引擎部署应用”,理由是这套流程更贴近当前业界主流做法。云原生的应用交付现在几乎都以镜像为单位,作业阶段熟悉这套流程,后续无论是做课程项目还是实习上手,收益都很大。另外多说一句:不少云平台对新用户都有短期免费额度,容器实例这类按秒计费的服务,跑完演示记得释放资源,能省不少钱。
提醒:选型的时候要看一下课程用的平台是否支持容器服务的私有网络访问。个别平台的对象存储和负载均衡器默认分布在不同的网络区域,如果你的后端实例无法访问存储服务,会在联调阶段浪费大量时间。
2.3 对象存储的接入方式
对象存储接入本地应用,主流方式是两种:一种是通过平台提供的软件开发工具包(SDK),在应用代码里调用写入/读取接口;另一种是通过挂载工具把对象存储挂载成服务器的本地目录,应用透明读写。
课程项目中,图片上传数量不大,我建议使用SDK方式,因为这种方式最能体现对云服务的理解。挂载方式适合大数据量或大量遗留应用,但作业里难以展示“代码调用云服务”的过程。
如果你选的Web框架是Python系,SDK的使用非常直接:初始化一个客户端,填入访问密钥和存储桶名称,然后调用上传接口即可。需要提前在平台控制台创建一个存储桶,并设置访问权限为“私有读写”,只允许后端实例通过密钥访问,避免用户直接上传或篡改文件。
3. 核心细节解析:镜像构建、负载均衡与弹性策略
3.1 应用容器化的关键操作
这个环节是整个作业工作量最大的部分,也是新手最容易被卡住的地方。
先说一下应用本身。我用的是Python的Flask框架写的一个极简Web应用,代码结构大致如下:
app/ ├── app.py # 主程序 ├── requirements.txt # 依赖列表 ├── templates/ │ └── index.html # 前端页面 └── Dockerfile # 镜像构建文件app.py 的核心逻辑就三个接口:首页展示图片列表、上传接口接收文件、静态资源访问(通过对象存储签名URL)。为了不让代码过于复杂,数据库部分我直接用了轻量的记录方式,作业里能演示即可。
Dockerfile 是镜像构建的关键,我使用的版本是这样的:
FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple COPY . . EXPOSE 8080 CMD ["python", "app.py"]这里有两个细节要展开:第一个是基础镜像的选择,我刻意用了python:3.11-slim而不是python:3.11。slim 版本体积更小,构建后镜像只有后者的三分之一左右。体积小的意义在于:后续创建容器时启动更快,镜像仓库的存储成本也降低,更重要的是在一些网络带宽受限的实验环境里,拉取镜像的时间成本差异会非常明显。
第二个是端口的选择,我用了8080而不是常见的80。原因很实际:在某些云平台的安全组配置中,低于1024的端口需要有额外的权限说明,而8080作为常用应用端口,配置起来省事,而且不易和平台自带的服务冲突。
写完后在项目目录执行构建命令:
docker build -t cloud-assignment5:v1 .执行完毕后用docker images确认镜像存在。如果这一步构建过程有报错,九成情况是依赖包下载超时,把国内镜像源写进pip命令即可解决。
镜像构建好之后,推到平台的镜像仓库。如果你使用的是平台自带的容器服务,一般都有内网地址镜像仓库,推送命令和公共仓库基本一致,不过登录凭证需要提前配置好:
docker tag cloud-assignment5:v1 registry.example.com/cloud/ca5:v1 docker push registry.example.com/cloud/ca5:v1实操心得:如果你之前没接触过容器,这里最容易出错的其实是“上下文目录”的概念。执行
docker build时,命令末尾的点号.代表当前目录,Docker会将当前目录下的所有文件作为构建上下文发送给引擎。如果项目目录里有一个很大的文件夹(比如虚拟环境或数据集),构建会非常慢,甚至卡住。建议在项目根目录创建.dockerignore文件,把不需要的文件排除在外。
3.2 负载均衡器的配置和会话保持问题
负载均衡器在这个架构里的作用,你可以把它理解为“前台的接待员”。来访用户不需要知道具体哪一位工作人员处理他的业务,只需要把请求递给前台,前台再根据后台人员的忙碌程度分配任务。
平台上创建负载均衡器的流程大同小异,需要填写的主要参数包括:监听协议、监听端口、后端协议、后端端口、健康检查路径等。我的配置如下:
| 配置项 | 取值 | 说明 |
|---|---|---|
| 监听协议 | HTTP | 本作业不涉及HTTPS证书,用HTTP即可 |
| 监听端口 | 80 | 用户访问入口 |
| 后端协议 | HTTP | 容器内部跑的是HTTP服务 |
| 后端端口 | 8080 | 对应应用的监听端口 |
| 健康检查路径 | /health | 应用提供的探活接口 |
| 调度算法 | 加权轮询 | 默认算法,适合多实例场景 |
健康检查路径值得单独说明。应用代码里必须提供一个/health接口,返回HTTP 200状态码,负载均衡器会每隔几秒请求一次这个路径,若连续多次无响应,就把对应的后端实例标记为“不健康”,不再分配流量到它身上。这是一个极其重要的高可用细节——如果应用有异常,但是健康检查配置不正确,流量依然会被发往故障实例,用户就会看到超时或报错页面。
我的 app.py 中对应的代码片段如下:
@app.route('/health') def health(): return 'ok', 200配置完负载均衡之后,不要急着和后端实例绑定,先把后端服务的访问地址在浏览器里单独测试一下,确认端口和路径都正确,再绑定到负载均衡器的后端组里。这样定位问题会非常快,一步到位反而容易在多节点环境中搞不清楚是负载均衡的问题还是后端的问题。
另外一个小提示:负载均衡器的后端实例需要和负载均衡器在同一个私有网络里,如果你创建实例时选了不同的网络区域,后端组里会看到“不可用”或者无法选择该实例。这个细节在实验手册里经常被一笔带过,但实际卡住过不少人。
3.3 弹性伸缩策略的参数设定思路
弹性伸缩是整个作业里最能体现“云计算”价值的部分。我配置的规则是:当后端实例组的CPU平均使用率超过70%,且持续3分钟,自动增加1个实例;当CPU平均使用率低于20%,且持续5分钟,自动减少1个实例。实例数量范围设定为2到5台。
这三个参数其实都是有讲究的:
- 阈值70%不是随便定的。CPU使用率在70%左右说明系统还有一定缓冲,但已经开始有负载压力,此时扩容可以避免请求堆积。如果把阈值设成90%,虽然能节省资源,但高负载时扩容动作还没来得及生效,服务可能已经超时了。
- 持续时间的设定是为了防止“抖动”。线上业务的CPU使用率经常会有瞬时峰值,如果一超过70%就扩容,可能十分钟后又低于阈值触发缩容,实例频繁创建销毁,反而增加成本和系统不稳定。持续3分钟的观测窗口可以过滤掉这种瞬时波动。
- 实例数量的上下限是成本和安全之间的平衡。下限2保证基础可用性,上限5避免失控扩展产生的费用。
创建弹性伸缩组时,还需要提供“启动配置”或“实例模板”,本质上就是告诉平台“新实例创建后应该是什么样”。这里需要选择镜像、规格、磁盘大小等参数。因为你已经做了容器化,启动配置里只需要选定一个基础Linux镜像即可,然后编写“启动脚本”,脚本内容是把最新的容器镜像拉取下来并运行,实现节点启动即服务就绪的自动化效果。
启动脚本里核心命令大致是:
#!/bin/bash docker pull registry.example.com/cloud/ca5:latest docker stop ca5 || true docker rm ca5 || true docker run -d --name ca5 -p 8080:8080 \ -e OSS_BUCKET=your-bucket \ -e OSS_ENDPOINT=your-endpoint \ -e DB_CONNECT_STR=your-db \ registry.example.com/cloud/ca5:latest脚本中|| true的写法是容错处理,意思是如果容器不存在导致停止或删除时报错,不中断脚本执行。环境变量中存储连接方式和密钥等信息,不在镜像中暴露,这是安全考量的基本素养。
4. 实操过程记录:从零到可访问的完整流程
4.1 开通基础资源
实操阶段在整个过程里是最耗费时间的,我按步骤记录下来供你参考。
第一步是在平台上创建一个私有网络。这个网络的作用是让负载均衡器、后端实例和数据库之间可以通过内网地址通信,既安全又不产生公网流量费用。创建时需要规划一个网段,比如 10.0.0.0/16,并建立一个安全组,安全组规则先放通内网互通,再把负载均衡器所在的网段放通到8080端口的入方向。
第二步是创建数据库实例。我选择了一个轻量的MySQL类型实例,规格选择最低配即可,毕竟作业数据量很小。创建数据库实例的时候要注意几个配置:字符集选utf8mb4,因为图片描述信息可能包含中文和特殊符号;自动备份策略打开,备份时间选在凌晨低峰时段;对外访问地址选择“仅内网”,避免数据库暴露到公网。
第三步是创建对象存储的一个存储桶。命名要全局唯一,一般取课程缩写加学号后几位即可。存储桶创建完成后,进入访问控制页面,生成一对访问密钥,并记录访问端点。生成的密钥只在创建时显示一次,务必保管好,否则只能重新生成。
注意:把访问密钥放在代码仓库里过于危险,即使作业不算正式生产系统,养成密钥不入库的好习惯也是必要的。稳妥做法是把密钥放在容器的环境变量中,或者使用平台的密钥管理服务。
4.2 打包应用镜像并推送
把上一节提到的应用代码准备好之后,在本地执行命令构建镜像并且推送到平台的镜像仓库。操作本身不算复杂,但涉及平台账号的命令行登录,需要提前安装并配置好对应工具。
如果构建镜像时遇到网络问题,可以把pip install和apt-get的软件源都切换到国内镜像。作业时间本身有限,不要在依赖下载上耗太多时间。
镜像推送完成后,可以在平台的镜像仓库页面看到它的版本信息。这里有一个提升演示效果的小技巧:把同一个镜像同时打上v1和latest两个标签。这样当更新应用时,只需要推送新的latest标签,伸缩组创建的新实例就会自动拉取到新版本,不需要修改启动配置。演示动态扩容的时候,你可以对评委解释这个设计是“把配置和代码解耦”,这属于加分表达。
4.3 创建容器实例并加入负载均衡后端
通过容器服务能力创建第一个容器实例。创建时选择的路子是:先手动创建两个实例(对应架构里的初始2台),验证服务和负载均衡正常后,再把这两个实例纳入弹性伸缩组的管理范围。不同的平台操作路径不完全一致,但整体思路是手动跑通流程后再交给自动策略接管。
创建容器实例时指定镜像为刚才推送的版本,端口映射设置为容器端口8080映射到主机端口8080。环境变量需要填写数据库连接串、对象存储桶名称、访问端点等信息,这些变量在应用代码里通过os.environ获取。
两台实例创建完成后,在应用所在服务器上执行命令测试模块。
然后到负载均衡器的后端组页面,把两台实例添加进去。添加时设置权重,权重相同表示轮询分发。保存后等待大约1到2分钟让配置生效。在浏览器访问负载均衡器的公网IP,反复刷新页面查看访问日志,两个实例的访问量应该接近一比一。
到这里,整个系统已经具备基本可用能力了:用户通过同一个入口访问,请求分摊到两台后端实例,图片存在对象存储而不是本地磁盘。
4.4 验证弹性伸缩的效果
弹性伸缩功能是否生效,需要实际制造负载来验证。我使用的压测方式是启动几个并发请求脚本去访问应用的首页接口,每次请求会查询数据库列表数据,同时生成缩略图时消耗一定的CPU资源。
在弹性伸缩组页面观察监控曲线。压测开始后两三分钟内,CPU使用率从20%左右持续攀升到超过70%,再过一个触发周期,伸缩组自动创建了一台新实例,可以看到实例总数从2变为3,新实例经过启动脚本完成镜像拉取和启动后,自动注册到负载均衡后端组,开始承接流量。
这里有一个环节值得特别说明:新实例的启动时间。容器镜像约200MB,在实验内网环境下,拉取镜像和启动容器一般在2分钟以内完成。但如果你的网络环境或者镜像仓库访问速度较慢,扩容触发到实例真正可服务之间会有明显延迟,可能影响压测效果。我的建议是:在正式演示之前,先人工删除一台实例,让弹性伸缩组自动补齐一次,测试整个自动化链路是否通畅。这个过程能把所有隐藏的问题提前暴露出来。
4.5 数据可用性的验证
高可用系统除了要保证应用进程不挂,还要保证底层的数据不丢。我做了两个验证:第一个是上传一张测试图片到对象存储,然后在对象存储控制台确认文件存在;第二个是在数据库里插入一条记录,然后在另一台后端实例上查询该记录,确认数据共享。
虽然这个验证看起来简单,但它恰恰证明了架构拆分的价值——后端实例可以被随意删掉,只要存储层和数据库层还在,应用数据就不会丢。
5. 实操中踩过的坑:问题排查与避坑指南
5.1 健康检查不通过的典型原因
第一次配置完负载均衡,等了五分钟,实例状态还是“不健康”。我查了半天,最后定位到原因:健康检查路径配置的是/health,但安全组没有放通负载均衡器所在网段到8080端口的入方向规则。负载均衡器的探针请求被安全组挡住了,自然永远得到超时响应。
排查这类问题的顺序是:先在浏览器或命令行直接访问后端实例的IP加端口,确认服务本身正常;然后去看安全组规则,确认放通了来源网段;最后确认负载均衡制器配置的后端端口和实际监听端口一致。按照这个顺序排查基本能解决90%的健康检查问题。
排障时可以使用的命令:
curl -v http://后端实例IP:8080/health如果返回200并且内容为ok,说明后端服务没问题,接着查网络策略和负载均衡本身的配置。
5.2 图片上传到对象存储报错
上传接口能调用通但报访问被拒绝,最常见的原因是访问密钥没有配置正确的权限策略。我在第一次测试时,密钥配置只有读取权限,没有写入权限,调用上传接口时直接返回403。页面上的提示信息如果比较模糊,需要去服务端的日志看完整的错误码,才能定位到权限原因。
解决方法是在平台的对象存储权限管理中对存储桶配置对应的读写权限。更严谨的做法是创建一个专用子账号,仅授予该存储桶的读写权限,避免使用主账号密钥操作,因为主账号权限过大,一旦泄露出问题的面更广。
5.3 弹性伸缩冷却时间导致的不稳定
在压测过程中我发现一个现象:缩容之后再次压测,扩容响应不如第一次快。原因是伸缩组有冷却时间设置——扩容完成后的一段时间内不响应新的伸缩任务,防止频繁操作。这个设计是合理的,但在实验场景里,调整完毕后需要等待冷却时间结束才能进行下一轮验证。
经验是:如果演示时需要反复测试伸缩行为,把冷却时间调短一些,比如默认300秒调到60秒。但这只是实验环境的临时优化,真实生产环境中冷却时间不宜过短。
5.4 镜像构建过程中的依赖下载问题
依赖下载超时是我遇到的最频繁的问题。Python应用需要安装Flask、对象存储SDK等,pip默认源在实验网络环境里速度很慢,经常超时。切换到国内镜像源之后,问题立即解决。
如果你使用其它镜像源仍然出现个别包下载失败,可以尝试单独指定包的镜像:
pip install 包名 -i https://pypi.tuna.tsinghua.edu.cn/simple另外,如果本地构建镜像时因磁盘不足报错,可以执行清理命令:
docker system prune -af该命令会删除所有停止的容器和未使用的镜像,运行前确认不影响需要的资源。
5.5 成本控制经验
课程作业的资金额度一般有限,如果忘记释放资源,几台实例加上负载均衡器跑一个星期,费用可能超支。为了控制成本,我在每次实验结束后都执行了清理动作:释放弹性伸缩组、删除不再使用的容器实例、释放负载均衡器。同时设置平台账户的资源释放提醒,保证实验结束后能及时回收资源。
如果作业是分阶段提交的,中间隔几天再继续实验,也建议每次实验前重新创建资源,实验后释放,顺带复习一遍创建流程。反复练习几次,整个流程就烂熟于心了,到时候答辩也好、考试也好,都会轻松很多。
6. 实验报告的撰写思路与演示要点
大部分云计算作业除了要跑通功能,还需要交一份实验报告并进行演示。报告的质量直接决定作业的最终评级,我总结一下写作思路。
报告的核心不是罗列操作步骤,而是回答“为什么”。每一章给出解决方案时,都应该有对应的环境截图、命令记录和结果验证截图,但更关键的是架构图和应用逻辑说明。
我报告的章节结构大致这样安排:
- 第一章,实验目标与总体架构设计。画出架构图,标注各组件的职责和关系。
- 第二章,关键配置说明。包含容器镜像的构建过程、负载均衡器的参数配置、弹性伸缩规则设定。每个参数都写明选择理由。
- 第三章,实施过程与验证结果。按顺序展示从创建资源到压测验证的完整过程,每步附数据和截图。
- 第四章,问题分析与心得。把你在实操中遇到的问题和解决方案按上面梳理的整理方式写清楚,这部分的含金量很高,老师一般会比较看重。
演示环节有几个值得注意的细节:进入系统后截至目前,我已经习惯了先把架构图投到大屏幕上,对着图讲一遍整体流程,然后按照“用户请求进来,负载均衡分发,后端处理,对象存储读写”的顺序做一次完整的功能演示。不要上来就演示页面功能,先讲架构能让评委快速理解你的系统是怎么组织起来的。
演示弹性伸缩时,先展示当前实例数为2,然后启动压测脚本,运行期间让评委看到监控图表上CPU曲线的上升、实例数量的自动增加,以及压测结束后实例数量的回落。整个过程可视化程度高,在作业演示中属于加分项。
报告和演示都做完之后,我建议保留一个“资源清理列表”,记录哪些资源保留、哪些资源释放。这既是对实验经费负责,也能体现你对云资源成本控制的意识。这一点在老师眼里往往很加分,因为在真实工作中,“会创建资源”不如“会管理资源”重要。
7. 最后一轮自测与交付检查
在提交作业之前,花一点时间完成最后的自测,可以避免很多尴尬情况。
首先要检查的事项是:从负载均衡器入口访问应用是否稳定正常。这是用户看到的唯一入口,如果你只测了后端实例的IP,忽略了负载均衡器,而负载均衡器后端组配置有误,交付演示时问题就会暴露在所有人面前。
其次测试弹性伸缩的完整性。建议从手动删除一台实例开始,看看伸缩组能否在一个周期内自动补齐,并且新实例是否自动注册到负载均衡后端。如果这个过程跑不通,说明自动化链路还不完整,需要排查启动配置、镜像仓库凭证、安全组策略等环节。
然后检查对象存储的文件是否可以通过应用正常展示。上传一张图片,再通过首页列表查看缩略图,确保读取路径无误。
最后检查各类密钥和配置信息是否有明文出现在代码中。我见过有同学把对象存储的访问密钥直接写在了代码里,评委打开源代码页面时直接扣分,这是非常可惜的疏漏。
我在最终提交前还做了一件事:把所有操作过程中执行的命令整理成一个脚本文件,放在报告的附录里。这样如果评委想看细节,只需要翻阅附录即可,不需要翻到前面的章节里一条一条找。这个方法看似笨拙,但对报告的整体评价帮助很大。
个人体会:完成这一类综合作业,真正花时间的地方往往不是应用代码本身,而是各个云服务之间如何配合。不要觉得自己把平台页面里的按钮都点了一遍就等于学会了,能够把负载均衡、弹性伸缩、对象存储这些模块在脑子里面组装成一个完整的系统,并清楚每一个配置项的意义,才是这门课真正想培养的能力。
作业5做完之后,我整理了一份自己的避坑清单:网络资源创建顺序先私有网络再实例,密钥管理严格走环境变量或者密钥管理服务,压测前确保冷却时间已过,演示前半小时完整跑一遍核心链路。这些习惯在这学期后面的作业和小组项目里都帮了大忙。希望这篇文章能帮你少走一些弯路,把时间和精力花在真正的理解上,而不是在和平台页面较劲上。