GCP自己架設前後端Docker容器筆記和踩過的雷 2025-07-07 15:45:12

前言

架設這個網站dbvs.space已經快3年了
這是一台Laravel架設的網站,沒有前後端分離
隨著新技術的出現
現在比較高階的工程師職位要求的技術也越來越多
所以打算另外架設一個網站
1.在GCP上面架設新的機器,並在Godaddy買新的域名
2.在機器上架設Docker容器,分成前端和後端,並用nginx容器反向代理將子域名導向兩個容器
3.用certbot容器自動建立ssl憑證
4.前端機器用Vuejs,後端機器用Laravel建立RESTful API
5.學習編寫RESTful API和API文件,以及前後端的單元測試和整合測試
以後Blog的文章就移到那邊去,並且新增留言功能讓有緣人留言互相交流
這篇文章先從機器架設開始

架設Docker容器步驟

1.GCP建立一個CentOS機器,並安裝docker和docker compose
注意事項 : 因為要讓nginx docker容器來處理反向代理,所以host機器上不用安裝nginx(因為之前習慣機器建好第一步就安裝nginx,導致我第一次嘗試的時候差點發瘋)

2.在根目錄建立一個專案目錄結構

your-project/
├── docker-compose.yml
├── nginx/
│   └── default.conf
├── certbot/
│   └── conf/
│   └── www/
├── frontend/
│   └── (Vue 專案程式碼)
└── backend/
    └── (Laravel 專案程式碼)

3.Docker Compose 配置 (docker-compose.yml)

services:
  nginx:
    image: nginx:latest
    container_name: nginx_proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certbot/conf:/etc/nginx/ssl:ro # 掛載 Let's Encrypt 憑證
      - ./certbot/www:/var/www/certbot # 掛載用於憑證驗證的目錄
    depends_on:
      - frontend
      - backend
    restart: unless-stopped
    networks:
      - app_network

  frontend:
    build:
      context: ./frontend # Vue 專案的路徑
      dockerfile: Dockerfile.frontend
    container_name: vue_frontend
    restart: unless-stopped
    networks:
      - app_network

  backend:
    build:
      context: ./backend # Laravel 專案的路徑
      dockerfile: Dockerfile.backend
    container_name: laravel_backend
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      # 其他 Laravel 環境變數
    restart: unless-stopped
    networks:
      - app_network

  redis:
    image: redis:latest
    container_name: redis_cache
    ports:
      - "6379:6379" # 僅在需要從外部訪問時才開放,一般情況下不建議
    restart: unless-stopped
    networks:
      - app_network

  certbot:
    image: certbot/certbot
    container_name: certbot_manager
    volumes:
      - ./certbot/conf:/etc/letsencrypt # 憑證儲存路徑
      - ./certbot/www:/var/www/certbot # Webroot 路徑
    command: certonly --webroot -w /var/www/certbot --email your_email@example.com -d frontend.domain.com -d backend.domain.com --agree-tos --no-eff-email --force-renewal
    # 第一次運行時用於獲取憑證,之後可以註釋掉或修改為 renew 命令
    # command: renew --webroot -w /var/www/certbot --post-hook "service nginx reload" # 自動更新並重載 Nginx
    depends_on:
      - nginx # Certbot 需要 Nginx 運行才能進行 Webroot 驗證
    networks:
      - app_network

networks:
  app_network:
    driver: bridge

4.Dockerfile 配置

前端 Dockerfile (frontend/Dockerfile.frontend)
# Vue 前端 Dockerfile
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
後端 Dockerfile (backend/Dockerfile.backend)
# Laravel 後端 Dockerfile
FROM php:8.3-fpm-alpine

WORKDIR /var/www/html

