Thiết lập CI/CD cho ASP.NET Core trên IIS với GitHub Actions: Build, Test, Deploy và Quản lý Secrets
Ở nhiều dự án ASP.NET Core chạy trên IIS, quy trình deploy thường bắt đầu khá đơn giản:
- Publish từ Visual Studio
- Remote Desktop vào server
- Copy đè file
- Restart IIS
Cách làm này ổn khi dự án còn nhỏ. Tuy nhiên khi số lần release tăng lên, việc deploy thủ công bắt đầu xuất hiện nhiều vấn đề:
- Dễ quên bước
- Khó rollback
- Khó truy vết lỗi
- Mất thời gian khi release nhiều lần trong ngày
Trong bài viết này, mình sẽ cấu hình một pipeline CI/CD bằng GitHub Actions cho ASP.NET Core chạy trên IIS với ba giai đoạn riêng biệt:
flowchart TD
A[Build] --> B[Test]
B --> C[Deploy]
Pipeline sẽ tự động:
- Build source code
- Chạy Unit Test
- Publish artifact
- Thay thế Connection String bằng GitHub Secrets
- Deploy lên IIS thông qua MSDeploy
Kiến trúc Pipeline
Thay vì đặt toàn bộ logic vào một job duy nhất, mình tách pipeline thành ba job:
| Job | Mục đích |
|---|---|
| Build | Restore, Build và Publish |
| Test | Chạy Unit Test |
| Deploy | Deploy lên IIS |
Luồng hoạt động:
flowchart TD
A[Push hoặc Merge vào main]
B[Build]
C[Test]
D[Deploy]
E[IIS Server]
A --> B
B --> C
C --> D
D --> E
Deploy chỉ được thực hiện khi Build và Test đều thành công.
Bước 1: Khai báo GitHub Secrets
Thông tin nhạy cảm không nên xuất hiện trong repository:
- Database Connection String
- MSDeploy Username
- MSDeploy Password
Trong GitHub Repository:
flowchart TD
A[Repository]
B[Settings]
C[Secrets and variables]
D[Actions]
A --> B
B --> C
C --> D
Tạo các secret:
DB_DEFAULT_CONNECTION
DB_HANGFIRE_CONNECTION
MSDEPLOY_SITE
MSDEPLOY_SERVER
MSDEPLOY_USERNAME
MSDEPLOY_PASSWORD
Lúc này repository có thể public hoặc chia sẻ cho team mà không làm lộ thông tin production.
Bước 2: Build Job
Build Job chịu trách nhiệm:
- Checkout source
- Restore NuGet packages
- Build solution
- Publish ứng dụng
- Upload artifact
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- run: dotnet restore VietnamStock.Api/VietnamStock.sln
- run: dotnet build VietnamStock.Api/VietnamStock.sln \
--configuration Release \
--no-restore
- run: dotnet publish VietnamStock.Api/VietnamStock/VietnamStock.csproj \
--configuration Release \
--output publish \
--no-build
- uses: actions/upload-artifact@v4
with:
name: api-publish-output
path: publish/
Sau bước này, GitHub sẽ lưu thư mục publish dưới dạng Artifact để các job phía sau có thể sử dụng.
Bước 3: Test Job
Mỗi GitHub Actions Job chạy trên một runner độc lập.
Điều đó có nghĩa là Build Job hoàn thành không đồng nghĩa Test Job sẽ có sẵn source code hoặc NuGet packages.
Vì vậy Test Job cần:
- Checkout lại source
- Restore lại dependency
- Chạy test
test:
runs-on: windows-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- run: dotnet restore VietnamStock.Api/VietnamStock.sln
- run: dotnet test VietnamStock.Api/VietnamStock.sln
Trong thực tế, nhiều dự án có Integration Test phụ thuộc:
- SQL Server local
- Redis
- RabbitMQ
Các test này thường không phù hợp để chạy trên GitHub Runner.
Khi đó nên gắn tag:
[Trait("Category", "Integration")]
và loại bỏ khỏi pipeline CI:
--filter "Category!=Integration"
Bước 4: Download Artifact để Deploy
Deploy Job không build lại source code.
Thay vào đó nó sử dụng đúng artifact đã được Build Job tạo ra.
- uses: actions/download-artifact@v4
with:
name: api-publish-output
path: publish
Điều này đảm bảo:
Những gì được deploy chính là những gì đã được build và test.
Bước 5: Thay thế Connection String bằng GitHub Secrets
Một cách làm phổ biến là tạo:
appsettings.Development.json
appsettings.Production.json
Tuy nhiên với các dự án deploy trực tiếp bằng MSDeploy, mình thường sử dụng Variable Substitution.
Ví dụ file publish:
{
"ConnectionStrings": {
"DefaultConnection": "",
"HangfireConnection": ""
}
}
Sử dụng action:
- uses: microsoft/variable-substitution@v1
và map secret:
env:
ConnectionStrings.DefaultConnection: ${{ secrets.DB_DEFAULT_CONNECTION }}
ConnectionStrings.HangfireConnection: ${{ secrets.DB_HANGFIRE_CONNECTION }}
Action sẽ đọc file JSON và ghi đè giá trị tương ứng trước khi deploy.
Bước 6: Deploy lên IIS bằng MSDeploy
Sau khi cập nhật cấu hình, pipeline sử dụng MSDeploy để đồng bộ thư mục publish lên IIS.
-verb:sync
-source:contentPath=...
-dest:contentPath=...
MSDeploy sẽ chỉ copy những file thay đổi thay vì upload lại toàn bộ ứng dụng.
Một tham số quan trọng:
-enableRule:AppOffline
Khi deploy, MSDeploy sẽ tạo:
app_offline.htm
IIS tạm dừng ứng dụng để giải phóng các file DLL đang bị lock.
Nhờ đó tránh được lỗi:
The process cannot access the file because it is being used by another process
Kết luận
Sau khi hoàn thành pipeline này, mỗi lần merge code vào nhánh main, GitHub Actions sẽ:
- Build ứng dụng
- Chạy test
- Publish artifact
- Thay thế cấu hình bằng GitHub Secrets
- Deploy lên IIS
Toàn bộ quy trình phát hành được tự động hóa và có thể theo dõi trực tiếp trên GitHub Actions.
Nếu dự án của bạn vẫn đang deploy bằng cách Publish từ Visual Studio rồi copy file qua Remote Desktop, đây là một bước nâng cấp tương đối dễ triển khai nhưng mang lại hiệu quả rất lớn cho quy trình release.
Nhận xét
Đăng nhận xét