近期把博客折腾到腾讯云了,对比了腾讯云和阿里云后,发现腾讯云近期 CDN 做了很多升级,对于 CDN 缓存节点的配置更加细致,另外毕竟腾讯云价格更低嘛,对个人站长更友好一些。这篇文章主要讲讲如何使用腾讯云的 COS+CDN 部署静态网站,以及使用腾讯云函数服务完美解决(对官方提供的刷新函数做了些优化)CDN 节点的缓存自动刷新问题。
我的部署方案
运行环境:站点解析采用境内和境外分开解析的方案,境内解析到腾讯云的 CDN 节点,境外解析到 Vercel 的 CDN 节点,虽然站点的主要访问由于就是国内用户,但毕竟谷歌、必应等等搜索引擎都是国外的,而国内 CDN 对于境外的加速又比较贵,所以就分开解析啦。
境内:腾讯云 COS 对象存储 + 腾讯云 CDN 加速
境外:Vercel 静态网站托管
自动化部署:由于站点的源码托管在 GitHub 上面,所以使用 GitHub Actions 进行自动化部署,写完文章直接 push 上去就可以自动部署到多个平台,简直不要太方便。
GitHub Actions 自动部署到腾讯云 COS 腾讯云提供了非常好用的 cli 工具,执行一些简单的命令就可以快速上传文件到 COS 对象存储。在 Git 工程的根目录下创建.github/workflows/xxx.yml
文件,就可以创建一个 Actions 配置。 使用下面的配置可以将 Hexo 部署到腾讯云 COS 对象存储:
.github/workflows/deploy.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 name: Blog on: push: branches: - master jobs: deploy: name: Deploy Blog runs-on: ubuntu-latest env: TZ: Asia/Shanghai steps: - name: Checkout Repository master branch uses: actions/checkout@v2 with: ref: 'master' submodules: true - name: Setup Node.js uses: actions/setup-node@master with: node-version: "14.x" - name: Yarn Install Cache uses: c-hive/gha-yarn-cache@v1 - name: Install Dependencies run: yarn install - name: Generate Hexo Site Public Files & Create Files for Blog Assets run: yarn build - name: Deploy to Tencent COS env: SECRET_ID: ${{ secrets.TENCENT_SECRET_ID }} SECRET_KEY: ${{ secrets.TENCENT_SECRET_KEY }} BUCKET: ${{ secrets.TENCENT_COS_BUCKET }} REGION: ap-shanghai run: | sudo pip install coscmd coscmd config -a ${SECRET_ID} -s ${SECRET_KEY} -b ${BUCKET} -r ${REGION} coscmd upload -rs --delete ./public/ / -f
其中:
${{ secrets.TENCENT_SECRET_ID }}
、${{ secrets.TENCENT_SECRET_KEY }}
、${{ secrets.TENCENT_COS_BUCKET }}
需要在 github 仓库的 secrets 设置中进行配置。
命令 coscmd upload -rs --delete ./public/ / -f
的意思是比较当前 COS 存储桶内的文件,如果发生变更则更新,如果存储桶中存在但 public 目录中不存在,则删除对应的文件,coscmd
的具体使用方法可以参考腾讯云官方文档 。
开启 COS 静态网站功能 将网站文件上传到 COS 还不够,需要开启 COS 的静态网站功能,打开存储桶 -> 基础配置 -> 静态网站 ,按照下图所示进行配置:
这时就可以通过图中的访问节点提供的域名来访问网站了,当然也可以绑定自定义域名,前提是域名已经备案。
腾讯云 CDN 加速 创建域名 如果你的域名有备案的话,同时可以使用腾讯云 CDN 加速 COS 静态网站,配置十分简单,在 CDN 页面中创建一个域名,按下图所示进行配置:
配置缓存 缓存配置十分重要,由于站点是静态站点,网站的内容变化比较少,为了降低 CDN 的回源请求,一定要配置节点缓存策略,可以参考我的配置如下,其中浏览器缓存可以按需配置:
注意:这里不用担心节点缓存时间配置的过长会导致页面无法更新,后面我会讲如何配置缓存的自动刷新。
关于 CDN 配置的更多细节就不讲了,都很简单,一看就懂。
CDN 节点缓存自动刷新 官方方案 由于上一步中配置了 CDN 节点的缓存策略,请求只要能够命中缓存,就不会进行回源请求了,这会导致我们的页面更新不能及时的展现给用户,因此需要考虑如何进行 CDN 节点的缓存自动刷新。
腾讯云官方给我们提供了一个解决方案,可以在 COS 存储桶的函数计算 ->CDN 缓存刷新函数 中配置一个函数,可参考下图所示配置:
但这个方案存在一个问题,由于我们的静态网站有默认索引页面 index.html,而官方提供的这个函数只会刷新对应的文件的 URL,而不会刷新索引 URL,例如 http://www.bytelife.net/index.html
这个文件,通常我们的请求是 http://www.bytelife.net/
,因此官方的方案针对于静态网站来说不算完美。
优化方案 可以通过简单修改官方的函数来解决这个问题,点击刚刚创建的 CDN缓存刷新函数
列表中的函数名称,可以跳转的函数的编辑页面:
将 index.js 文件内容替换为下面的代码,最后点击右上角的 “部署” 按钮即可:
index.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 'use strict' const CosSdk = require ('cos-nodejs-sdk-v5' )const CdnSdk = require ('./common/CdnSdk' )const CdnRefreshTask = require ('./common/CdnRefreshTask' )const { getParams, getObjectUrl, logger, getLogSummary } = require ('./common/utils' ) exports .main_handler = async (event, context, callback) => { const { objects, cdnHosts, secretId, secretKey, token } = getParams(event) logger({ title: 'param is parsed success, param as follow: ' , data: { objects, cdnHosts, event } }) if (!secretId || !secretKey || !token) { throw new Error (`secretId, secretKey or token is missing` ) } const cdnSdkInstance = new CdnSdk({ secretId, secretKey, token }) const cosInstance = new CosSdk({ SecretId: secretId, SecretKey: secretKey, XCosSecurityToken: token }) const taskList = objects.map(({ bucket, region, key } ) => { const purgeUrls = []; cdnHosts.forEach(host => { const tempUrl = getObjectUrl({ cosInstance, bucket, region, key, origin: `${/^(http\:\/\/|https\:\/\/)/ .test(host) ? '' : 'https://' } ${host} ` }); purgeUrls.push(tempUrl); if (tempUrl.lastIndexOf('/index.html' ) == (tempUrl.length - 11 )){ purgeUrls.push(tempUrl.substr(0 , tempUrl.length - 10 )) } }); return new CdnRefreshTask({ cdnSdkInstance, urls: purgeUrls }) }) const taskResults = [] for (const task of taskList) { const results = await task.runPurgeTasks() taskResults.push(...results) } logger({ title: 'cdn refresh full logs:' , data: taskResults }) const { status, messages } = getLogSummary(taskResults) logger({ messages: messages.map(item => item.replace(/\,\ /g , '\n' )) }) if (status === 'fail' ) { throw messages.join('; ' ) } else { return messages.join('; ' ) } }