菜鸟-创作你的创作

[准备搞=未完成].net core 手撸一个.net core 持续集成/持续部署 (手撸 ci/cd)

太好了——你准备“手撸一个 .NET Core(.NET)CI/CD”(从编译、测试、打包到部署)——下面我给你一份开箱即可用的实战方案:包含方案选择、版本/兼容性注意、推荐工具、完整的 GitHub Actions 工作流(YAML)、Dockerfile、部署脚本、以及可直接拿去改的 Jenkinsfile 示例。所有关键点我都标注了来源,方便你验证与后续扩展。

先行提示:如果你目标是长期维护,优先以 LTS .NET 版本 + 容器化部署 + GitHub Actions(或 Jenkins 自托管) 为主线;下面以 GitHub Actions + Docker 部署到自托管 Linux 服务器为主示例(你也可以替换为 Azure App Service / AKS / ECR 等)。关于 .NET 当前 LTS/版本信息请以官方页面为准。(Microsoft)


1)高层架构(快速看图)

  1. 开发者 push → GitHub repo
  2. GitHub Actions CI:restore → build → test → publish artifact / build Docker image → push registry(Docker Hub / ACR)
  3. CD:在成功推镜像后,Actions 通过 SSH 在目标服务器上 pull 镜像并重启容器(或触发 k8s rollout)
    可选:使用 Jenkins 自托管 + Webhook 触发(更适合复杂私有 infra)。(Jenkins)

2)选型建议(为什么这样选)


3)必须先做的准备(环境)

  1. csproj 根 repo(或 monorepo)中保证 dotnet test 可运行并通过。
  2. 创建 Docker Hub / 私有镜像仓库凭据(或 Azure Container Registry)。
  3. 在目标服务器(Linux)上安装 Docker(以及 docker-compose 可选)。
  4. 在 GitHub 仓库设置 Secrets:DOCKERHUB_USERNAME, DOCKERHUB_TOKEN, SSH_HOST, SSH_USER, SSH_PRIVATE_KEY(私钥),CONTAINER_NAME 等。

4)关键文件:多阶段 Dockerfile(推荐 .NET 8/10 LTS,根据你项目改)

把这个放到 Web 项目根目录(例如 src/MyApp/)内作为 Dockerfile

# syntax=docker/dockerfile:1
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "./"]
RUN dotnet restore "MyApp.csproj"
COPY . .
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish

# Runtime stage (smaller)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
ENV DOTNET_URLS=http://+:80
COPY --from=build /app/publish .
EXPOSE 80
ENTRYPOINT ["dotnet", "MyApp.dll"]

如果你选择 self-contained 或不同 .NET 版本,把镜像 tag 换成相应版本(参考官方下载页)。(Microsoft)


5)GitHub Actions 工作流(.github/workflows/ci-cd.yml

下面示例会:checkout → setup-dotnet → restore → build → test → publish Docker image → push 到 Docker Hub → SSH 到目标服务器拉镜像并重启容器(使用 appleboy/ssh-action)。

name: CI-CD .NET Docker

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  IMAGE_NAME: myorg/myapp

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'     # or 10.0.x LTS if you choose .NET 10
      - name: Restore
        run: dotnet restore ./src/MyApp/MyApp.csproj

      - name: Build
        run: dotnet build ./src/MyApp/MyApp.csproj --configuration Release --no-restore

      - name: Test
        run: dotnet test ./tests/MyApp.Tests/MyApp.Tests.csproj --no-build --verbosity normal

      - name: Publish
        run: dotnet publish ./src/MyApp/MyApp.csproj -c Release -o ./publish

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push Docker image
        run: |
          IMAGE=${{ env.IMAGE_NAME }}:${{ github.sha }}
          docker build -t $IMAGE ./src/MyApp
          docker push $IMAGE
          # Also update "latest" tag
          docker tag $IMAGE ${{ env.IMAGE_NAME }}:latest
          docker push ${{ env.IMAGE_NAME }}:latest

  deploy:
    needs: build-and-test
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to server via SSH - pull & restart
        uses: appleboy/ssh-action@v0.1.7
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          port: 22
          script: |
            docker pull ${{ env.IMAGE_NAME }}:${{ github.sha }}
            docker stop ${{ secrets.CONTAINER_NAME }} || true
            docker rm ${{ secrets.CONTAINER_NAME }} || true
            docker run -d --name ${{ secrets.CONTAINER_NAME }} -p 80:80 ${{ env.IMAGE_NAME }}:${{ github.sha }}


6)目标服务器上的部署脚本(可选:deploy.sh 放到服务器)

如果你不想用 SSH action 的 inline 脚本,可以在 server 上维护:

#!/bin/bash
IMAGE=$1
CONTAINER=myapp

docker pull $IMAGE
docker stop $CONTAINER || true
docker rm $CONTAINER || true
docker run -d --name $CONTAINER -p 80:80 $IMAGE

在 Actions 中只需 ssh 并调用 ./deploy.sh myorg/myapp:sha.


7)Jenkinsfile(简洁版)—— 如果你想自托管 Jenkins

把 Jenkins agent 配置好(带 Docker),然后在 repo 里放 Jenkinsfile

pipeline {
  agent any
  stages {
    stage('Checkout') {
      steps { checkout scm }
    }
    stage('Build') {
      steps {
        sh 'dotnet restore src/MyApp/MyApp.csproj'
        sh 'dotnet build src/MyApp/MyApp.csproj -c Release --no-restore'
      }
    }
    stage('Test') {
      steps {
        sh 'dotnet test tests/MyApp.Tests/MyApp.Tests.csproj --no-build'
      }
    }
    stage('Docker Build & Push') {
      steps {
        withCredentials([usernamePassword(credentialsId: 'dockerhub', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) {
          sh '''
            docker login -u $DOCKER_USER -p $DOCKER_PASS
            docker build -t myorg/myapp:${GIT_COMMIT} src/MyApp
            docker push myorg/myapp:${GIT_COMMIT}
          '''
        }
      }
    }
    stage('Deploy') {
      steps {
        // Run ssh to server and pull+restart
        sh "ssh ubuntu@your.server 'docker pull myorg/myapp:${GIT_COMMIT} && ./deploy.sh myorg/myapp:${GIT_COMMIT}'"
      }
    }
  }
}

Jenkins 文档有更多 .NET 支持、插件、report 集成示例。(Jenkins)


8)测试报告 / 代码覆盖 / 安全扫描(可选增强)


9)实操小贴士(经验)


10)快速清单(你可以照着做)

  1. 在 repo 建 Dockerfiledockerignorepublish 脚本。
  2. 配置 GitHub Secrets(DOCKERHUB_, SSH_)。
  3. 把上面的 ci-cd.yml.github/workflows/ 并 push 到 main
  4. 在目标 Linux server 上准备 docker、deploy.sh、开放端口(80/443)。
  5. push commit,观察 Actions(build → push → deploy)是否通过;若失败,先修 CI(单元测试/restore 出错最常见)。
退出移动版