编辑按:Zira认为云厂商推荐的央企用云案例没有技术含量,对广大云用户也没有参考价值。他自己发展了一系列读者能复制的云原生案例,不仅省硬件成本,而且省开发成本。这些案例会陆续分享出来,请大家评论转发。
用户的需求
我自己闲暇时间也会做一些线上的工具类产品,用云计算来为这些产品提供服务支撑。也会有空接一些帮别人参谋的小活,给大大小小的商家或者小企业做上云架构。今天我想分享的案例就来自其中一个商家的实践。
这个商家做预制菜礼品,需要在不同规格的礼盒上贴二维码,用户扫码能看到该规格礼盒的一些信息。商家希望购买者微信扫码直接能打开微信小程序。礼盒规格不到 10 种,信息更新频率最多 2 次/年。
之前该商家用的一些表单类的 SAAS 小程序,生成页面展示。当商家想要有自己主体的小程序,之前的 SAAS 方案收费就有些贵。所以想自己建小程序,持续运行的成本尽量低。
我大概评估了一下需求,既然更新频率不高,那直接把规格信息写死放在微信小程序里,每年几次更新重新发版就是了,一点后端服务都没有,持续运行成本为 0。
在说服商家把二维码改成小程序码的过程中,明显感觉商家还是有自己小心思的。后面他希望能在其他 APP 扫码也能打开这个 APP 的小程序。说麦当劳肯德基就是这么做的,他也要这个感觉。
行吧,那这样必须要上一个后端服务了,这个服务主要有两个作用:
提供一个网址做为各宿主 APP 扫码的基础。做过小程序的同学应该了解扫普通二维码,其实就是在识别网址,根据不同的网址路由打开不同的小程序。
为未来的多端小程序提供商品信息,并能够在后面同时更新这些信息。
架构方案选型
我们按照刚才的指标过一遍:
可靠性: 商家是个小商家,购买者扫码只是看信息,不行也可以再扫一次,因此可以接受非 100%请求成功率,但也不能太离谱;也要提防这个商家万一做大了,访问量激增带来的负载问题。
时效性: 一年两次更新,每次更新都在半夜三更,也不用非要在意什么时效性。
整体价格: 希望持续运行的成本能越低越好。
由于可靠性和时效性这令人感动的要求,在市面上搞个 JSONBOX、图床网盘我都觉得能满足要求。可能只剩下价格这一个指标了。
虽然要求低,但最起码人家还是一个商家,最基本的安全稳定可靠,服务商不跑路才是第一位的,因此我还是直接在云计算厂商里挑选产品组合。
部分开发者直接的想法是,租个长期非常便宜的小规格服务器,部署一个简单的服务,并搭配一个域名和免费的 SSL 证书,这事就解决了。
除了直接操刀基础产品,对云产品的种类特点有些储备的情况下,也会提出下面几种:
使用无服务器云函数方案,调用一次付一次钱,信息更新直接更新云函数的代码配置。
使用对象存储,根据流量和调用次数,占用空间按量计费,通过覆盖文件来做信息更新。
如果在对象存储的基础上进一步搭配 CDN服务,在流量价格方面又能节省一部分。
以上几种也都能搭配自定义域名配免费的 SSL 证书。
在价格方面进一步权衡后,我最终给商家推荐了对象存储+CDN 方案,架构设计如下:
商品信息展示服务架构图在这个架构中,使用 CDN 服务绑定自定义域名,向对象存储桶回源。小程序根据扫码的链接直接解析出对应的商品信息地址,向 CDN 服务发起请求,得到信息后展示到页面中。
小程序效果图由于每年 2 次的更新频率,我觉得教商家用「对象存储客户端」自己维护几个信息文件问题不大,因此压根就没有做管理员更新这部分的软件开发。但如果商家的管理是多个人并且成体系的,我倒是可以用云的权限管控(给不同的员工开不同的云子账号)写一个基于对象存储的信息管理控制台,但在这个例子中这么做太浪费了。
下面主要介绍一下我的实施部分,分云服务产品的开通、前端小程序的开发调试两部分。
云服务产品实践
在上云的实践描述中,我都以 terraform 或其他 IaC 形式来展现,以准确清晰表述我所有的配置动作。如果你不了解 IaC 相关知识,可以先简单学习一下再上手。
为了简化大家复制动作,本文的 terraform 代码均写在一个 tf 文件下,你可以自己按规范建议分拆成多个文件。
腾讯云对象存储+CDN 的 tf 文件内容如下,locals 本地值里需要自己调整一下 domain 为自己的域名。
terraform { required_providers { tencentcloud = { source = “tencentcloudstack/tencentcloud” version = “1.81.60” } null = { source = “hashicorp/null” version = “3.2.2” } }}data “tencentcloud_user_info” “info” {}locals { app_id =data.tencentcloud_user_info.info.app_id
owner_uin =data.tencentcloud_user_info.info.owner_uin
region = “ap-shanghai” // 希望创建资源的地域 domain = “www.example.com” // 绑定的自定义域名 index_name = “index.html” // 访问中间页路径 index_file = “${path.module}/index.html” // 访问中间页文件 example_id = “0” // 示例参数 example_name = “asset/0.json” // 示例配置路径 example_file = “${path.module}/set.json” // 示例配置文件 cert = file(“${path.module}/ssl.crt”) // 自定义域名证书crt文件 private_key = file(“${path.module}/ssl.key”) // 自定义域名证书key文件}provider “tencentcloud” { region =local.region
}data “tencentcloud_cdn_domain_verifier” “shop_cdnvr” { domain =local.domain
auto_verify =true
freeze_record =true
}resource “null_resource” “check_verification” { count =data.tencentcloud_cdn_domain_verifier.shop_cdnvr.verify_result ? 0 : 1
provisioner “local-exec” { command = “echo 需要进行${data.tencentcloud_cdn_domain_verifier.shop_cdnvr.record_type}解析验证|${data.tencentcloud_cdn_domain_verifier.shop_cdnvr.sub_domain} | ${data.tencentcloud_cdn_domain_verifier.shop_cdnvr.record} && exit 1″ }}resource “tencentcloud_cos_bucket” “shop_bucket” { bucket = “shop-${local.app_id}“ acl = “private” multi_az =true
force_clean =true
website { index_document = “index.html” error_document = “index.html” }}resource “tencentcloud_cos_bucket_policy” “cos_policy” { bucket =tencentcloud_cos_bucket.shop_bucket.id
policy =<
{
“Statement”: [
{
“Principal”: {
“qcs”: [
“qcs::cam::uin/${local.owner_uin}:service/cdn”
]
},
“Effect”: “Allow”,
“Action”: [
“name/cos:GetObject”,
“name/cos:HeadObject”,
“name/cos:OptionsObject”
],
“Resource”: [
“qcs::cos:ap-shanghai:uid/${local.app_id}:${tencentcloud_cos_bucket.shop_bucket.id}/*”
]
}
],
“version”: “2.0”
}
EOF}resource “tencentcloud_cos_bucket_object” “upload_index” { bucket =tencentcloud_cos_bucket.shop_bucket.id
acl = “private” key =local.index_name
source =local.index_file
}resource “tencentcloud_cos_bucket_object” “upload_example” { bucket =tencentcloud_cos_bucket.shop_bucket.id
acl = “private” key =local.example_name
source =local.example_file
}resource “tencentcloud_cdn_domain” “shop_cdn” { depends_on = [null_resource.check_verification] domain =local.domain
service_type = “web” area = “mainland” cache_key { full_url_cache = “off” } origin { origin_type = “cos” origin_list = [“${tencentcloud_cos_bucket.shop_bucket.id}.cos-website.${local.region}.myqcloud.com”] server_name = “${tencentcloud_cos_bucket.shop_bucket.id}.cos-website.${local.region}.myqcloud.com” origin_pull_protocol = “follow” cos_private_access = “on” } rule_cache { cache_time =86400
rule_type = “all” rule_paths = [“*”] switch = “on” re_validate = “on” } compression { switch = “on” compression_rules { algorithms = [“gzip”] compress =true
max_length =2097152
min_length =256
rule_paths = [“js”, “html”, “css”, “xml”, “shtml”, “htm”, “json”] rule_type = “file” } } https_config { https_switch = “on” http2_switch = “on” force_redirect { switch = “on” redirect_type = “https” redirect_status_code =302
} ocsp_stapling_switch = “on” server_certificate_config { message = “自上传证书” certificate_content =local.cert
private_key =local.private_key
} verify_client = “off” }}output “manage_url” { value = “https://cosbrowser.cloud.tencent.com/editor?bucket=${tencentcloud_cos_bucket.shop_bucket.id}®ion=ap-shanghai” description = “存储桶文件管理地址”}output “dns_cname” { value =tencentcloud_cdn_domain.shop_cdn.cname
description = “域名cname解析内容”}output “wxapp_url” { value = “https://developers.weixin.qq.com/s/KMyOSEmp7MN7” description = “微信小程序代码片段”}output “scan_url” { value = “https://${local.domain}/${local.example_id}“ description = “在小程序后台配置二维码扫描后,将此路径制作成二维码,用微信扫码观看效果”}在 tf 文件同级目录下,还有几个配套的文件:
index.html: 放置在对象存储桶里,用于扫码的中转页(非微信客户端扫码时展示提示)
set.json: 一个商品信息的示例,你可以根据自己的需要配合小程序自行修改
ssl.key: 自定义域名证书 key 文件
ssl.crt: 自定义域名证书 crt 文件
除了两个证书文件需要自己提供以外,其他两个的文件内容如下:
index.html
<html><head> <meta charset=“UTF-8“> <meta name=“viewport“ content=“width=device-width, initial-scale=1“> <title>中间页面</title></head><body> <div style=“margin-top: 100px;text-align: center;“>请使用微信客户端扫描此二维码</div></body></html>set.json
{ “title”: “家宴高端预制菜礼盒”, “desc”: “理想中开发食品有限公司”, “price”: { “p”: “¥98.00-208.00”, “t”: “建议零售价” }, “list”: [{ “label”:“规格”, “value”:“370 × 170 × 280 mm” },{ “label”:“菜品”, “value”:“胡椒猪肚鸡、毛血旺、红烧肉、酸汤肥牛、水煮肉片、辣子鸡丁、金汤肥牛、鱼香肉丝、牛肉煲、木须肉、糖醋里脊、宫保鸡丁、藤椒鲜炒鸡、菠萝咕咾肉、金汤酸菜鱼”, }]}接下来运行 terraform,具体使用参考腾讯云文档
https://cloud.tencent.com/document/product/1653/82867
由于是第一次讲述 terraform 代码配置,因此在这里特意列下执行过程,后面的架构文章就不再介绍了。
如果你想本地运行,需要在腾讯云的权限管控中获取 SK 信息,并写入环境变量:
export TENCENTCLOUD_SECRET_ID=YOUR_SECRET_ID
export TENCENTCLOUD_SECRET_KEY=YOUR_SECRET_KEY
在目录中运行终端,执行 init 命令,安装 provider plugins
terraform init
完成后,运行 plan 命令查看执行计划
terraform plan -out=“coscdn”// 将计划导出到执行目录中,文件名coscdn
我们可以清晰的在计划中看到我们要做什么,新增、更新、移除资源的数量,以及拟输出的内容等等。
确认执行的内容后,我们可以用 apply 来执行
terraform apply “coscdn”// 这里的 coscdn 是 plan 时保存的
执行过程包含,开通对象存储桶,配置 policy,上传 2 个测试文件,开通 CDN 共 5 个部分
最后输出了几个内容(这些内容都是 tf 中定义的,后面其他案例会不一样,每个案例都会描述这一部分):
dns_cname: 自定义域名应该 cname 解析的值,由于自定义域名可能来自不同的服务商,因此没有直接写到tf中,需要手动配置这个部分,如果你的域名也在腾讯云上,可以直接加配置解析这一部分。
manage_url: 可以访问该地址,登录腾讯云账号来管理对象存储的内容。
后面商家自己可以创建同样的文件,自己维护里面的内容scan_url: 根据自定义域名做的二维码解析,以域名+商品 ID 的形式出现。由于在对象存储中配置了 error 页均为 index.html,因此除了 asset 下信息文件路径正常返回内容外,其余的所有路径均返回 index.html。这个是开发设计如此,你可以根据自己需要更改这一部分。
wxapp_url: 这个是微信小程序代码片段链接,复制到浏览器打开后,会引导你安装或打开微信开发者工具 IDE,里面有相关的代码,接下来我们重点介绍这一部分。
如果在执行过程中报出了下述错误,需要先做域名 TXT 解析验证,验证域名所有权归属于你。
微信小程序开发调试
微信小程序的调试开发比较简单,使用代码片段导入后,打开 page/index/index.js 文件,将前 3 行代码做一些调整,改为自己的域名。
另外微信小程序扫普通二维码配置需要前往微信公众平台(mp.weixin.qq.com),在 开发配置 中按照规范配置内容:
在微信开发者工具IDE中,可以通过配置参数 q=https%3A%2F%2Fwww.example.com%2F0 来模拟扫码 https://www.example.com/0 进入小程序。(URL 需要通过encodeURIComponent方法处理)
运行效果如下:
为什么要用 IaC
有部分人可能不太理解和适应 terraform 的表达形式,觉得用控制台的截图配文字说明更直观。
我之前给别人方案时,无论我说的怎样详实,在每个人的理解中也会有很多差异。即使如严谨的法律条文,不同的情景下也有不同的解释结果。
我们需要开通两个产品:对象存储、CDN 服务。CDN 服务需要配置 SSL 证书,并将源站指向对象存储桶。对象存储的 ACL 设置为私有读写,但要开鉴权给 CDN 服务。
大家有没有发现,我上面 balabala 说了一大堆,很多关键点都没说清,比如 CDN 服务的缓存规则具体怎么配置、自定义域名如何解析,应该解析什么内容、对象存储是否要开启静态网站能力、CDN 对 COS 开启鉴权应该怎么配置 Policy。
为了把这些表述清楚,我可能要配很多控制台截图或者操作动图。但大家在按方抓药时难免也有遗漏,这种实践的展现在传输时很多信息会丢失,复现成功率很低。
另外如果云厂商的控制台大改版,之前所有的截图基本就全废了。虽然我是腾讯云的资深用户,我也在架构方案中频繁提及腾讯云,但我并不认为腾讯云的文档和实践指引就是合格的。
我翻了翻腾讯云的 CDN 快速入门指引,看到的是这种内容:
而好巧不巧的是,这两天腾讯云控制台大改版。CDN 控制台内部的表格顺序也发生变化了,要不是深度结合上下文,一路连蒙带猜,很多人都不知道应该点什么。
这种带控制台截图的确实有用,可以给不懂操作的用户一步步指导(但截图并不是唯一的形式,也有更多更直观有效的展现形式给用户教学)。而且在每一次控制台页面内容发生变化后,都需要产品同学来第一时间更新维护才行,过时的截图对新手用户作用是负数。
尤其对于我这种带场景的架构实践,大家一定会想一比一复刻我的实践,在其基础上做一些理解和自由变更。这种前提基础上,我就必然会选择用一些更准确的表现形式,即使大家不想用 terraform,也可以根据其中的代码细节,得到我每个云产品的配置信息和前后关系。
云厂商乱推荐
国内云计算厂商每年的大促活动,跟淘宝双十一有的一拼。
我前两天去各个云登录了一圈对比了下云产品规格,然后我的手机就被各家云厂商的销售打爆了,不约而同的话术就是:我们有活动,买服务器便宜,还有更低的折扣呢!
我对这些电话的内容感到非常无语。做为用户,我希望的是能够在安全可靠的基础上,花更少的人力成本和普惠价格完成自己的业务支撑。
价格当然是因素之一,但不是全部啊。电话上来不问具体的需求,或者问了具体的需求还是直接说服务器便宜,要不要买几台啊?
这种电销模式带来的无非两种客户:
单用服务器、硬盘等基础产品搭建服务的用户,唯一的诉求就是能便宜。
学生或者个人尝鲜开发者,少喝两杯咖啡省出来一个小服务器然后自己玩玩。
上云的衡量
如果你只是一个人想自己玩一下,那参与这种优惠没啥问题,反正没啥生产数据,第二年续费贵了之后再换一家就是,哪家的羊毛不是薅啊?
但如果你是要给一个企业或者商家做一个上云方案,那价格这方面就可能需要往后放一放了,我们要先做架构选型,然后再谈价格的问题。
不同的场景需求,都应该有其适合的架构。衡量架构是否合适的指标有几点:
可靠性: 在客户业务需求和访问情况确定时,对请求成功率是否有要求,是否可以接受短暂服务不可用。我觉得没客户会主动说接受不可用,架构师需要评估服务停摆带来的损失,来判断是否值得用更可靠的设计来尽量减少服务不可用带来的损失。
时效性: 客户是否关心数据的时效性,读写是否必须同步。比如商品秒杀就非常在意数据时效性,而社区文章阅读点赞则可以不做数据实时同步。时效性的要求对架构的产品选型至关重要,它也有效决定了最终架构方案的价格。
整体价格: 在客户能预测的服务时间区间中,服务运行成本加区间内需要的技术和人工成本能接受的预算有多少。
以上这几点,首先应该考虑的是可靠性和时效性,给出相应的一种或几种方案,最后再去比较价格;确定最优的方案后,去各大云厂商比较价格。
我一直认为世界上不存在什么云计算标准架构,有的也只有适用场景需求的推荐架构。
写在后面
上面展示的案例代码简化了很多,实际上还有轮播图和菜品图片。目前该商家小程序上线已经小半年了,这半年总花费约 5 块钱,平均单月价格 0.78 元,相比买服务器是很便宜了。
在帮助企业组织来做云服务的架构设计过程中,我发现大部分企业技术决策者,对如何做云计算架构设计没有一点经验,还是按照 IDC 时代的主机角色来看待架构问题。
而各大云计算厂商好像也不太重视云计算推荐架构的案例推广。案例全是大企业花重金采购的一个单一产品这类的,这对要上云的大部分用户没有任何参考价值,致使大部分的潜在上云用户的认知是严重失衡的。
因此,我计划不断探索和整理一些有意义且可复制的案例,帮助更多潜在上云用户理解和获得云服务的价值。既然各大云服务商对此类案例不够重视,那我们就自己来填补这一空白。
关于作者:
ZiraLi,微信生态领域 MVP,就职于相关团队担任技术产品经理、架构师;为多家企业组织提供上云架构和微信生态产品咨询服务。