From da2154ddca55fde1b7721deaaa2745afec2c8d9f Mon Sep 17 00:00:00 2001 From: Akira Date: Tue, 24 Feb 2026 14:32:12 +0900 Subject: [PATCH] =?UTF-8?q?=E6=9C=AC=E7=95=AA=E3=83=87=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=82=A4=E7=94=A8=E8=A8=AD=E5=AE=9A=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - backend/Dockerfile.prod: gunicorn で起動する本番用 Dockerfile - frontend/Dockerfile.prod: マルチステージビルドの本番用 Dockerfile - docker-compose.prod.yml: Traefik 連携・本番用 compose 設定 - main.keinafarm.net でフロントエンド・バックエンドを公開 - /api/ はバックエンド(priority=10)、それ以外はフロントエンド(priority=5) - .env.production.example: 本番環境変数のサンプル - settings.py: ALLOWED_HOSTS・CORS_ALLOWED_ORIGINS を環境変数から設定可能に Co-Authored-By: Claude Sonnet 4.6 --- .env.production.example | 7 +++ backend/Dockerfile.prod | 31 ++++++++++++ backend/keinasystem/settings.py | 19 +++++--- docker-compose.prod.yml | 83 +++++++++++++++++++++++++++++++++ frontend/Dockerfile.prod | 28 +++++++++++ 5 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 .env.production.example create mode 100644 backend/Dockerfile.prod create mode 100644 docker-compose.prod.yml create mode 100644 frontend/Dockerfile.prod diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..903f33b --- /dev/null +++ b/.env.production.example @@ -0,0 +1,7 @@ +# 本番環境用の環境変数サンプル +# サーバー上で .env.production としてコピーして値を設定してください +# このファイル自体は git にコミットして OK(値は入れない) + +DB_PASSWORD=ここにDBパスワードを設定 +SECRET_KEY=ここにDjangoのSECRET_KEYを設定(50文字以上のランダム文字列) +MAIL_API_KEY=ここにWindmillとの連携用APIキーを設定 diff --git a/backend/Dockerfile.prod b/backend/Dockerfile.prod new file mode 100644 index 0000000..d270584 --- /dev/null +++ b/backend/Dockerfile.prod @@ -0,0 +1,31 @@ +FROM python:3.12-slim + +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + gcc \ + g++ \ + postgresql-client \ + libpq-dev \ + libgdal-dev \ + libgeos-dev \ + libproj-dev \ + fonts-noto-cjk \ + libgirepository1.0-dev \ + libcairo2 \ + libpango-1.0-0 \ + libpangocairo-1.0-0 \ + gir1.2-pango-1.0 \ + && rm -rf /var/lib/apt/lists/* + +ENV CPLUS_INCLUDE_PATH=/usr/include/gdal +ENV C_INCLUDE_PATH=/usr/include/gdal + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["gunicorn", "keinasystem.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "2", "--timeout", "120"] diff --git a/backend/keinasystem/settings.py b/backend/keinasystem/settings.py index 52d8dce..5d4b45d 100644 --- a/backend/keinasystem/settings.py +++ b/backend/keinasystem/settings.py @@ -20,7 +20,8 @@ SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-default-key') DEBUG = os.environ.get('DEBUG', 'True') == 'True' -ALLOWED_HOSTS = ['*'] +_allowed_hosts = os.environ.get('ALLOWED_HOSTS', '*') +ALLOWED_HOSTS = [h.strip() for h in _allowed_hosts.split(',')] # Application definition @@ -145,12 +146,16 @@ SIMPLE_JWT = { 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), } -CORS_ALLOWED_ORIGINS = [ - "http://localhost:3000", - "http://127.0.0.1:3000", - "http://localhost:3001", - "http://127.0.0.1:3001", -] +_cors_origins = os.environ.get('CORS_ALLOWED_ORIGINS', '') +if _cors_origins: + CORS_ALLOWED_ORIGINS = [o.strip() for o in _cors_origins.split(',')] +else: + CORS_ALLOWED_ORIGINS = [ + "http://localhost:3000", + "http://127.0.0.1:3000", + "http://localhost:3001", + "http://127.0.0.1:3001", + ] # メールフィルタリング機能 MAIL_API_KEY = os.environ.get('MAIL_API_KEY', '') diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..745850a --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,83 @@ +version: '3.8' + +networks: + traefik-net: + external: true + internal: + internal: true + +services: + db: + image: postgis/postgis:16-3.4 + container_name: keinasystem_db + restart: always + environment: + POSTGRES_DB: keinasystem + POSTGRES_USER: keinasystem + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U keinasystem -d keinasystem"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - internal + + backend: + build: + context: ./backend + dockerfile: Dockerfile.prod + container_name: keinasystem_backend + restart: always + environment: + DB_NAME: keinasystem + DB_USER: keinasystem + DB_PASSWORD: ${DB_PASSWORD} + DB_HOST: db + DB_PORT: 5432 + SECRET_KEY: ${SECRET_KEY} + DEBUG: "False" + ALLOWED_HOSTS: main.keinafarm.net + CORS_ALLOWED_ORIGINS: https://main.keinafarm.net + MAIL_API_KEY: ${MAIL_API_KEY} + FRONTEND_URL: https://main.keinafarm.net + depends_on: + db: + condition: service_healthy + networks: + - internal + - traefik-net + labels: + - "traefik.enable=true" + - "traefik.http.routers.keinasystem-api.rule=Host(`main.keinafarm.net`) && PathPrefix(`/api/`)" + - "traefik.http.routers.keinasystem-api.entrypoints=websecure" + - "traefik.http.routers.keinasystem-api.tls=true" + - "traefik.http.routers.keinasystem-api.tls.certresolver=letsencrypt" + - "traefik.http.routers.keinasystem-api.priority=10" + - "traefik.http.services.keinasystem-api.loadbalancer.server.port=8000" + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.prod + args: + NEXT_PUBLIC_API_URL: https://main.keinafarm.net + container_name: keinasystem_frontend + restart: always + depends_on: + - backend + networks: + - traefik-net + labels: + - "traefik.enable=true" + - "traefik.http.routers.keinasystem.rule=Host(`main.keinafarm.net`)" + - "traefik.http.routers.keinasystem.entrypoints=websecure" + - "traefik.http.routers.keinasystem.tls=true" + - "traefik.http.routers.keinasystem.tls.certresolver=letsencrypt" + - "traefik.http.routers.keinasystem.priority=5" + - "traefik.http.services.keinasystem.loadbalancer.server.port=3000" + +volumes: + postgres_data: diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod new file mode 100644 index 0000000..de7d53d --- /dev/null +++ b/frontend/Dockerfile.prod @@ -0,0 +1,28 @@ +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +# NEXT_PUBLIC_API_URL はビルド時に埋め込まれる +ARG NEXT_PUBLIC_API_URL=https://main.keinafarm.net +ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} + +RUN npm run build + +# --- 実行ステージ --- +FROM node:20-alpine + +WORKDIR /app + +COPY --from=builder /app/package*.json ./ +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/public ./public +COPY --from=builder /app/node_modules ./node_modules + +EXPOSE 3000 + +CMD ["npm", "start"]