Serverless + GitHub Actions 完美自动化部署静态网站

作为强迫症患者,一直对自动化部署非常痴迷,个人认为全自动部署最重要的就是稳定可靠。经过研究测试,最终使用 GitHub 和腾讯云两大平台,成功完成了全自动部署网站的实践。

本文来自 Serverless 社区用户「Stille」投稿

方案简介

业务需求

博主有一个简单的纯静态文档站点 docs.ioiox.com,使用的的是 docsify 项目的 Markdown 渲染程序,平时通过本地 VSCode 编辑文档,并提交到 GitHub。早前是直接使用 GitHub Pages 绑定域名来访问,但由于网络问题,体验并不好。

寻求方案

腾讯云对象存储 COS 服务能够提供静态网页服务,并可以配置 CDN 域名进行访问。那么就需要解决以下两个问题:

  1. 如何使 GitHub 自动同步文件到腾讯云 COS
  2. 腾讯云 COS 对应的 CDN 如何自动刷新

解决方案

  • GitHub Action - 配置每次 Push 代码后自动上传到 COS
  • 腾讯云云函数 SCF - 检测到 COS 内文件变动后自动刷新对应的 CDN 链接

方案流程图

第一阶段 - GitHub Actions

2019 年 11 月,GitHub 正式开放了 GitHub Actions 这个功能,不再需要申请就能自由使用,目前是按照 workflow 的使用时长来收费,个人用户每月 2000 分钟的免费额度也基本够用了。

获取腾讯云 API 密钥

登录腾讯云控制面板 - 访问控制 - 访问密钥 - API 密钥管理

新建密钥

新建密钥

此密钥拥有所有权限,为保证安全,也可以添加子用户,配置 COS,CDN 对应的权限

配置腾讯云 COS

登录腾讯云控制面板 - 对象存储 - 存储桶列表

创建存储桶

选择适合你的区域,设置权限为 公有读私有写.

serverless

serverless

获取存储桶相关信息

serverless

配置 GitHub Actions

GitHub仓库 - Settings - Secrets

添加 SecretIdSecretKey 分别为刚才获取的腾讯云 API 密钥

serverless

GitHub仓库 - Actions

默认会有很多推荐的 workflows,这里选择 Set up a workflow yourself 自己来配置。

serverless

系统会创建一个 workflow 的 yml 配置文件,删除预设代码,复制以下样本代码。

图上标红两处需修改为刚才创建存储桶获取的名称和区域

然后右上角提交即可。

serverless

yml 配置文件样本

name: Upload to COS

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Install coscmd
      run: sudo pip install coscmd
    - name: Configure coscmd
      env:
        SECRET_ID: ${{ secrets.SecretId }}
        SECRET_KEY: ${{ secrets.SecretKey }}
        BUCKET: docs-1300533487
        REGION: ap-shanghai
      run: coscmd config -a $SECRET_ID -s $SECRET_KEY -b $BUCKET -r $REGION
    - name: Upload
      run: coscmd upload -rs --delete -f ./ / --ignore "./.git/*"

测试 GitHub Actions

提交 yml 后系统检测到 main.yml 的 push,便会开始运行这个 workflow,根据 yml 配置文件,可以看出整个工作流简单理解为安装了腾讯云的 coscmd 工具,并根据配置的 SecretIdSecretKeyBUCKETREGION 上传整个仓库到腾讯云 COS,同时忽略掉 .git 文件夹。其中 upload -rs 命令会使用 md5 比对存储桶中已存在的文件,相同文件将会跳过上传。

serverless

serverless

第二阶段 - 腾讯云函数 SCF

serverless

配置腾讯云 CDN 域名

登录腾讯云控制面板 - 对象存储

进入创建的存储桶 - 基础配置 - 开启静态网站

serverless

域名管理

添加自定义加速域名,并设置域名指向生成的CNAME地址,源站类型改为静态网站源站

serverless

控制面板 - 内容分发网络 - 域名管理

点击添加的域名 - 高级配置

开启 HTTPS,设置强制跳转 HTTPS,并更改跳转方式为 301。在点击前往配置申请免费证书。

serverless

配置云函数 SCF

登录腾讯云控制面板 - 云函数

首次使用云函数可能会跳出 服务授权 框,需要前往访问添加并同意授权即可。该角色对本次添加的云函数没有影响。

serverless

serverless

选择和你存储桶相同区域并新建

填写函数名,运行环境选择 Php 5.6,创建方式选择 空白函数 下一步。

