實戰筆記:搬瓦工 Docker 部署 .NET 8 與 BWH API 自動監控流水線
更多語言
更多操作
最近社區裡有不少帖子在討論各種面板、測速,或者教大家寫 Bash 腳本查 VPS 流量。
其實對於開發者來說,買一台網絡穩的機器(比如搬瓦工的 GIA),最大的用途還是跑自己的後端服務。但這年頭,如果每次改完代碼還要手動 FTP 傳文件、在宿主機折騰各種運行時環境,不僅容易把系統弄髒,維護起來也挺繁瑣。
今天分享一套我日常在用的 DevOps 工作流:在 Linux 下用 Docker Compose 隔離運行 .NET 8 服務,搭配 GitHub Actions 做全自動 CI/CD。順便用 C# 對接一下搬瓦工官方的 KiwiVM API,寫個 Telegram 監控端。廢話不多說,直接開始。
01. 代碼實現:C# 調用 KiwiVM API 與 TG 機器人
相比傳統的 Bash 腳本定時任務,用 C# 的 BackgroundService 做長連接監控更穩定。另外,很多人用腳本查 API 經常會算錯流量,主要是沒留意官方文檔里的兩個細節:搬瓦工的高端機房流量是有 monthly_data_multiplier(流量乘數)的,而且 API 返回的日期是 UNIX 時間戳。
我們在 .NET 8 Worker 項目中引入 Telegram.Bot,順手把這兩個容易踩坑的地方處理掉:
using Telegram.Bot;
using Telegram.Bot.Types;
using System.Net.Http.Json;
public class BwhMonitorWorker : BackgroundService
{
private readonly TelegramBotClient _botClient = new("你的_TG_BOT_TOKEN");
private readonly HttpClient _http = new();
private const string API_HOST = "https://" + "api.64clouds.com";
private const string VEID = "你的VEID";
private const string API_KEY = "你的API_KEY";
private const long ADMIN_CHAT_ID = 123456789; // 你的 TG 账号 ID
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_botClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, null, stoppingToken);
while (!stoppingToken.IsCancellationRequested) await Task.Delay(1000, stoppingToken);
}
private async Task HandleUpdateAsync(ITelegramBotClient bot, Update update, CancellationToken ct)
{
if (update.Message?.Text == null || update.Message.Chat.Id != ADMIN_CHAT_ID) return;
var text = update.Message.Text;
var chatId = update.Message.Chat.Id;
if (text == "/status")
{
var url = $"{API_HOST}/v1/getServiceInfo?veid={VEID}&api_key={API_KEY}";
var res = await _http.GetFromJsonAsync<BwhInfo>(url, ct);
if (res.error == 0)
{
// 细节 1:计算时必须带上官方规定的流量乘数 (monthly_data_multiplier)
double multiplier = res.monthly_data_multiplier > 0 ? res.monthly_data_multiplier : 1;
double usedGb = (res.data_counter * multiplier) / 1024.0 / 1024.0 / 1024.0;
double limitGb = (res.plan_monthly_data * multiplier) / 1024.0 / 1024.0 / 1024.0;
// 细节 2:UNIX 时间戳转换为本地直观时间
DateTime resetDate = DateTimeOffset.FromUnixTimeSeconds(res.data_next_reset).LocalDateTime;
await bot.SendTextMessageAsync(chatId,
$"🖥️ 主机名: {res.hostname}\n" +
$"📍 节点: {res.node_location}\n" +
$"📶 流量: {usedGb:F2}GB / {limitGb:F2}GB\n" +
$"📅 重置: {resetDate:yyyy-MM-dd HH:mm:ss}",
cancellationToken: ct);
}
}
else if (text == "/reboot")
{
var url = $"{API_HOST}/v1/restart?veid={VEID}&api_key={API_KEY}";
var res = await _http.GetFromJsonAsync<BwhInfo>(url, ct);
if(res.error == 0) {
await bot.SendTextMessageAsync(chatId, "🔄 硬件重启指令已发送,请稍候...", cancellationToken: ct);
}
}
}
private Task HandleErrorAsync(ITelegramBotClient bot, Exception ex, CancellationToken ct) => Task.CompletedTask;
}
// 对应官方文档的 JSON 映射类
public class BwhInfo {
public int error { get; set; }
public string hostname { get; set; }
public string node_location { get; set; }
public long data_counter { get; set; }
public long plan_monthly_data { get; set; }
public double monthly_data_multiplier { get; set; }
public long data_next_reset { get; set; }
}
02. Docker 容器化:告別環境污染
我們不需要在宿主機上裝任何 .NET SDK 或運行時,直接把程序打包進 Docker。這樣不僅系統乾淨,而且自帶進程守護(掛了自動拉起),省去了配置 Systemd 的麻煩。
在代碼根目錄下新建這兩個文件:
1. Dockerfile(極簡運行時鏡像):
# 使用微软官方轻量级 ASP.NET 8 运行时镜像
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
# 拷贝编译好的文件到容器内
COPY . .
# 启动程序 (替换为你的 DLL 名字)
ENTRYPOINT ["dotnet", "MyBwhBot.dll"]
2. docker-compose.yml:
services:
bwh-bot:
build: .
container_name: bwh-telegram-bot
restart: always # 容器挂了自动重启
environment:
- TZ=Asia/Shanghai
03. GitHub Actions 全自動化 CI/CD
服務和容器配置都準備好了,接下來的痛點是怎麼自動化部署。這裡我們直接用 GitHub Actions 搞定。
只要你往 main 分支推代碼,GitHub 就會自動走完這個流程:裝 .NET SDK -> 編譯代碼 -> 打包連同 Docker 文件一起 SCP 傳到伺服器 -> 最後通過 SSH 觸發 docker compose up 構建並重啟。
在代碼倉庫新建 .github/workflows/deploy.yml 文件:
name: Docker Deploy to BWH
on:
push:
branches: [ "main" ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 配置 .NET 8 编译环境
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: 编译发布
run: dotnet publish -c Release -o ./publish_out
- name: 准备 Docker 文件
run: |
cp Dockerfile ./publish_out/
cp docker-compose.yml ./publish_out/
- name: SCP 传输文件到服务器
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.BWH_IP }} # 你的 VPS IP
username: root
key: ${{ secrets.SSH_PRIVATE_KEY }} # 私钥 (在 GitHub 后台配置)
source: "./publish_out/*"
target: "/opt/bwh-bot"
strip_components: 1
- name: SSH 触发 Docker 重新构建
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.BWH_IP }}
username: root
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/bwh-bot
docker compose down
docker compose up -d --build
docker image prune -f # 清理旧的无用镜像
提示:記得在 GitHub 倉庫的
Settings -> Secrets and variables -> Actions里把BWH_IP和SSH_PRIVATE_KEY填好。
總結
配置好之後,以後每次在本地更新完代碼只要 push 一下,雲端的服務就會自動完成重建和替換。
這套流程其實對 VPS 的網絡連通性有一定要求。如果伺服器網絡比較差,GitHub Action 在 SCP 傳文件或者 SSH 連接時偶爾會超時報錯。平時部署後端服務,建議儘量選網絡穩一點的機房(類似 搬瓦工 CN2 GIA 高端線這種),文件傳輸基本秒達,CI/CD 流水線跑起來會順暢很多。