Вступ
У цій статті ми детально розглянемо процес розгортання Laravel додатку з використанням Traefik як reverse proxy, Cloudflare для SSL та Docker Compose для оркестрації. Ви дізнаєтеся про всі проблеми, з якими ми зіткнулися, та як їх вирішити.
Архітектура рішення
Наша кінцева архітектура включає:
- Traefik v3.0 – reverse proxy з автоматичним SSL через Cloudflare DNS Challenge
- Laravel 10 – PHP додаток з Inertia.js
- PHP 8.3-FPM – для обробки PHP коду
- Nginx – веб-сервер для обслуговування статичних файлів та проксування до PHP-FPM
- MySQL 8.0 – база даних
- Redis – кеш та черги
- PHPMyAdmin – адміністрування бази даних
- Worker контейнери – для обробки черг та cron завдань
Початкові налаштування
1. Підготовка сервера
|
1 2 3 4 5 6 7 8 9 10 11 |
# Оновлення системи sudo apt update && sudo apt upgrade -y # Встановлення Docker curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER # Встановлення Docker Compose v2 sudo apt install docker-compose-plugin |
2. Створення мереж Docker
|
1 2 3 4 |
# Створення зовнішньої мережі для Traefik docker network create web docker network create shared_network |
Налаштування Traefik
docker-compose.yml для Traefik та сопутніх сервісів
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
services: traefik: image: traefik:v3.0 container_name: traefik restart: unless-stopped command: - "--api.dashboard=true" - "--api.insecure=false" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--certificatesresolvers.cloudflare.acme.dnschallenge=true" - "--certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare" - "--certificatesresolvers.cloudflare.acme.email=your-email@domain.com" - "--certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" environment: - CLOUDFLARE_EMAIL=your-email@domain.com - CLOUDFLARE_DNS_API_TOKEN=your-cloudflare-token ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./letsencrypt:/letsencrypt networks: - web |
Laravel додаток
Структура проекту
|
1 2 3 4 5 6 7 8 9 |
project/ ├── docker-compose.yml # Traefik та основні сервіси ├── docker-compose-php83.yml # Laravel додаток ├── .env # Змінні середовища для Docker ├── www/telegram-api/src/ # Laravel код ├── conf/nginx/conf.d/ # Nginx конфігурації ├── docker/mysql/ # MySQL дані та конфігурації └── images/php83/ # Docker образ для PHP |
docker-compose-php83.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
services: nginx: image: nginx:latest container_name: nginx restart: unless-stopped expose: - "80" volumes: - ./conf/nginx/conf.d/:/etc/nginx/conf.d/ - ./www:/var/www - ./logs:/var/log/nginx labels: - "traefik.enable=true" - "traefik.http.routers.telegram-app.rule=Host(`telegram.crumpel.site`)" - "traefik.http.routers.telegram-app.tls.certresolver=cloudflare" - "traefik.http.routers.telegram-app.service=telegram-app" - "traefik.http.services.telegram-app.loadbalancer.server.port=80" - "traefik.docker.network=web" # КРИТИЧНО ВАЖЛИВО! depends_on: - php-8 networks: - app-network - shared_network - web php-8: build: ./images/php83 container_name: php83 restart: unless-stopped environment: MYSQL_HOST: mysql MYSQL_DATABASE: ${DB_DATABASE:-file_storage} MYSQL_USER: ${DB_USERNAME:-myuser} MYSQL_PASSWORD: ${DB_PASSWORD:-mypassword} volumes: - ./www:/var/www:delegated networks: - app-network - shared_network - web working_dir: /var/www mysql: image: mysql:8.0.35 container_name: mysql_container restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-secret} MYSQL_DATABASE: ${DB_DATABASE:-file_storage} MYSQL_USER: ${DB_USERNAME:-myuser} MYSQL_PASSWORD: ${DB_PASSWORD:-mypassword} MYSQL_ROOT_HOST: '%' command: - --authentication-policy=mysql_native_password - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --default-authentication-plugin=mysql_native_password - --bind-address=0.0.0.0 volumes: - ./docker/mysql/data:/var/lib/mysql networks: - app-network - shared_network networks: app-network: driver: bridge ipam: config: - subnet: 172.26.0.0/16 shared_network: external: true web: external: true |
Підводні камені та їх вирішення
1. Проблема: “SQLSTATE[HY000] [1130] Host not allowed”
Симптоми: Laravel не може підключитися до MySQL з помилкою доступу.
Причина: MySQL за замовчуванням дозволяє підключення тільки з localhost.
Рішення:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Додати в environment MySQL: MYSQL_ROOT_HOST: '%' # І в command: - --bind-address=0.0.0.0 # Або створити користувача вручну: docker exec mysql_container mysql -u root -psecret -e " CREATE USER 'myuser'@'%' IDENTIFIED BY 'mypassword'; GRANT ALL PRIVILEGES ON *.* TO 'myuser'@'%'; FLUSH PRIVILEGES; " |
2. Проблема: “Gateway Timeout” від Traefik
Симптоми: Traefik не може підключитися до nginx контейнера.
Причина: Коли контейнер підключений до кількох мереж, Traefik вибирає випадкову IP адресу.
Рішення:
|
1 2 3 |
labels: - "traefik.docker.network=web" # Вказати конкретну мережу |
3. Проблема: Nginx дає 404 на PHP файли
Симптоми: Статичні файли обслуговуються, але PHP файли повертають 404.
Причина: Неправильна конфігурація PHP блоку в nginx.
Рішення:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# /etc/nginx/conf.d/laravel.conf server { listen 80; server_name _; root /var/www/telegram-api/src/public; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ [^/]\.php(/|$) { fastcgi_split_path_info ^(.+?\.php)(/.*)$; fastcgi_pass php83:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } |
4. Проблема: “Permission denied” для Laravel логів
Симптоми: Laravel дає 500 помилку через неможливість писати в лог файли.
Причина: Неправильні права доступу до папки storage.
Рішення:
|
1 2 3 4 5 6 |
# Виправити права доступу docker exec php83 chown -R www-data:www-data /var/www/telegram-api/src/storage/ docker exec php83 chmod -R 775 /var/www/telegram-api/src/storage/ docker exec php83 chown -R www-data:www-data /var/www/telegram-api/src/bootstrap/cache/ docker exec php83 chmod -R 775 /var/www/telegram-api/src/bootstrap/cache/ |
5. Проблема: Різні назви баз даних у .env файлах
Симптоми: Docker створює одну базу, а Laravel шукає іншу.
Причина: Два різні .env файли – один для Docker Compose, інший для Laravel.
Рішення:
|
1 2 3 4 5 6 7 |
# Синхронізувати .env файли # Docker Compose .env: DB_DATABASE=file_storage # Laravel .env: DB_DATABASE=file_storage |
6. Проблема: PHP-FPM не запущений
Симптоми: Nginx дає 502 Bad Gateway для PHP файлів.
Причина: PHP-FPM процес не запущений в контейнері.
Рішення:
|
1 2 3 4 5 6 |
# Перевірити чи працює PHP-FPM docker exec php83 ps aux | grep php-fpm # Запустити якщо не працює docker exec php83 php-fpm -D |
Налаштування Cloudflare
DNS записи
Створіть наступні A записи в Cloudflare:
|
1 2 3 |
telegram.your-domain.com → Server IP (Proxied) traefik.your-domain.com → Server IP (Proxied) |
API токен
- Cloudflare Dashboard → My Profile → API Tokens
- Create Token з правами:
- Zone:Read
- DNS:Edit
- Додати токен в docker-compose.yml
Діагностика та моніторинг
Корисні команди для діагностики
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# Перевірити статус контейнерів docker ps # Логи конкретного контейнера docker logs nginx --tail 50 # Перевірити мережі docker network ls docker network inspect web # API Traefik для перевірки сервісів curl -s http://localhost:8080/api/http/routers | grep telegram # Тестування nginx напряму docker exec nginx curl -I http://localhost # Перевірити підключення до MySQL docker exec php83 bash -c " cd /var/www/telegram-api/src php artisan migrate:status " |
SSH тунель для PHPMyAdmin
|
1 2 3 4 5 6 |
# З локальної машини ssh -L 8090:localhost:8090 user@server-ip # Відкрити http://localhost:8090 # Логін: root, Пароль: secret |
Оптимізація та безпека
Nginx оптимізація
|
1 2 3 4 5 6 7 8 9 |
# Додати до nginx конфігурації gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript; client_max_body_size 100M; fastcgi_read_timeout 300; |
PHP оптимізація
|
1 2 3 4 5 6 |
# php.ini налаштування memory_limit = 256M max_execution_time = 300 upload_max_filesize = 100M post_max_size = 100M |
MySQL оптимізація
|
1 2 3 4 5 6 |
# my.cnf [mysqld] innodb_buffer_pool_size = 1G max_connections = 200 query_cache_size = 64M |
Моніторинг продуктивності
Метрики для відстеження
- Час відповіді Laravel додатку
- Використання пам’яті PHP-FPM
- Навантаження на MySQL
- Статус SSL сертифікатів
- Доступність сервісів через Traefik
Логування
|
1 2 3 4 5 |
# Додати до Traefik command: - "--log.level=INFO" - "--accesslog=true" - "--accesslog.filepath=/logs/access.log" |
Висновки
Розгортання Laravel додатку з Traefik та Cloudflare вимагає уваги до деталей, особливо в частині мережевої конфігурації. Основні виклики:
- Мережева ізоляція – правильне налаштування Docker мереж
- Service Discovery – забезпечення того, щоб Traefik знайшов та зміг підключитися до сервісів
- Права доступу – Laravel потребує специфічних прав для storage та cache папок
- База даних – MySQL вимагає додаткового налаштування для підключень з інших контейнерів
Проте після налаштування ви отримуєте надійну, масштабовану систему з автоматичним SSL, що значно спрощує подальшу підтримку та розгортання нових сервісів.
Корисні ресурси
Ця стаття базується на реальному досвіді розгортання та вирішення проблем у production середовищі.