Github Actions 自动部署博客前后端记录

Feb 12, 2025

#Docker

Github Actions 自动部署博客前后端记录

本文记录了如何使用 GitHub Actions 实现博客前后端的自动部署,并通过 Docker 容器化部署,完全脱离 Vercel,实现自定义服务器部署流程。

在线预览:

一、公共设置

1.生成SSH私钥

sh
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

2.将公钥添加到授权文件中

sh
# 将公钥添加到 authorized_keys 文件* cat ~/.ssh/id_rsa.pub **>>** ~/.ssh/authorized_keys # 设置正确的权限* chmod 600 ~/.ssh/authorized_keys chmod 700 ~/.ssh

3.Github Secret设置

SSHPWD: 您的 SSH 私钥内容

HOST: 您的服务器 IP 地址或域名

USERNAME: 服务器登录用户名

image-20250210144454285

二、Github Actions自动部署Next.js应用

前端应用相对简单,直接在docker环境中运行镜像即可

大致流程如下:

111

1.设置Dockerfile

dockerfile
# 构建阶段 FROM node:18-alpine AS builder WORKDIR /app # 全局安装 pnpm RUN npm install -g pnpm && \ npm install -g pnpm@latest && \ pnpm config set registry https://registry.npmmirror.com/ # 复制依赖文件 COPY package.json pnpm-lock.yaml .npmrc ./ # 安装依赖 RUN pnpm install # 复制源代码 COPY . . # 构建应用 RUN pnpm build # 运行阶段 FROM node:18-alpine AS runner WORKDIR /app # 设置环境变量 ENV NODE_ENV production ENV PORT 3000 # 从构建阶段复制必要文件 # 复制 standalone 目录 COPY --from=builder /app/.next/standalone ./ # 复制静态文件 COPY --from=builder /app/.next/static ./.next/static # 复制公共文件(如果有) COPY --from=builder /app/public ./public # 暴露端口 EXPOSE 3000 # 启动服务 CMD ["node", "server.js"]

这里其他都比较好理解,对standalone相关进行说明

这是对Nextjs镜像体积的优化

背景:为了支持SSR,Next.js项目构建完成后的产物不仅仅是前端的静态资产,还包含可以使用node命令运行的js文件,以及node_modules文件夹,导致整体文件偏大。

解决方法:使用官方的 output: "standalone"设置,官网参考,打包后会输出一个不带node_modules的最小server.js文件,可以直接代替next start

image-20250212205838866

可以看到这个最小包是不包含静态文件的,因此在dockerfile需要将静态文件复制到 standalone 目录

简而言之,使用 output: "standalone" 的主要目的是生成一个更加轻量化的部署包,减少冗余文件,并且保持 SSR 功能的同时优化了部署流程。

2.设置 Github Actions Workflow

路径:/.github/workflows/deploy.yml

yml
name: Deploy Next.js to Cloud Server on: push: branches: - main # 当推送到主分支时触发 jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Log in to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build Docker image run: | docker build -t lizh606/wanyue-blog:latest . - name: Push Docker image to Docker Hub run: | docker push lizh606/wanyue-blog:latest - name: SSH to remote server and deploy uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSHPWD }} port: 22 debug: true script: | # 登录后,拉取最新的 Docker 镜像并启动容器 docker pull lizh606/wanyue-blog:latest docker stop next-client || true docker rm next-client || true docker run -d --name next-client -p 3000:3000 lizh606/wanyue-blog:latest

也就是对应上面👆流程图的步骤

完成后提交代码开始执行

三、Github Actions 自动部署 Vue 应用

Vue项目的部署类似,这里不过多介绍

1.设置Dockerfile

dockerfile
# 使用官方 Nginx 镜像作为基础镜像 FROM nginx:latest # 复制自定义的 nginx 配置文件到容器中 COPY nginx.conf /etc/nginx/nginx.conf # 复制构建的应用文件到 Nginx 的默认静态文件目录 COPY dist /usr/share/nginx/html # 暴露容器的 8080 端口 EXPOSE 8080 # 启动 Nginx 让Nginx在前台运行,而不是作为守护进程在后台运行。 CMD ["nginx", "-g", "daemon off;"]

2.设置 Nginx 配置(nginx.conf)

⚠️ 项目中使用了域名,最后路径为域名/admin,所以需要设置路由基本路径为/admin/,将 IP 访问重定向到域名是处理在ip+端口无法访问到资源问题,若无域名,中间的处理步骤可删除

nginx
user nginx; worker_processes auto; pid /run/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; gzip on; # 服务器配置 server { listen 8080; server_name ip; # 将 IP 访问重定向到域名 if ($host = 'ip:端口') { return 301 https://wanyue.me$request_uri; } # 根目录设置 root /usr/share/nginx/html; index index.html index.htm; # 处理 JavaScript 文件的 MIME 类型 location ~* \.js$ { add_header Content-Type application/javascript; try_files $uri =404; } # 处理 /admin 路径 location /admin { alias /usr/share/nginx/html; # 使用 alias 而不是 root try_files $uri $uri/ /admin/index.html; # 处理静态资源 location ~ ^/admin/assets/.*\.(js|css|png|jpg|jpeg|gif|ico|svg)$ { add_header Content-Type application/javascript; try_files $uri =404; } } # 默认位置设置 location / { try_files $uri $uri/ /index.html; } # 添加错误页面配置 error_page 404 /404.html; location = /404.html { root /usr/share/nginx/html; } } }

3.设置 Github Actions Workflow

路径:/.github/workflows/deploy.yml