serverless

serverless

函数配置

上部分保持默认即可

删除默认代码,复制以下样本代码至此.

图上标红两处需修改为之前获取的 API 密钥,注意此处的 ID 和 KEY 顺序和之前配置 GitHub Actions 时是相反的,并把 CDN 链接改为你的域名,如果域名已配置过 HTTPS 和证书,确保此处为 https。

完成即可

serverless

serverless

函数代码样本

<?php
$gl = 1;
function main_handler($event, $context) {
    $eve = json_decode(json_encode($event,JSON_FORCE_OBJECT),true);
    $usr_url=strval($eve["Records"][0]["cos"]["cosObject"]["url"]);

    //截取object部分
    $object=substr($usr_url,strpos($usr_url,"/",8));

    /*需要填写您的密钥,可从  https://console.cloud.tencent.com/capi 获取 SecretId 及 $secretKey*/
    $secretKey='XXXXXXXXXXXXXX';
    $secretId='XXXXXXXXXXXXXX';
    $action='RefreshCdnUrl';

    $HttpUrl="cdn.api.qcloud.com";
    /*除非有特殊说明,如MultipartUploadVodFile,其它接口都支持GET及POST*/
    $HttpMethod="GET";
    /*是否https协议,大部分接口都必须为https,只有少部分接口除外(如MultipartUploadVodFile)*/
    $isHttps =true;
    $nurl="https://XXXX.XXXX.com".$object; //   示例:$nurl="http://abc.com".$object;
    //print_r($nurl);

    /*下面这五个参数为所有接口的 公共参数;对于某些接口没有地域概念,则不用传递Region(如DescribeDeals)*/
    $COMMON_PARAMS = array(
                    'Nonce' => rand(),
                    'Timestamp' =>time(NULL),
                    'Action' =>$action,
                    'SecretId' => $secretId,
                    'SignatureMethod' => 'HmacSHA256',
                    'urls.0' => $nurl
                    );
    $PRIVATE_PARAMS = array();
    //**********执行CDN刷新URL操作**********/
    CreateRequest($HttpUrl,$HttpMethod,$COMMON_PARAMS,$secretKey, $PRIVATE_PARAMS, $isHttps);
   return "RefreshCdnUrl OK";
}
/***************CDN API调用方法***************/
function CreateRequest($HttpUrl,$HttpMethod,$COMMON_PARAMS,$secretKey, $PRIVATE_PARAMS, $isHttps)
{
        $FullHttpUrl = $HttpUrl."/v2/index.php";

        /***************对请求参数 按参数名 做字典序升序排列,注意此排序区分大小写*************/
        $ReqParaArray = array_merge($COMMON_PARAMS, $PRIVATE_PARAMS);
        ksort($ReqParaArray);

        /**********************************生成签名原文**********************************
         * 将 请求方法, URI地址,及排序好的请求参数  按照下面格式  拼接在一起, 生成签名原文,此请求中的原文为
         * GETcvm.api.qcloud.com/v2/index.php?Action=DescribeInstances&Nonce=345122&Region=gz
         * &SecretId=AKIDz8krbsJ5yKBZQ    ·1pn74WFkmLPx3gnPhESA&Timestamp=1408704141
         * &instanceIds.0=qcvm12345&instanceIds.1=qcvm56789
         * ****************************************************************************/
        $SigTxt = $HttpMethod.$FullHttpUrl."?";
        $isFirst = true;
        foreach ($ReqParaArray as $key => $value)
        {
                if (!$isFirst)
                {
                        $SigTxt = $SigTxt."&";
                }
                $isFirst= false;
                /*拼接签名原文时,如果参数名称中携带_,需要替换成.*/
                if(strpos($key, '_'))
                {
                        $key = str_replace('_', '.', $key);
                }
                $SigTxt=$SigTxt.$key."=".$value;
        }
        /*********************根据签名原文字符串 $SigTxt,生成签名 Signature******************/
        $Signature = base64_encode(hash_hmac('sha256', $SigTxt, $secretKey, true));

        /***************拼接请求串,对于请求参数及签名,需要进行urlencode编码********************/
        $Req = "Signature=".urlencode($Signature);
        foreach ($ReqParaArray as $key => $value)
        {
                $Req=$Req."&".$key."=".urlencode($value);
        }

        /*********************************发送请求********************************/
        if($HttpMethod === 'GET')
        {
                if($isHttps === true)
                {
                        $Req="https://".$FullHttpUrl."?".$Req;
                }
                else
                {
                        $Req="http://".$FullHttpUrl."?".$Req;
                }
                $Rsp = file_get_contents($Req);
        }
        else
        {
                if($isHttps === true)
                {
                        $Rsp= SendPost("https://".$FullHttpUrl,$Req,$isHttps);
                }
                else
                {
                        $Rsp= SendPost("http://".$FullHttpUrl,$Req,$isHttps);
                }
        }
        var_export(json_decode($Rsp,true));
}
function SendPost($FullHttpUrl, $Req, $isHttps)
{
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $Req);
        curl_setopt($ch, CURLOPT_URL, $FullHttpUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        if ($isHttps === true) {
                curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,  false);
                curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,  false);
        }
        $result = curl_exec($ch);
        return $result;
}
?>

