0%

GitHub Actions+docker context CLI+Nginx部署前后端分离应用

背景

使用CI/CD及docker容器部署一个简单的web应用时,利用docker context可以简单快速地进行远程镜像部署,而无需任何镜像托管服务进行发布和拉取。本篇博文中的例子将以Nginx作为反向代理服务器。

思路

配置好生产服务器的SSH连接后,即可利用docker context进行远程容器部署。其中前端应用由于只是静态文件,在GitHub Action中编译后直接上传至远程服务器。
以下例子采用

  • 前端:Angular应用
  • 后端:.NET Core WebAPI
  • 反向代理:Nginx

编码

  1. Dockerfile编写

    • .NET Core服务端

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
      WORKDIR /app
      EXPOSE 80

      FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
      WORKDIR /src
      COPY ["My.Server.csproj", "My.Server/"]
      RUN dotnet restore "My.Server/My.Server.csproj"
      COPY . My.Server/
      WORKDIR "/src/My.Server"
      RUN dotnet build "My.Server.csproj" -c Release -o /app/build

      FROM build AS publish
      RUN dotnet publish "My.Server.csproj" -c Release -o /app/publish

      FROM base AS final
      WORKDIR /app
      COPY --from=publish /app/publish .
      ENTRYPOINT ["dotnet", "My.Server.dll"]
    • Angular应用

      1
      2
      3
      4
      5
      6
      7
      8
      FROM node:16 AS build
      WORKDIR /usr/local/app
      COPY . .
      RUN npm install && npm run build:prod

      FROM nginx:latest
      COPY --from=build /usr/local/app/dist/apps/myapp /usr/share/nginx/html
      EXPOSE 80
  2. docker-compose编写

  • 为了方便在服务运行时直接修改Nginx配置而不需要重启容器,博主在相应的service中进行了挂载配置,以让Nginx容器读取服务器本地的配置文件。同样对日志及Web静态文件进行挂载配置。需要在第一次部署前在远程服务器中创建好相应的目录。Nginx配置文件可以一并提前创建好,也可在源码中签入一份默认Nginx配置并在GitHub Actions中上传至远程机。
  • 由于Nginx和后端应用作为两个镜像部署,需要配置network以支持容器间通信。Nginx在宿主机中暴露80端口,后端应用在my_net网络中暴露80端口,可对Nginx依据这些地址进行相应配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
version: '3.7'

services:
nginx:
image: nginx:latest
container_name: my_nginx
volumes:
- /etc/nginx/conf.d/default.prod.conf:/etc/nginx/conf.d/default.conf:ro
- /var/log/nginx/prod:/var/log/nginx
- /usr/share/nginx/html/prod:/usr/share/nginx/html
networks:
my_net:
ipv4_address: 172.30.1.3
ports:
- "80:80"

my-backend:
image: 'my/service:latest'
container_name: my_service
build:
context: ./My.Server/My.Server
dockerfile: ./Dockerfile
environment: # 根据实际需要,在这里提供配置项以覆盖appsettings.json中的配置
- App__BaseUrl=http://my-server-domain-name/api
networks:
my_net:
ipv4_address: 172.30.1.8

networks:
my_net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.30.1.0/24
gateway: 172.30.1.1
  1. 最后的GitHub Actions文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    name: Build and Deploy

    # 依据实际情况配置触发条件,这里配置为手动触发模拟正式环境部署
    on: workflow_dispatch

    env:
    ANGULAR_ROOT_DIR: ./My.AngularUI

    jobs:
    build-and-deploy:
    name: Build and Deploy
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2.3.4

    - name: Use Node 16.x
    uses: actions/setup-node@v3.1.1
    with:
    node-version: '16.10.0'

    - name: Install dependencies
    run: npm ci
    working-directory: ${{ env.ANGULAR_ROOT_DIR }}

    - name: Build Angular
    run: npm run build:prod
    working-directory: ${{ env.ANGULAR_ROOT_DIR }}

    - name: Deploy angular to production server
    uses: appleboy/scp-action@v0.1.2
    with:
    host: ${{ secrets.REMOTE_HOST }}
    username: ${{ secrets.REMOTE_USER }}
    key: ${{ secrets.SSH_PRIVATE_KEY }}
    source: "${{ env.ANGULAR_ROOT_DIR }}/dist/apps/myapp/*"
    target: ${{ secrets.FRONT_TARGET_PATH_PROD }}
    strip_components: 4

    # 这里配置pipeline中的SSH公私钥及远程地址。
    - name: Setup ssh key
    run: |
    mkdir -p "$HOME/.ssh"

    printf '%s\n' "${{ secrets.SSH_PRIVATE_KEY}}" > "$HOME/.ssh/id_rsa"
    chmod 600 "$HOME/.ssh/id_rsa"

    printf '%s\n' "${{ secrets.SSH_PUBLIC_KEY}}" > "$HOME/.ssh/id_rsa.pub"
    chmod 600 $HOME/.ssh/id_rsa.pub

    ssh-keyscan -p 22 "${{ secrets.REMOTE_HOST }}" >> "$HOME/.ssh/known_hosts"

    - name: Docker compose up
    run: |
    docker context create remote --docker "host=ssh://${{ secrets.REMOTE_USER }}@${{ secrets.REMOTE_HOST }}"
    docker context use remote
    docker compose -f docker-compose.prod.yml up -d --build