Развёртывание нескольких копий бота на одном сервере
Системный nginx на хосте + N изолированных Docker-стэков по адресам /home/botN. Каждая копия — свой Telegram-токен, домен, БД, Redis.
Установка системных пакетов (один раз)
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx git
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER && newgrp docker
echo 'fs.inotify.max_user_watches=524288' | sudo tee -a /etc/sysctl.conf
echo 'fs.inotify.max_user_instances=512' | sudo tee -a /etc/sysctl.conf
sudo sysctl -pТаблица портов
| Бот | FPM | Postgres | Redis | pgAdmin | Node | Loki | Grafana |
|---|---|---|---|---|---|---|---|
| bot1 | 9001 | 5433 | 6380 | 5051 | 3011 | 3110 | 3010 |
| bot2 | 9002 | 5434 | 6381 | 5052 | 3012 | 3111 | 3011 |
| bot3 | 9003 | 5435 | 6382 | 5053 | 3013 | 3112 | 3012 |
Все порты бинятся только на 127.0.0.1.
Развёртывание копии botN
Подставляй N = 1, 2, 3, …
1. Клонирование
sudo git clone <URL_РЕПО> /home/botN
sudo chown -R www-data:www-data /home/botN
cd /home/botN2. docker-compose.yml
В данном способе необходимо удалить nginx из docker-compose.yml, так как мы будем использовать nginx от сервера.
Пример конфигурации docker-compose.yml:
services:
app:
build: .
user: root
restart: always
depends_on:
- pgdb
- redis
env_file:
- .env
working_dir: /var/www/
volumes:
- .:/var/www
ports:
- "127.0.0.1:${APP_FPM_PORT}:9000"
networks:
- pet
dns:
- 8.8.8.8
- 1.1.1.1
pgdb:
image: postgres
tty: true
restart: always
environment:
- POSTGRES_DB=${DB_DATABASE}
- POSTGRES_USER=${DB_USERNAME}
- POSTGRES_PASSWORD=${DB_PASSWORD}
ports:
- "127.0.0.1:${PGDB_PORT}:5432"
volumes:
- pgdata:/var/lib/postgresql
networks:
- pet
redis:
image: redis:latest
restart: always
ports:
- "127.0.0.1:${REDIS_PORT}:6379"
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD}
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
networks:
- pet
queue:
build: .
restart: always
depends_on:
- app
- redis
env_file:
- .env
working_dir: /var/www
volumes:
- .:/var/www
command: php artisan queue:work --sleep=3 --tries=3 --timeout=90
networks:
- pet
dns:
- 8.8.8.8
- 1.1.1.1
volumes:
pgdata:
networks:
pet:
driver: bridge3. .env
cp /home/botN/.env.example /home/botN/.env
sudo nano /home/botN/.envВ конфигурации .env для каждой версии проекта необходимо указать уникальные порты для сервисов.
Минимум уникальных строк:
APP_FPM_PORT=900N
PGDB_PORT=543N+2
REDIS_PORT=638N-1
APP_URL=https://botN.example.com
DB_HOST=pgdb
DB_PORT=5432
DB_DATABASE=botN
DB_USERNAME=botN
DB_PASSWORD=<openssl rand -hex 24>
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=<openssl rand -hex 24>
TELEGRAM_BOT_TOKEN=...
TELEGRAM_BOT_ID=...
TELEGRAM_GROUP_ID=...
TELEGRAM_SECRET_KEY=<openssl rand -hex 24>Внутри Docker все сервисы по-прежнему доступны по именам (pgdb:5432, redis:6379) — не путать с хост-портами.
4. Запуск контейнеров
cd /home/botN
docker compose build
docker compose up -d
docker compose psПроверка: все порты в выводе должны быть 127.0.0.1:900N->9000/tcp и т.д.
5. Инициализация Laravel
cd /home/botN
docker compose exec app composer install --no-dev --optimize-autoloader
docker compose exec app php artisan key:generate
docker compose exec app php artisan migrate --force
docker compose exec app php artisan storage:link
docker compose exec app php artisan config:cache
docker compose exec app php artisan route:cache
docker compose exec app chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache
docker compose exec app chmod -R 775 /var/www/storage /var/www/bootstrap/cache6. Права для системного nginx
ls -ld /home/botNЕсли владелец не www-data:
sudo chmod o+x /home/botN
sudo chmod -R o+rX /home/botN/publicПроверка: sudo -u www-data ls /home/botN/public/index.php — не должно быть «Permission denied».
7. Конфиг nginx
sudo nano /etc/nginx/sites-available/botNВставить (заменить botN и 900N на реальные значения):
server {
listen 80;
server_name botN.example.com www.botN.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name botN.example.com www.botN.example.com;
ssl_certificate /etc/letsencrypt/live/botN.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/botN.example.com/privkey.pem;
client_max_body_size 60M;
root /home/botN/public;
index index.php index.html;
access_log /var/log/nginx/botN.access.log;
error_log /var/log/nginx/botN.error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:900N;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME /var/www/public$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTPS on;
fastcgi_param HTTP_X_FORWARDED_PROTO https;
fastcgi_read_timeout 120s;
}
location ~ /\.ht { deny all; }
}Критически важно:
fastcgi_param HTTPS on;+HTTP_X_FORWARDED_PROTO https;— без них Laravel получает redirect-петлю на HTTPS.root /home/botN/public;— путь на хосте.SCRIPT_FILENAME /var/www/public$fastcgi_script_name;— путь внутри контейнера.fastcgi_pass 127.0.0.1:900N— порт этой копии, не путать.
8. Выпуск SSL-сертификата
sudo certbot --nginx -d bot3.aibot7.ru -d www.bot3.aibot7.ru9. Активация nginx
sudo ln -sf /etc/nginx/sites-available/botN /etc/nginx/sites-enabled/botN
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx # или start, если nginx был остановлен10. Регистрация Telegram-webhook
https://api.telegram.org/bot{{TELEGRAM_TOKEN}}/setWebhook?url={{TELEGRAM_WEBHOOK}}&max_connections=45&drop_pending_updates=true&secret_token={{TELEGRAM_SECRET_KEY}}&ip_address={{SERVER_IP}}11. Проверка
curl -I https://botN.example.com # 200/302 от Laravel
curl https://api.telegram.org/bot<TOKEN>/getWebhookInfo
sudo tail /var/log/nginx/botN.error.log
docker compose -f /home/botN/docker-compose.yml logs --tail=20 app queueДублирование для следующих копий
Повторить раздел «Развёртывание копии botN», меняя:
- Папку
/home/botN - Все
900N/543N/638Nпо таблице портов - Домен, Telegram-токены, пароли БД и Redis
server_name,root,fastcgi_pass, путь сертификата в nginx-конфиге
Возможные ошибки
Container "/botN_redis" is already in use
Старый контейнер с container_name. Убрать container_name: из compose, удалить старый:
docker rm -f $(docker ps -aq --filter "name=botN")bind() to 0.0.0.0:80 failed (Address already in use)
Порт 80 занят другим nginx (часто контейнерным). Найти и убить:
sudo ss -tlnp | grep ':80\s'
docker stop $(docker ps -q --filter "publish=80")services.volumes additional properties 'pgdata' not allowed
В docker-compose.yml блоки volumes: и networks: оказались внутри services:. Перенести на нулевой отступ.
0.0.0.0:5432->5432/tcp (БД торчит наружу)
В compose - ${PGDB_PORT} без IP, или в .env старый формат PGDB_PORT=5432:5432. Поправить:
- compose:
"127.0.0.1:${PGDB_PORT}:5432" .env:PGDB_PORT=5433(просто число)
Welcome to nginx! вместо Laravel
Не отключён default или нет симлинка на botN:
sudo ln -sf /etc/nginx/sites-available/botN /etc/nginx/sites-enabled/botN
sudo rm -f /etc/nginx/sites-enabled/default
sudo systemctl reload nginx502 Bad Gateway
PHP-FPM недоступен:
sudo ss -tlnp | grep 900N
docker compose ps
sudo tail /var/log/nginx/botN.error.log403 Forbidden
nginx не видит /home/botN/public/:
sudo chmod o+x /home/botN
sudo chmod -R o+rX /home/botN/publicFile not found от FPM
Неверный SCRIPT_FILENAME. Должно быть /var/www/public$fastcgi_script_name (путь внутри контейнера).
ERR_TOO_MANY_REDIRECTS / redirect loop
В блоке listen 443 отсутствуют fastcgi_param HTTPS on; + fastcgi_param HTTP_X_FORWARDED_PROTO https;. Laravel не понимает, что запрос пришёл по HTTPS, и редиректит снова на HTTPS. Добавить эти два параметра, sudo systemctl reload nginx.
conflicting server name "X" on 0.0.0.0:443, ignored
Два конфига объявляют один и тот же server_name:
sudo grep -rn 'server_name' /etc/nginx/sites-enabled/Удалить дубль.
Could not automatically find a matching server block
Certbot не нашёл блок с нужным server_name. Не использовать --nginx, выпускать через certonly --webroot или certonly --standalone, потом руками прописать сертификат в конфиге (см. раздел 7–8).
certbot предлагает Expand/Cancel
У домена уже есть сертификат без www. Ответить E — Let's Encrypt расширит сертификат и покроет оба домена.
inotify watch limit reached
Не критично, но лечится:
echo 'fs.inotify.max_user_watches=524288' | sudo tee -a /etc/sysctl.conf
echo 'fs.inotify.max_user_instances=512' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p