测试函数代码

确认 API 及 CDN 配置正确,点击测试,返回成功。

serverless

添加触发方式

此处需要分别添加 全部创建全部删除 两个触发方式

触发方式:COS 触发

COS Bucket:选择你的存储桶 (请再次确保存储桶和云函数的区域相同)

事件类型:全部创建全部删除

serverless

测试配置

腾讯云控制台 - 内容分发网络

左侧刷新预热 - 操作记录 - 查询

可以看到刚才测试成功的一条记录,现在可以尝试在 Push 代码到 GitHub 来完整的测试整个流程了。

serverless

Serverless Framework 30 天试用计划

我们诚邀您来体验最便捷的 Serverless 开发和部署方式。在试用期内,相关联的产品及服务均提供免费资源和专业的技术支持,帮助您的业务快速、便捷地实现 Serverless!

详情可查阅:Serverless Framework 试用计划

One More Thing

3 秒你能做什么?喝一口水,看一封邮件,还是 —— 部署一个完整的 Serverless 应用?

复制链接至 PC 浏览器访问:https://serverless.cloud.tencent.com/deploy/express

3 秒极速部署,立即体验史上最快的 Serverless HTTP 实战开发!

传送门:

  • GitHub: github.com/serverless
  • 官网:serverless.com

欢迎访问:Serverless 中文网,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发!


推荐阅读:《Serverless 架构:从原理、设计到项目实战》

热门文章

暂无图片
编程学习 ·

那些年让我们目瞪口呆的bug

程序员一生与bug奋战&#xff0c;可谓是杀敌无数&#xff0c;见怪不怪了&#xff01;在某知识社交平台中&#xff0c;一个“有哪些让程序员目瞪口呆的bug”的话题引来了6700多万的阅读&#xff0c;可见程序员们对一个话题的敏感度有多高。 1、麻省理工“只能发500英里的邮件” …
暂无图片
编程学习 ·

redis的下载与安装

下载redis wget http://download.redis.io/releases/redis-5.0.0.tar.gz解压redis tar -zxvf redis-5.0.0.tar.gz编译 make安装 make install快链方便进入redis ln -s redis-5.0.0 redis
暂无图片
编程学习 ·

《大话数据结构》第三章学习笔记--线性表(一)

线性表的定义 线性表&#xff1a;零个或多个数据元素的有限序列。 线性表元素的个数n定义为线性表的长度。n为0时&#xff0c;为空表。 在比较复杂的线性表中&#xff0c;一个数据元素可以由若干个数据项组成。 线性表的存储结构 顺序存储结构 可以用C语言中的一维数组来…
暂无图片
编程学习 ·

对象的扩展

文章目录对象的扩展属性的简洁表示法属性名表达式方法的name属性属性的可枚举性和遍历可枚举性属性的遍历super关键字对象的扩展运算符解构赋值扩展运算符AggregateError错误对象对象的扩展 属性的简洁表示法 const foo bar; const baz {foo}; baz // {foo: "bar"…
暂无图片
编程学习 ·

让程序员最头疼的5种编程语言

世界上的编程语言&#xff0c;按照其应用领域&#xff0c;可以粗略地分成三类。 有的语言是多面手&#xff0c;在很多不同的领域都能派上用场。大家学过的编程语言很多都属于这一类&#xff0c;比如说 C&#xff0c;Java&#xff0c; Python。 有的语言专注于某一特定的领域&…
暂无图片
编程学习 ·

写论文注意事项

