每次都把打包好的文件手动复制粘贴到服务器然后再手动停止重启 IIS 未免也太复杂了对吧。能省一点力气就省一点么。虽然还有很多功能还没做到。
闲话
最近无聊在研究 CI/CD,本来是像无头苍蝇一样在看 Jenkins 来着,但是在某位不愿透露姓名的大佬(@陈宁)的点拨下开始看 GitHub Actions 了。
看到一半就想说,我要是能用这玩意儿把我自己的某个项目放到我的服务器上,岂不美哉?
刚好,我这儿有一个自己的基于 .NET Core 的一套 API,还有一个阿里云服务器,那就来试试!
当然了,这篇文章只说 .NET Core -> Windows Server IIS 这一种场景。其他的应该比这个要简单,就不介绍了。
毕竟也没亲自试过对吧。
所以涉及到这么几个东西:
- GitHub Repository
- GitHub Actions
- GitHub Self-hosted Runner
- 运行 Windows Server 且已安装 IIS 的服务器
当然了,IIS 的配置这儿就不说了,超 scope 了。就默认 IIS 已经配置好了,且 IIS 网站也建好了,也绑定到了某个端口和物理路径。
在 Windows Server 上安装 GitHub Self-hosted Runner
为什么要安装个这东西呢?
长话短说,就是,我们需要在服务器上安装这个东西,能让服务器根据我们的命令,从 github.com 上下载我们打包好的 Artifact。
这里的 Artifact,特指我们通过 dotnet publish 或其他等效操作打包好的可以直接部署到 IIS 的一堆文件。只要我们把这堆文件上传到 GitHub,再让服务器把文件从 GitHub 上下载到服务器的某个位置,再在 IIS 里进行绑定,那不就可以了!
要安装 GitHub Self-hosted Runner,需要:
- 访问 GitHub 上的 Repository
- 点击 Settings
- 点击左侧 Code and automation 下 Actions 的 Runners
- 点击 New self-hosted runner 按钮
- 在打开的页面中,选择你的服务器操作系统和架构。这里我选择的是 Windows x64
- 在服务器的 Powershell 中,运行 GitHub 提供的命令。
在运行 Invoke-WebRequest
命令时,可能会遇见报错。如果提示“未能创建 SSL/TLS 安全通道,可以运行
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Ssl3 -bor [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12
[Net.ServicePointManager]::SecurityProtocol
来解决问题。
在下载这一步可能需要花费一点时间。整个压缩包大概 70M 左右,具体花费时间取决于服务器速度。
如果发现运行 configure 命令时报错,试着返回 GitHub 页面刷新一下获取一个新的 token 再重试。
到此,Runner 就安装好了。我们可以在 Actions Workflow 中的 yaml 文件中声明 runs-on: self-hosted
后,给这个 Runner 发送命令。
编写 yaml 文件
接下来我们开始编写给 workflow 用的 yaml 文件。
我的需求是这样的:
- 在 main 分支有代码 push 或有 PR 被 merge 到 main 分支时,执行 workflow。
- 在部署之前,需要运行
dotnet build
命令,以确保代码可以编译通过。 - 在部署之前,需要运行
dotnet test
命令,以确保单元测试可以通过。 - 运行
dotnet publish
命令,将代码打包成发布文件。 - 将打包好的发布文件上传到 GitHub Artifact。
- 中断 IIS 服务。
- 服务器将上传上去的 GitHub Artifact 下载到服务器 IIS 指定的位置。
- 重新启动 IIS 服务。
由于一个 workflow 对应一个 yaml 文件,所以,yaml 文件的内容应该像下面这样:
name: Deploy to Server # 随便起一个名字就可以了,显示在 GitHub Actions 中
on:
push:
branches: [ "main" ] # main 分支被 push 时触发
pull_request:
branches: [ "main" ] # main 分支有 PR 被 merge 时触发
jobs:
build: # 构建代码。可以任意起名
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3 # 将代码 pull 到容器内
- name: Setup .NET # 安装 .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x # 此处取决于项目的 .NET 版本
- name: Restore dependencies # 下载依赖文件
run: dotnet restore
- name: Build project # 构建项目
run: dotnet build --no-restore
- name: Run unit tests # 运行单元测试
run: dotnet test --no-build --verbosity normal
- name: Publish project demo # 将 Demo 环境的代码打包
run: dotnet publish MyAPI/MyAPI.csproj -c release -o release --nologo /p:EnvironmentName=Demo
- name: Upload a build artifact demo # 将打包好的 Demo 发布文件上传到 GitHub Artifact
uses: actions/upload-artifact@v3
with:
name: myapi-demo
path: release
- name: Publish project staging # 将 Staging 环境的代码打包
run: dotnet publish MyAPI/MyAPI.csproj -c release -o release --nologo /p:EnvironmentName=Staging
- name: Upload a build artifact staging # 将打包好的 Staging 发布文件上传到 GitHub Artifact
uses: actions/upload-artifact@v3
with:
name: myapi-staging
path: release
deploy-demo: # 部署 Demo 环境代码。可以任意起名
needs: build # 需要在上面的 build 执行完成后再执行
runs-on: self-hosted # 运行在安装在服务器的 runner 上
steps:
- name: Take application offline # 停止 IIS 服务
run: New-Item -Type File -Name app_offline.htm -Path C:/Users/Administrator/Desktop/GitHub-Actions/MyAPI.Demo -Force
- name: Download new binaries over the top of the app # 将 Artifact 下载到指定文件夹
uses: actions/download-artifact@v3
with:
name: myapi-demo
path: C:/Users/Administrator/Desktop/GitHub-Actions/MyAPI.Demo
- name: Bring the app back online # 重新启动 IIS 服务
run: remove-item C:/Users/Administrator/Desktop/GitHub-Actions/MyAPI.Demo/app_offline.htm
deploy-staging: # 部署 Staging 环境代码。可以任意起名
needs: deploy-demo # 需要在上面的 deploy-demo 执行完成后再执行
runs-on: self-hosted # 运行在安装在服务器的 runner 上
steps:
- name: Take application offline # 停止 IIS 服务
run: New-Item -Type File -Name app_offline.htm -Path C:/Users/Administrator/Desktop/GitHub-Actions/MyAPI.Staging -Force
- name: Download new binaries over the top of the app # 将 Artifact 下载到指定文件夹
uses: actions/download-artifact@v3
with:
name: myapi-staging
path: C:/Users/Administrator/Desktop/GitHub-Actions/MyAPI.Staging
- name: Bring the app back online # 重新启动 IIS 服务
run: remove-item C:/Users/Administrator/Desktop/GitHub-Actions/MyAPI.Staging/app_offline.htm
至此,一旦这个文件出现在 GitHub 的 main 分支上,这条 workflow 就被自动添加到 Actions 中了,同时会开始第一次运行。
遗留问题
目前这套流程还有一些遗留问题:
- 没有办法给某个步骤添加审批,比如部署到 staging 需要 block 住等待某人 approve。所以现在会一口气跑完整个 workflow。这个可能是 GitHub Orginization 的功能,需要付费。
- runner 这个东西在每次运行我们的 deployment 之前,一旦 runner 有新版本,它会自动升级自己,但是它的升级速度实在太慢(至少在中国大陆是这样的)。目前还不知道怎么屏蔽自动升级,或提高升级速度。
安装好的可执行文件是一个勘误请见 2023年5月21日更新*.cmd
文件,无法直接安装为系统服务,编写成*.bat
文件的话安装会报错。所以目前还得手动运行,并保持一个 cmd 窗口。- 好像没法指定跑哪个 branch,只能跑 main branch。
结语
大概就是这个样子啦。如果有什么缺少我会回来更新文档。如果有什么更好的办法欢迎补充~!
2023年1月18日更新
突然想起来一个点,为了部署不同的分支到环境,可以曲线救国:新建一个分支,专门把这个分支上的代码发布到服务器上。所以在 yaml 文件中,on
下面的 branches
就是新建的这个分支。当然了,只要 push
event 就可以了。
同时,也可以将发布到不同的环境拆分成不同的 actions。
所以此时,我建了两个 yaml 文件,分别叫 deploy-to-demo.yml
和 deploy-to-staging.yml
。代码分别如下:
# file name: deploy-to-demo.yml
name: Deploy to Demo
on:
push:
branches: [ "deploy-to-demo" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build project
run: dotnet build --no-restore
- name: Run unit tests
run: dotnet test --no-build --verbosity normal
- name: Publish project
run: dotnet publish My.API/My.API.csproj -c release -o release --nologo /p:EnvironmentName=Demo
- name: Upload a build artifact
uses: actions/upload-artifact@v3
with:
name: my.api-demo
path: release
deploy:
needs: build
runs-on: self-hosted
steps:
- name: Take application offline
run: New-Item -Type File -Name app_offline.htm -Path C:/Users/Administrator/Desktop/GitHub-Actions/My.API.Demo -Force
- name: Download new binaries over the top of the app
uses: actions/download-artifact@v3
with:
name: my.api-demo
path: C:/Users/Administrator/Desktop/GitHub-Actions/My.API.Demo
- name: Bring the app back online
run: remove-item C:/Users/Administrator/Desktop/GitHub-Actions/My.API.Demo/app_offline.htm
# file name: deploy-to-staging.yml
name: Deploy to Staging
on:
push:
branches: [ "deploy-to-staging" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build project
run: dotnet build --no-restore
- name: Run unit tests
run: dotnet test --no-build --verbosity normal
- name: Publish project
run: dotnet publish My.API/My.API.csproj -c release -o release --nologo /p:EnvironmentName=Staging
- name: Upload a build artifact
uses: actions/upload-artifact@v3
with:
name: my.api-staging
path: release
deploy:
needs: build
runs-on: self-hosted
steps:
- name: Take application offline
run: New-Item -Type File -Name app_offline.htm -Path C:/Users/Administrator/Desktop/GitHub-Actions/My.API.Staging -Force
- name: Download new binaries over the top of the app
uses: actions/download-artifact@v3
with:
name: my.api-staging
path: C:/Users/Administrator/Desktop/GitHub-Actions/My.API.Staging
- name: Bring the app back online
run: remove-item C:/Users/Administrator/Desktop/GitHub-Actions/My.API.Staging/app_offline.htm
2023年5月21日更新
对于前面遗留问题里提到的必须运行一个 console 这个问题,是我当时搞错了。
Runner 在 console 里安装的时候有个步骤,会问你是否要将这个 Runner 安装为服务。输入 y 就是了。