yml
name: Deploy on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: # 下载代码仓库 - uses: actions/checkout@v2 # 设置 Docker Buildx - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 # 登录 Docker Hub - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} # 安装依赖并构建 - name: Install and Build run: | npm install -g pnpm && \ pnpm install --no-frozen-lockfile && \ pnpm build # 构建并推送 Docker 镜像 - name: Build and push Docker image run: | docker build -t ${{ secrets.DOCKER_USERNAME }}/vue-admin:latest . docker push ${{ secrets.DOCKER_USERNAME }}/vue-admin:latest # SSH 连接并部署 - name: Deploy to server uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSHPWD }} port: 22 script: | # 拉取最新镜像 docker pull ${{ secrets.DOCKER_USERNAME }}/vue-admin:latest # 确保网络存在 docker network create app_common-network 2>/dev/null || true # 停止并删除旧容器 docker stop vue-admin-app || true docker rm vue-admin-app || true # 运行新容器 docker run -d --name vue-admin-app \ -p 8080:8080 \ --network=app_common-network \ ${{ secrets.DOCKER_USERNAME }}/vue-admin:latest

提交代码自动运行即可

四、Github Actions自动部署Nest.js应用

相比于前端部署,后台需要进行数据库的构建和连接,需要通过docker compose来实现

1.设置Dockerfile

dockerfile
FROM node:18-alpine WORKDIR /app # 安装 pnpm RUN npm install -g pnpm COPY package*.json ./ # 使用 pnpm 安装依赖 RUN pnpm install COPY . . RUN pnpm run build EXPOSE 13000 CMD ["sh", "-c", "ls -la /app && ls -la /app/dist/src && cat /app/dist/src/main.js && echo '\n环境变量:' && printenv && echo '\n正在启动应用...' && pnpm start:prod"]

CMD主要是方便判断执行问题,也可指运行pnpm start:prod

2.设置docker-compose.yml

yml
# Use root/example as user/password credentials version: '3.1' services: api: build: . image: nest-api container_name: nest-api ports: - '13000:13000' networks: - app-network depends_on: - db db: image: mysql:8.0 container_name: nest-db restart: always environment: MYSQL_ROOT_PASSWORD: x x x MYSQL_DATABASE: your-database-name ports: - '12000:3306' networks: app-network: ipv4_address: 172.22.0.2 volumes: - mysql_data_new:/var/lib/mysql command: - --default-authentication-plugin=mysql_native_password - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --innodb-flush-log-at-trx-commit=0 # navicat # adminer: # image: adminer # restart: always # ports: # - 12005:8080 # networks: # - app-network # 添加网络配置 networks: app-network: driver: bridge ipam: driver: default config: - subnet: 172.22.0.0/16 # 在文件末尾添加 volumes 数据卷配置 volumes: mysql_data_new: driver: local

⚠️注意点:

  • dbapi需要设置内部网络连接app-network
  • 若服务器之前启用过数据卷,需注意数据卷是否重名,以及之前用的mysql版本是否对应,否则会导致数据库启动失败
  • adminer可选,生产环境我直接使用了phpmyadmin来进行数据库管理

3.数据库连接环境变量配置

yml
DB_HOST=nest-db DB_PORT=3306 DB_DATABASE=your-database-name DB_USERNAME="xxx" DB_PASSWORD="xxx" DB_SYNC=true //生产环境不开启 LOG_ON=true # JWT secret SECRET="xxx" APP_PORT=13000

⚠️注意点:

  • DB_SYNC是typeorm的同步设置,官网表明生产环境使用是不安全的,建议使用migration进行迁移

  • DB_HOST不能是127.0.0.1或者localhost,需设置成数据库服务名称nest-db

    image-20250212212028779

3.部署测试

可在本地直接运行docker compose up -d --build测试是否可以启动,访问http://localhost:13000

运行成功后直接推送代码部署

五、Docker常用命令速查

分类命令描述
构建和管理镜像docker build -t <tag_name> <path>根据 Dockerfile 构建镜像,并使用 <tag_name> 给镜像打标签。
docker images列出所有本地存储的 Docker 镜像。
docker rmi <image_id>删除指定的镜像。
管理容器docker run -d --name <container_name> <image_name>使用指定镜像在后台启动一个容器,并命名为 <container_name>
docker ps列出当前正在运行的所有容器。
docker ps -a列出所有容器,包括停止的容器。
docker stop <container_id>停止指定的容器。
docker start <container_id>启动已停止的容器。
docker restart <container_id>重启指定的容器。
docker rm <container_id>删除指定的容器。
容器日志和监控docker logs <container_id>查看指定容器的日志。
docker stats显示所有容器的实时资源使用情况。
网络和卷管理docker network ls列出所有 Docker 网络。
docker network create <network_name>创建一个新的 Docker 网络。
docker volume ls列出所有 Docker 卷。
docker volume create <volume_name>创建一个新的 Docker 卷。
清理系统docker system prune -a -f删除所有未使用的容器、网络、镜像(包括悬空镜像)和构建缓存。
docker volume prune -f删除所有未使用的卷。
Docker Composedocker-compose up启动定义在 docker-compose.yml 文件中的所有服务。
docker-compose up -d在后台启动所有服务。
docker-compose down停止并删除所有服务和网络。
docker-compose stop停止所有服务。
docker-compose start启动已停止的服务。
docker-compose restart重启所有服务。
构建和管理服务docker-compose build根据 docker-compose.yml 文件构建或重新构建服务。
docker-compose build --no-cache不使用缓存构建服务。
docker-compose logs查看所有服务的日志。
docker-compose logs <service_name>查看指定服务的日志。
docker-compose ps列出所有服务的状态。
docker-compose exec <service_name> <command>在运行中的服务容器内执行命令。
docker-compose run <service_name> <command>在新容器中运行命令。

目录