From a42884b4532bf045eba1d28d494a8d894a1a6417 Mon Sep 17 00:00:00 2001 From: CaasianVale <1544257291@qq.com> Date: Fri, 7 Mar 2025 04:08:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Docker=E9=95=9C=E5=83=8F=E6=9E=84?= =?UTF-8?q?=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 70 +++++++++++++++++++++++++--- docker-compose.prod.yml | 48 ++++++++++++++++++++ docker-compose.yml | 42 +++++++++++++++-- frontend/Dockerfile | 39 ++++++++++++++++ frontend/nginx.conf | 73 ++++++++++++++++++++++++++++++ 5 files changed, 262 insertions(+), 10 deletions(-) create mode 100644 docker-compose.prod.yml create mode 100644 frontend/Dockerfile create mode 100644 frontend/nginx.conf diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 4260c42..4789a57 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -3,13 +3,22 @@ name: Docker Build and Push on: push: branches: [ main ] # 只在 main 分支推送时触发 + workflow_dispatch: # 支持手动触发 + +env: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + CACHE_FROM_BACKEND: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-backend:buildcache + CACHE_TO_BACKEND: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-backend:buildcache,mode=max + CACHE_FROM_FRONTEND: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-frontend:buildcache + CACHE_TO_FRONTEND: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-frontend:buildcache,mode=max jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Checkout code + uses: actions/checkout@v3 - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -19,20 +28,67 @@ jobs: run: echo "TIME=$(date +'%Y%m%d%H%M')" >> $GITHUB_ENV - name: Login to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 + with: + platforms: linux/amd64,linux/arm64 - - name: Build and push - uses: docker/build-push-action@v2 + # 构建后端镜像 + - name: Build and push backend + uses: docker/build-push-action@v4 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: | - ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner:latest - ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner:${{ env.TIME }} + ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-backend:latest + ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-backend:${{ env.TIME }} + cache-from: ${{ env.CACHE_FROM_BACKEND }} + cache-to: ${{ env.CACHE_TO_BACKEND }} + + # 构建前端镜像 + - name: Build and push frontend + uses: docker/build-push-action@v4 + with: + context: ./frontend + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-frontend:latest + ${{ secrets.DOCKERHUB_USERNAME }}/stock-scanner-frontend:${{ env.TIME }} + cache-from: ${{ env.CACHE_FROM_FRONTEND }} + cache-to: ${{ env.CACHE_TO_FRONTEND }} + + deploy: + needs: build + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + + steps: + - name: Deploy to server + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.SERVER_HOST }} + username: ${{ secrets.SERVER_USERNAME }} + key: ${{ secrets.SSH_PRIVATE_KEY }} + script: | + cd ${{ secrets.DEPLOY_PATH }} + + # 设置环境变量 + export DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }} + export TAG=${{ env.TIME }} + + # 拉取最新代码 + git pull + + # 拉取最新镜像并重启服务 + docker compose -f docker-compose.prod.yml pull + docker compose -f docker-compose.prod.yml up -d + + # 清理未使用的镜像和容器 + docker system prune -f \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..5c18530 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,48 @@ +version: '3.8' + +services: + backend: + image: ${DOCKERHUB_USERNAME}/stock-scanner-backend:${TAG:-latest} + container_name: stock-scanner-backend + ports: + - "8888:8888" + environment: + - API_KEY=${API_KEY} + - API_URL=${API_URL} + - API_MODEL=${API_MODEL} + - API_TIMEOUT=${API_TIMEOUT} + - LOGIN_PASSWORD=${LOGIN_PASSWORD} + - ANNOUNCEMENT_TEXT=${ANNOUNCEMENT_TEXT} + volumes: + - ./logs:/app/logs + - ./.env:/app/.env + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8888/config"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + networks: + - stock-scanner-network + + frontend: + image: ${DOCKERHUB_USERNAME}/stock-scanner-frontend:${TAG:-latest} + container_name: stock-scanner-frontend + ports: + - "80:80" + depends_on: + - backend + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + networks: + - stock-scanner-network + +networks: + stock-scanner-network: + driver: bridge \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 4d0da35..57ddb0b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,11 @@ version: '3.8' services: - stock-analyzer: - build: . + backend: + build: + context: . + dockerfile: Dockerfile + container_name: stock-scanner-backend ports: - "8888:8888" environment: @@ -10,6 +13,39 @@ services: - API_URL=${API_URL} - API_MODEL=${API_MODEL} - API_TIMEOUT=${API_TIMEOUT} + - LOGIN_PASSWORD=${LOGIN_PASSWORD} + - ANNOUNCEMENT_TEXT=${ANNOUNCEMENT_TEXT} volumes: - - .:/app + - ./logs:/app/logs restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8888/config"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + networks: + - stock-scanner-network + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: stock-scanner-frontend + ports: + - "80:80" + depends_on: + - backend + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + networks: + - stock-scanner-network + +networks: + stock-scanner-network: + driver: bridge diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..625b254 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,39 @@ +# 构建阶段 +FROM node:18-alpine as build-stage + +# 设置工作目录 +WORKDIR /app + +# 安装 yarn +RUN apk add --no-cache yarn + +# 复制 package.json 和 yarn.lock(如果有) +COPY package*.json yarn*.lock ./ + +# 安装依赖 +RUN yarn install --frozen-lockfile + +# 复制项目文件 +COPY . . + +# 构建应用 +RUN yarn build + +# 生产阶段 +FROM nginx:stable-alpine as production-stage + +# 复制自定义nginx配置(如需要) +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# 从构建阶段复制构建结果到nginx服务目录 +COPY --from=build-stage /app/dist /usr/share/nginx/html + +# 暴露80端口 +EXPOSE 80 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --quiet --tries=1 --spider http://localhost:80 || exit 1 + +# 启动nginx +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..046294f --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,73 @@ +server { + listen 80; + server_name localhost; + + #access_log /var/log/nginx/host.access.log main; + + root /usr/share/nginx/html; + index index.html; + + # 缓存静态资源 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, max-age=31536000, immutable"; + } + + # API请求代理到后端服务 + location /api/ { + proxy_pass http://backend:8888/api/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 300s; + } + + location /login { + proxy_pass http://backend:8888/login; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location /check_auth { + proxy_pass http://backend:8888/check_auth; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location /need_login { + proxy_pass http://backend:8888/need_login; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location /config { + proxy_pass http://backend:8888/config; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + location /analyze { + proxy_pass http://backend:8888/analyze; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 300s; + } + + # 所有其他路由返回index.html(SPA应用需要) + location / { + try_files $uri $uri/ /index.html; + } + + # 错误页面 + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} \ No newline at end of file