[DevOps]使用github-action工具部署docker容器(实现提交代码一键推送部署到服务器)

前提:云服务器、docker 账号、GitHub 账号

CI/CD 主要有三种实现方式:

  • 直接命令行部署:通过脚本直接部署到服务器
  • 服务器工具部署:使用阿里云等服务器提供的部署工具
  • GitHub Actions 自动化部署:基于代码提交自动触发部署

1. 命令行直接部署

项目结构

  • env 文件: 存放服务器端口、账号等配置信息
  • deploy 脚本: 编写主要部署逻辑
  • docker 文件: Docker 容器配置
  • nginx 配置: Web 服务器配置文件

部署脚本示例

Docker Compose 配置文件

docker-compose.yml

yaml 复制代码
version: "3"
services:
  mainpage:
    image: nginx:stable-alpine
    container_name: mainpage-nginx
    restart: always
    ports:
      - "80:80"
    volumes:
      # Nginx 配置
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      # 0MainPage 构建产物
      - /root/project/0MainPage/dist:/usr/share/nginx/html:ro
Nginx 配置文件

nginx.conf

nginx 复制代码
server {
    listen       80;
    server_name  localhost;

    # MIME 类型配置
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    # 安全头配置
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # 开启 gzip 压缩
    gzip on;
    gzip_min_length 1k;
    gzip_comp_level 6;
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json;
    gzip_vary on;
    gzip_disable "MSIE [1-6]\.";

    # 静态资源缓存配置 - JS文件
    location ~* \.js$ {
        root /usr/share/nginx/html;
        add_header Content-Type "application/javascript; charset=utf-8";
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # 静态资源缓存配置 - CSS文件
    location ~* \.css$ {
        root /usr/share/nginx/html;
        add_header Content-Type "text/css; charset=utf-8";
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # 静态资源缓存配置 - 图片和字体文件
    location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        root /usr/share/nginx/html;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # HTML文件不缓存
    location ~* \.html$ {
        root /usr/share/nginx/html;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
    }

    # 0MainPage - 主页项目(根路径)
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
PowerShell 部署脚本

deploy.ps1

powershell 复制代码
# Deploy script for 0MainPage project
Write-Host "[Deploy] Starting 0MainPage deployment process..." -ForegroundColor Green

# Load environment variables from .env file

$envFile = Join-Path $PSScriptRoot ".env-0mainpage"
if (Test-Path $envFile) {
    Write-Host "[Config] Loading environment variables from .env file..." -ForegroundColor Yellow
    Get-Content $envFile | ForEach-Object {
        if ($_.Trim() -match "^([^#][^=]+)=(.*)$") {
            $name = $matches[1].Trim()
            $value = $matches[2].Trim()
            [Environment]::SetEnvironmentVariable($name, $value, "Process")
        }
    }
} else {
    Write-Host "[Warning] .env file not found, using default values" -ForegroundColor Yellow
}

# Server configuration from environment variables

$serverHost = if ($env:SERVER_HOST) { $env:SERVER_HOST } else { "" }
$serverUser = if ($env:SERVER_USER) { $env:SERVER_USER } else { "" }
$serverPassword = $env:SERVER_PASSWORD
$serverPath = if ($env:SERVER_0MAINPAGE_PATH) { $env:SERVER_0MAINPAGE_PATH } else { "/root/project/0MainPage/dist" }
$serverConfigPath = if ($env:SERVER_CONFIG_PATH) { $env:SERVER_CONFIG_PATH } else { "/root/project/CD/0MainPage" }
$projectPath = if ($env:LOCAL_0MAINPAGE_PATH) { $env:LOCAL_0MAINPAGE_PATH } else { "C:\Users\what\Desktop\my-project\0MainPage" }

if (-not $serverPassword) {
    Write-Host "[Error] SERVER_PASSWORD not found in environment variables" -ForegroundColor Red
    exit 1
}

Write-Host "[Config] Server: $serverHost" -ForegroundColor Yellow
Write-Host "[Config] Target: 0MainPage (Port 80)" -ForegroundColor Yellow
Write-Host "[Config] Project Path: $projectPath" -ForegroundColor Yellow

# Check and navigate to 0MainPage

if (!(Test-Path $projectPath)) {
    Write-Host "[Error] 0MainPage directory not found at: $projectPath" -ForegroundColor Red
    exit 1
}

$currentPath = Get-Location
Set-Location $projectPath
Write-Host "[Info] Changed to project directory: $projectPath" -ForegroundColor Yellow

try { # 1. Install dependencies if needed
    if (!(Test-Path "node_modules")) {
        Write-Host "[Step1] Installing dependencies..." -ForegroundColor Cyan
        npm install
        if ($LASTEXITCODE -ne 0) {
            throw "Dependencies installation failed with exit code $LASTEXITCODE"
        }
    }

    # 2. Build project
    Write-Host "[Step2] Building 0MainPage project..." -ForegroundColor Cyan
    npm run build
    if ($LASTEXITCODE -ne 0) {
        throw "Build command failed with exit code $LASTEXITCODE"
    }

    if (!(Test-Path "dist")) {
        Write-Host "[Error] Build failed, dist directory not found" -ForegroundColor Red
        exit 1
    }

    # 3. Create server directory if not exists
    Write-Host "[Step3] Preparing server directory..." -ForegroundColor Cyan
    $plinkExists = Get-Command plink -ErrorAction SilentlyContinue
    if ($plinkExists) {
        try {
            $plinkCommand = "echo y | plink -pw '$serverPassword' ${serverUser}@${serverHost} 'mkdir -p ${serverPath} && mkdir -p ${serverConfigPath}'"
            Write-Host "[Execute] Creating directories on server..." -ForegroundColor Gray
            Invoke-Expression $plinkCommand
        } catch {
            Write-Host "[Warning] Directory creation failed: $_" -ForegroundColor Yellow
        }
    }

    # 4. Upload dist to server
    Write-Host "[Step4] Uploading 0MainPage dist to server..." -ForegroundColor Cyan
    $pscpExists = Get-Command pscp -ErrorAction SilentlyContinue
    if ($pscpExists) {
        try {
            $pscpCommand = "echo y | pscp -pw '$serverPassword' -r dist/* ${serverUser}@${serverHost}:${serverPath}/"
            Write-Host "[Execute] Uploading files..." -ForegroundColor Gray
            Invoke-Expression $pscpCommand
        } catch {
            throw "File transfer failed: $_"
        }
    }

    # 5. Upload config files
    Write-Host "[Step5] Uploading 0MainPage config files..." -ForegroundColor Cyan
    $configPath = $PSScriptRoot
    if (Test-Path $configPath) {
        if ($pscpExists) {
            $pscpConfigCommand = "pscp -pw '$serverPassword' ${configPath}/docker-compose.yml ${configPath}/nginx.conf ${serverUser}@${serverHost}:${serverConfigPath}/"
            Write-Host "[Execute] Uploading config files..." -ForegroundColor Gray
            Invoke-Expression $pscpConfigCommand
        }
    }

    # 6. Stop existing container
    Write-Host "[Step6] Stopping existing 0MainPage container..." -ForegroundColor Cyan
    try {
        if ($plinkExists) {
            $plinkCommand = "plink -batch -pw '$serverPassword' ${serverUser}@${serverHost} 'cd ${serverConfigPath} && docker stop mainpage-nginx || true && docker rm mainpage-nginx || true'"
            Write-Host "[Execute] Stopping existing container..." -ForegroundColor Gray
            Invoke-Expression $plinkCommand
        }
    } catch {
        Write-Host "[Info] No existing container to stop" -ForegroundColor Yellow
    }

    # 7. Start 0MainPage container
    Write-Host "[Step7] Starting 0MainPage container..." -ForegroundColor Cyan
    try {
        if ($plinkExists) {
            $plinkCommand = "plink -batch -pw '$serverPassword' ${serverUser}@${serverHost} 'cd ${serverConfigPath} && docker compose up -d'"
            Write-Host "[Execute] Starting container..." -ForegroundColor Gray
            Invoke-Expression $plinkCommand
        }
    } catch {
        Write-Host "[Error] Container start failed: $_" -ForegroundColor Red
        throw
    }

    # 8. Verify deployment
    Write-Host "[Step8] Verifying 0MainPage deployment..." -ForegroundColor Cyan
    try {
        if ($plinkExists) {
            $plinkVerifyCommand = "plink -batch -pw '$serverPassword' ${serverUser}@${serverHost} 'docker ps | grep mainpage-nginx'"
            Write-Host "[Execute] Verifying container..." -ForegroundColor Gray
            Invoke-Expression $plinkVerifyCommand
        }
    } catch {
        Write-Host "[Warning] Verification failed: $_" -ForegroundColor Yellow
    }

    Write-Host "[Done] 0MainPage deployment successful!" -ForegroundColor Green
    Write-Host "[Access] http://$serverHost" -ForegroundColor Yellow

} catch {
    Write-Host "[Error] Deployment failed: $\_" -ForegroundColor Red
    exit 1
} finally {
    Set-Location $currentPath
}

2. 服务器工具部署

使用阿里云、腾讯云等云服务商提供的部署工具,具体配置根据服务商文档进行设置。

3. GitHub Actions 自动化部署(推荐)

提供两种部署模式:

  • 主分支自动部署:提交代码到主分支时自动触发部署
  • 标签部署:根据 Git Tag 来触发部署

Dockerfile 配置

dockerfile 复制代码
# 构建阶段
FROM node:20-alpine as build-stage

WORKDIR /app

# 复制 package 文件
COPY package*.json ./
COPY pnpm-lock.yaml ./

# 安装 pnpm 并安装依赖
RUN npm install -g pnpm
RUN pnpm install

# 复制源代码(排除 node_modules)
COPY src ./src
COPY public ./public
COPY index.html ./
COPY vite.config.ts ./
COPY tsconfig.json ./
COPY tsconfig.app.json ./
COPY tsconfig.node.json ./

# 构建应用
RUN pnpm run build

# 生产阶段
FROM nginx:stable-alpine as production-stage

# 复制自定义 nginx 配置
COPY nginx.conf /etc/nginx/conf.d/default.conf

# 复制构建产物
COPY --from=build-stage /app/dist /usr/share/nginx/html

# 暴露端口
EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

GitHub Actions 配置文件

yaml 复制代码
name: Publish Image

on:
  push:
    branches: ["master"]
    paths-ignore:
      - ".gitignore"
      - "README.md"
      - ".vscode/**"
  pull_request:
    branches: ["master"]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: actions/setup-node@v4
        with:
          node-version: "20"

      - uses: actions/cache@v4
        id: cache
        with:
          path: node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: npm install

      - name: Build
        run: npm run build

      - name: Build and push Docker image
        run: |
          docker build . --file Dockerfile --tag ghcr.io/${{ github.repository_owner }}/0mainpage:latest
          docker push ghcr.io/${{ github.repository_owner }}/0mainpage:latest

      - name: Deploy to server
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          password: ${{ secrets.SERVER_PASSWORD }}
          port: ${{ secrets.SERVER_PORT || '22' }}
          timeout: 30s
          command_timeout: 10m
          debug: true
          script: |
            docker stop mainpage-nginx || true
            docker rm mainpage-nginx || true
            docker login -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} https://ghcr.io
            docker pull ghcr.io/${{ github.repository_owner }}/0mainpage:latest
            docker run -dp 80:80 --restart=always --name mainpage-nginx ghcr.io/${{ github.repository_owner }}/0mainpage:latest

GitHub 仓库配置

需要在 GitHub 仓库的 Settings > Secrets and variables > Actions 中配置以下环境变量:

  • SERVER_HOST: 服务器地址
  • SERVER_USER: 服务器用户名
  • SERVER_PASSWORD: 服务器密码
  • SERVER_PORT: SSH 端口(默认 22)
相关推荐
70asunflower2 小时前
Docker exec 命令完全解析
linux·ubuntu·docker
摇滚侠2 小时前
css 设置边框
前端·css
星爷AG I2 小时前
9-24 视觉叙事(AGI基础理论)
前端·人工智能
云泽8082 小时前
深入浅出 Linux:Shell 运行机制与核心权限指令解析
linux·运维·服务器
2501_940007892 小时前
Flutter for OpenHarmony三国杀攻略App实战 - 鸿蒙适配与打包发布
前端·flutter
css趣多多2 小时前
跨域问题及Vue项目中本地/线上解决方法核心总结
前端
比奇堡派星星2 小时前
linux Zram
linux·运维·服务器
光影少年3 小时前
前端 AIGC
前端·aigc