Повний цикл контейнеризації та виведення в інтернет по HTTPS без відкриття вхідних портів (80/443). Трафік іде через захищений тунель Cloudflare до reverse-proxy (Caddy) та бекенду.
http://frontend:80.project_root/
├── .env # СТВОРИТИ (з токеном тунелю)
├── backend/
│ ├── requirements.txt
│ ├── ...
│ └── Dockerfile
├── frontend/
│ ├── package.json
│ ├── ...
│ ├── Caddyfile
│ └── Dockerfile
└── docker-compose.yml
TUNNEL_TOKEN=твій_токен_від_cloudflare
0.0.0.0. Прив'язка до localhost закриє доступ для Caddy.
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Приклад команди запуску. ЗАМІНИ НА СВОЮ.
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Створи файл роутингу, який вирішує проблему CORS:
:80 {
# Роздача статики SPA
root * /srv
file_server
try_files {path} /index.html
# Проксіювання API-запитів до бекенду
handle /api/* {
reverse_proxy backend:8000
}
}
Створи багатоетапний Dockerfile:
# Етап збірки Node.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Етап вебсервера (Caddy)
FROM caddy:alpine
COPY --from=builder /app/dist /srv
COPY Caddyfile /etc/caddy/Caddyfile
EXPOSE 80
volumes. У Python коді зберігай персистентні дані виключно в директорії /app/data.
version: '3.8'
services:
backend:
build: ./backend
restart: unless-stopped
volumes:
# Іменований том монтуємо в папку /app/data всередині контейнера
- backend_data:/app/data
frontend:
build: ./frontend
restart: unless-stopped
depends_on:
- backend
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
restart: unless-stopped
depends_on:
- frontend
# Оголошення тома для збереження даних
volumes:
backend_data:
docker compose up -d --build
Переконайся, що статус тунелю Healthy, і перевір домен.