使用 GitHub Actions 实现简易 CI/CD
Source: Dev.to

Container Ship by Ian Taylor
作为一家小公司的软件工程师,IT 常被视为成本中心,我尽量保持部署流程简单:小体积的 Docker 镜像、安全的步骤,并且理想情况下零成本。
最近的一个 YouTube 视频展示了即使是私有仓库也可以被克隆,这意味着如果我们不小心,.env、yml 或 Docker Compose 文件中的机密信息可能会泄露。这促使我重新审视自己的(简易)CI/CD 设置。下面是我用来将 .NET 应用部署到本地服务器的轻量化工作流。它仅作参考;你可能需要根据自己的环境调整路径、环境变量或服务名称。
免责声明
此 CI/CD 配置仅基于我的部署环境提供参考。服务器配置可能各不相同,你可能需要调整目录路径、环境变量或服务名称才能在自己的环境中正常工作。
本地开发(非常简单)
在 appsettings.json(或其他配置文件)中添加必要的配置,然后像往常一样本地运行应用。
为部署准备 Docker
Docker 设置
为本地和服务器部署创建多阶段 Dockerfile。下面的示例在 Alpine 上构建一个自包含的 .NET Web API 镜像,将 DOTNET_SYSTEM_GLOBALIZATION_INVARIANT 设置为 false(时区设置所需),并创建一个非 root 用户。
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS publish
WORKDIR /src
COPY your_project.csproj ./
RUN dotnet restore "./your_project.csproj" --runtime linux-musl-x64
COPY . .
RUN dotnet publish "your_project.csproj" \
-c Release \
-o /app/publish \
--no-restore \
--runtime linux-musl-x64 \
--self-contained true \
/p:PublishSingleFile=true
FROM mcr.microsoft.com/dotnet/runtime-deps:9.0-alpine AS final
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
RUN apk add --no-cache icu-libs
ENV LD_LIBRARY_PATH=/usr/lib
RUN apk upgrade musl
RUN adduser --disabled-password \
--home /app \
--gecos '' dotnetuser && chown -R dotnetuser /app
USER dotnetuser
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["./your_project"]
为本地开发创建 Docker Compose 文件,将所有环境变量放在 environment 部分。
version: '3.8'
services:
project:
build:
context: .
dockerfile: Dockerfile
container_name: project_name
ports:
- hardware_port1:container_port1
- hardware_port2:container_port2
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:container_port1;http://+:container_port2
- MYSQL_CONNECTION=server=localhost;port=3306;userid=root;password=your_password;database=your_database
restart: always
networks:
- project_name
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
project_name:
name: project_name_network
driver: bridge
运行组合:
docker compose -f docker-compose.Development.yml up -d --build
如果运行成功,将开发用的 Docker Compose 文件和 appsettings.json 加入 .gitignore,防止它们被推送,同时将 appsettings.json 加入 .dockerignore(因为所有环境变量已经在 Compose 文件中)。
通过 GitHub Actions 部署到服务器
GitHub Action 设置
- 在仓库中,进入 Settings → Security → Secrets and Variables → Actions → New repository secret。
- 将本地 Docker Compose 文件中的每个环境变量添加为 secret,保持相同的名称(例如
ASPNETCORE_ENVIRONMENT)。 - 将 SSH 凭证添加为 secret:
SSH_HOST、SSH_USERNAME和SSH_PRIVATE_KEYS。
创建一个针对服务器的 Docker Compose 文件,引用这些 secret:
version: '3.8'
services:
project:
build:
context: .
dockerfile: Dockerfile
container_name: project_name
ports:
- hardware_port1:container_port1
- hardware_port2:container_port2
environment:
- ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT}
- ASPNETCORE_URLS=${ASPNETCORE_URLS}
- MYSQL_CONNECTION=${MYSQL_CONNECTION}
networks:
- project_name
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
project_name:
name: project_name_network
driver: bridge
添加工作流文件(例如 .github/workflows/deploy.yml),该文件负责构建镜像、将镜像和 Compose 文件传输到服务器并执行部署。
name: Build and Deploy
on:
push:
branches: [ master ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
run: |
docker build -t your_project:latest .
docker save your_project:latest -o your_project.tar
- name: Create .env file from secrets
run: |
cat > .env << EOF
ASPNETCORE_ENVIRONMENT=${{ secrets.ASPNETCORE_ENVIRONMENT }}
ASPNETCORE_URLS=${{ secrets.ASPNETCORE_URLS }}
MYSQL_CONNECTION=${{ secrets.MYSQL_CONNECTION }}
EOF
- name: Copy files to server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEYS }}
source: "your_project.tar,docker-compose.yml,.env"
target: "/tmp/deploy_your_project"
- name: Deploy with Docker Compose
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_PRIVATE_KEYS }}
script: |
# Create app directory if it doesn't exist
mkdir -p ~/apps/your_project
# Move uploaded files into project directory
mv /tmp/deploy_your_project/docker-compose.yml ~/apps/your_project/
mv /tmp/deploy_your_project/.env ~/apps/your_project/
# Load Docker image
docker load -i /tmp/deploy_your_project/your_project.tar
# (optional) Move tar file into project directory
mv /tmp/deploy_your_project/your_project.tar ~/apps/your_project/
# Navigate to the app directory and start the services
cd ~/apps/your_project
docker compose up -d --remove-orphans