参考链接 给研究生修改了一篇论文后&#xff0c;该985博导几近崩溃…… 重点分析 摘要与结论几乎重合 这一条是我见过研究生论文中最常出现的事情&#xff0c;很多情况下&#xff0c;他们论文中摘要部分与结论部分重复率超过70%。对于摘要而言&#xff0c;首先要用一小句话引…
暂无图片
编程学习 ·

安卓 串口开发

上图&#xff1a; 上码&#xff1a; 在APP grable添加 // 串口 需要配合在项目build.gradle中的repositories添加 maven {url "https://jitpack.io" }implementation com.github.licheedev.Android-SerialPort-API:serialport:1.0.1implementation com.jakewhart…
暂无图片
编程学习 ·

2021-2027年中国铪市场调研与发展趋势分析报告

2021-2027年中国铪市场调研与发展趋势分析报告 本报告研究中国市场铪的生产、消费及进出口情况&#xff0c;重点关注在中国市场扮演重要角色的全球及本土铪生产商&#xff0c;呈现这些厂商在中国市场的铪销量、收入、价格、毛利率、市场份额等关键指标。此外&#xff0c;针对…
暂无图片
编程学习 ·

Aggressive cows题目翻译

描述&#xff1a; Farmer John has built a new long barn, with N (2 < N < 100,000) stalls.&#xff08;John农民已经新建了一个长畜棚带有N&#xff08;2<N<100000&#xff09;个牛棚&#xff09; The stalls are located along a straight line at positions…
暂无图片
编程学习 ·

剖析组建PMO的6个大坑︱PMO深度实践

随着事业环境因素的不断纷繁演进&#xff0c;项目时代正在悄悄来临。设立项目经理转岗、要求PMP等项目管理证书已是基操&#xff0c;越来越多的组织开始组建PMO团队&#xff0c;大有曾经公司纷纷建造中台的气质&#xff08;当然两者的本质并不相同&#xff0c;只是说明这个趋势…
暂无图片
编程学习 ·

Flowable入门系列文章118 - 进程实例 07

1、获取流程实例的变量 GET运行时/进程实例/ {processInstanceId} /变量/ {变量名} 表1.获取流程实例的变量 - URL参数 参数需要值描述processInstanceId是串将流程实例的id添加到变量中。变量名是串要获取的变量的名称。 表2.获取流程实例的变量 - 响应代码 响应码描述200指…
暂无图片
编程学习 ·

微信每天自动给女[男]朋友发早安和土味情话

微信通知&#xff0c;每天给女朋友发早安、情话、诗句、天气信息等~ 前言 之前逛GitHub的时候发现了一个自动签到的小工具&#xff0c;b站、掘金等都可以&#xff0c;我看了下源码发现也是很简洁&#xff0c;也尝试用了一下&#xff0c;配置也都很简单&#xff0c;主要是他有一…
暂无图片
编程学习 ·

C语言二分查找详解

二分查找是一种知名度很高的查找算法&#xff0c;在对有序数列进行查找时效率远高于传统的顺序查找。 下面这张动图对比了二者的效率差距。 二分查找的基本思想就是通过把目标数和当前数列的中间数进行比较&#xff0c;从而确定目标数是在中间数的左边还是右边&#xff0c;将查…
暂无图片
编程学习 ·

项目经理,你有什么优势吗?

大侠被一个问题问住了&#xff1a;你和别人比&#xff0c;你的优势是什么呢? 大侠听到这个问题后&#xff0c;脱口而出道&#xff1a;“项目管理能力和经验啊。” 听者抬头看了一下大侠&#xff0c;显然听者对大侠的这个回答不是很满意&#xff0c;但也没有继续追问。 大侠回家…
暂无图片
编程学习 ·

nginx的负载均衡和故障转移

#注&#xff1a;proxy_temp_path和proxy_cache_path指定的路径必须在同一分区 proxy_temp_path /data0/proxy_temp_dir; #设置Web缓存区名称为cache_one&#xff0c;内存缓存空间大小为200MB&#xff0c;1天没有被访问的内容自动清除&#xff0c;硬盘缓存空间大小为30GB。 pro…
暂无图片
编程学习 ·

业务逻辑漏洞

身份认证安全 绕过身份认证的几种方法 暴力破解 测试方法∶在没有验证码限制或者一次验证码可以多次使用的地方&#xff0c;可以分为以下几种情况︰ (1)爆破用户名。当输入的用户名不存在时&#xff0c;会显示请输入正确用户名&#xff0c;或者用户名不存在 (2)已知用户名。…