# 安裝 PHP 擴展和 Composer
RUN apk add --no-cache \
    nginx \
    supervisor \
    curl \
    libpng-dev \
    libjpeg-turbo-dev \
    libzip-dev \
    libpq-dev \
    icu-dev \
    && docker-php-ext-install pdo_mysql gd zip bcmath opcache intl \
    && rm -rf /var/cache/apk/*

COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer

COPY . .

RUN composer install --no-dev --optimize-autoloader

RUN php artisan optimize

# 配置權限
RUN chown -R www-data:www-data /var/www/html/storage \
    && chown -R www-data:www-data /var/www/html/bootstrap/cache \
    && chmod -R 775 /var/www/html/storage \
    && chmod -R 775 /var/www/html/bootstrap/cache

EXPOSE 9000
CMD ["php-fpm"]

5.Nginx 配置 (nginx/default.conf) 注意事項 : 因為要先讓certbot建立第一次的ssl憑證,所以nginx只要設定80port讓certbot容器可以連進來建立憑證(我第一次架的時候有寫443port的部分,結果nginx的容器一直打不開,docker status 一直 restarting)

server {
    listen 80;
    listen [::]:80;
    server_name api1.domain.com api2.domain.com;

    # Certbot 驗證路徑
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
}

6.第一次獲取 Let's Encrypt 憑證

先運行 Nginx並確認 nginx docker有正常開啟
docker compose up -d --build nginx
docker ps -a 確認nginx docker有正常開啟後運行certbot docker建立憑證
docker compose up -d --build certbot
執行docker logs certbot_manager查看日誌確定取得憑證成功
docker logs certbot_manager

7.拿完憑證要更改docker compose文件和nginx文件

Nginx 文件 (nginx/default.conf)
server {
    listen 80;
    listen [::]:80;
    server_name api1.domain.com api2.domain.com;

    # Certbot 驗證路徑
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri; # 強制跳轉到 HTTPS
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name api1.domain.com;

    ssl_certificate /etc/nginx/ssl/live/api1.domain.com/fullchain.pem; # Certbot 生成的憑證
    ssl_certificate_key /etc/nginx/ssl/live/api1.domain.com/privkey.pem; # Certbot 生成的私鑰

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Vue 前端反向代理
    location / {
        proxy_pass http://frontend:80; # frontend 是 Docker Compose 中定義的服務名稱
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name api2.domain.com;

    ssl_certificate /etc/nginx/ssl/live/api2.domain.com/fullchain.pem; # Certbot 生成的憑證
    ssl_certificate_key /etc/nginx/ssl/live/api2.domain.com/privkey.pem; # Certbot 生成的私鑰

    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1.2 TLSv1.3;

    # 指向 Laravel 專案的 public 目錄作為根目錄
    # /var/www/html 是 backend 容器內 Laravel 專案的根目錄,所以 public 會是 /var/www/html/public
    root /var/www/html/public; # <--- 關鍵更改

    index index.php index.html index.htm; # 定義索引檔案

    # 處理所有請求,如果不是真實檔案或目錄,就重寫到 index.php
    location / {
        try_files $uri $uri/ /index.php?$query_string; # <--- 關鍵更改
    }

    # 將 PHP 請求轉發到 PHP-FPM 服務 (backend 容器)
    location ~ \.php$ {
        # 注意:這裡使用 fastcgi_pass 而不是 proxy_pass
        fastcgi_pass backend:9000; # <--- 關鍵更改

        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # 這些是在 Nginx 容器內設置的頭部,用於轉發給 PHP-FPM
        fastcgi_param Host $host;
        fastcgi_param HTTPS on; # 告知 Laravel 請求是 HTTPS
        fastcgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for;
        fastcgi_param HTTP_X_REAL_IP $remote_addr;
        fastcgi_param HTTP_X_FORWARDED_PROTO $scheme;
    }

    # 阻止訪問敏感檔案和目錄
    location ~ /\.env {
        deny all;
    }

    location ~ /\.git {
        deny all;
    }
}
Docker Compose 文件 (docker-compose.yml)
certbot 的 command更改
certbot:
    image: certbot/certbot
    container_name: certbot_manager
    volumes:
      - ./certbot/conf:/etc/letsencrypt # 憑證儲存路徑
      - ./certbot/www:/var/www/certbot # Webroot 路徑
    # 第一次運行時用於獲取憑證,之後可以註釋掉或修改為 renew 命令
    command: renew --webroot -w /var/www/certbot --post-hook "service nginx reload" # 自動更新並重載 Nginx
    depends_on:
      - nginx # Certbot 需要 Nginx 運行才能進行 Webroot 驗證
    networks:
      - app_network

8.改完文件重新啟動nginx

docker compose restart nginx

9.啟動所有的服務

docker compose up -d --build

10.確認機器正常運作後子域名應該就可以正常顯示了

docker ps -a