Заощадьте 15% на всіх хостингових послугах

Перевірте свої навички і отримайте Знижку на будь-який план хостингу

Використовуй код: Skills Почати
Рубрики
Адміністрація Безпека

Як встановити HAProxy з Docker Compose на Ubuntu VPS

One web service on a VPS is easy to expose directly — until you want one clean public front door, the freedom to swap the backend later, or a safer way to stop sending traffic to something broken. That is the point where a proxy stops feeling like “something for big infrastructure teams” and starts feeling practical.

intro

HAProxy fits that role well. Think of it as the traffic manager sitting in front of your application: requests hit HAProxy first, and HAProxy decides where they should go next. You do not need a large cluster to benefit from that. Even on one Ubuntu 24.04 VPS, it gives you a cleaner edge between the internet and the service you are actually running.

This guide keeps the first deployment intentionally structured: one Ubuntu 24.04 VPS, Docker Compose, one HAProxy container, one demo backend, and proof that routing really works.

Чому HAProxy важливий, перш ніж він вам знадобиться

Уявіть невеликий VPS, на якому зараз добре працює один додаток. Він відповідає на порту, сайт завантажується, і все виглядає добре. Проблеми починаються, коли вам потрібна стабільна публічна точка входу, можливість замінити backend пізніше без зміни публічної адреси або передній шар, який може припинити надсилання трафіку до неробочого сервісу. Пряме розкриття додатка починає здаватися крихким дивовижно швидко.

whymatters

Усі ці вимоги вказують на один і той же відсутній шар: контрольовану точку входу між інтернетом та вашим додатком. HAProxy забезпечує цей шар. Клієнти спочатку підключаються до HAProxy, а HAProxy вирішує, куди далі йде кожен запит.

Це розділення корисне навіть до того, як у вас з’являться кілька серверів. Це дає вам чистіший публічний край зараз і безпечніший шлях до подальших змін, таких як заміна backend, маршрутизація з урахуванням здоров’я та HTTPS. Решта посібника показує цей шаблон у його найпростішій робочій формі та перевіряє його реальним шляхом запиту.

Швидкі терміни HAProxy, які полегшать розуміння решти цього посібника

quick

Вам потрібен лише невеликий набір словника, щоб впевнено виконати першого розгортання HAProxy. Таблиця нижче охоплює терміни, які мають значення в цьому посібнику.

ТермінЗначення простою мовою
🌐 reverse proxyСервіс, що стоїть спереду, який першим отримує запити та передає їх іншому внутрішньому сервісу.
⚖️ load balancerПередній шар, який може розподіляти запити між кількома цільовими бекендами.
🚪 frontendМісце, де клієнти підключаються до HAProxy.
🧩 backendСервіс або сервер, до якого HAProxy далі передає запит.
❤️ health checkСпосіб для HAProxy помітити, чи повинен бекенд продовжувати отримувати трафік.
🐳 imageУпакований шаблон додатку, який використовується для створення контейнерів.
📦 containerЗапущений екземпляр образу.

Для цього посібника reverse proxy — це перша концептуальна модель, яку слід мати на увазі. HAProxy стоїть перед чимось іншим і контролює передачу. Load balancing — це розширена можливість, яка стає корисною, коли ви пізніше додаєте кілька серверів бекенду.

Два терміни, які мають найбільше значення, коли ви відкриваєте конфіг — це frontend та backend. Frontend — це місце, де прибуває клієнт. Backend — це місце, куди HAProxy далі передає запит. Health check має значення, тому що дозволяє HAProxy помітити, коли ціль повинна припинити отримувати трафік.

Для чого HAProxy добре підходить — і що цей посібник навмисно пропускає

whatgood

Якщо уявити ваш стек як офісну будівлю, HAProxy — це стійка реєстрації: трафік спочатку надходить туди, потім спрямовується в потрібне приміщення і припиняється надсилання в приміщення, яке явно недоступне.

У цьому посібнику це перекладається на три завдання, релевантні для початківців:

  1. приймати вхідні HTTP запити
  2. перенаправляти їх на демо-бекенд
  3. контролювати, чи бекенд достатньо здоровий, щоб продовжувати отримувати трафік

Це вже корисно з одним бекендом, оскільки дає вам один контрольований публічний край перед додатком.

Пізніше той же паттерн масштабується чисто. Ви можете замінити бекенд, додати більше бекендів, запровадити HTTPS або дозволити HAProxy розподіляти трафік між кількома цілями замість однієї. Щоб зберегти першу версію навчальною, цей посібник залишається в режимі HTTP і навмисно пропускає TLS termination, ACLs, rate limiting, stick tables та HA pairs. Це все справжні теми HAProxy. Вони просто не є правильною стартовою точкою для першого робочого розгортання.

Що ви будуєте та що вам потрібно спочатку

buildingsetup

Перед створенням файлів корисно побачити остаточну форму стека. Розгортання в цьому посібнику виглядає так:

Client browser or curl
        |
        v
HAProxy frontend (:80)
        |
        v
demo backend service (demo:5678)

Optional local-only validation:
HAProxy stats frontend (127.0.0.1:8404/stats)

Docker Compose — це основний шлях тут, оскільки він зберігає першу установку відтворюваною, видимою та легкою для редагування. Замість створення користувацького образу в перший день ви зберігаєте конфігурацію HAProxy на хості, монтуєте її в контейнер і запускаєте весь стек з одного файлу. На самостійно керованому Ubuntu VPS — наприклад, на AlexHost VPS — це чистий варіант, оскільки макет залишається легким для перевірки.

💡 Порада: Цей посібник навмисне використовує Docker Compose плюс bind-mounted haproxy.cfg. Це найпрозоріший шлях першої установки, оскільки ви можете редагувати конфігурацію проксі безпосередньо без додавання кроку побудови образу.

Перед тим як почати, переконайтеся, що у вас є ці основи:

  • Ubuntu 24.04 VPS
  • Docker Engine встановлено
  • Docker Compose v2 доступний через docker compose
  • Доступ до терміналу та дозвіл на запуск Docker
  • Порт 80 доступний на хості
  • Вхідний HTTP дозволено, якщо ви використовуєте UFW або правила брандмауера на стороні провайдера

Спочатку перевірте версію Ubuntu

lsb_release -a

ubuntu-version

Далі підтвердьте, що Docker та сучасний Compose доступні:

docker --version
docker compose version

docker-version

Якщо обидві команди повертають інформацію про версію, сторона контейнерного середовища готова, і ви можете зосередитися на HAProxy замість відхилення на встановлення Docker.

Далі переконайтеся, що порт 80 вже не використовується, а потім перевірте, чи активний UFW і чи вже дозволено HTTP:

sudo ss -tlnp | grep -E ':(80)s' || true
sudo ufw status
sudo ufw allow 80/tcp

ufw-status

✏️ ПРИМІТКА: Відсутність виходу з перевірки ss зазвичай означає, що порт 80 вільний. Якщо ви бачите nginx, apache2, caddy або іншу службу, яка вже прослуховує там, спочатку це виправте. Це десятисекундний крок перевірки перед польотом, який заощаджує багато плутанини пізніше.

У наведеному вище прикладі sudo ufw status показує Status: active, а 80/tcp вже присутній у списку дозволів. Ось чому sudo ufw allow 80/tcp повертає Skipping adding existing rule замість додавання нового правила. Цей вихід є нормальним і просто означає, що правило брандмауера вже було на місці.

Створіть папку проекту та файл Compose

Почніть зі створення невеликої папки проекту для двох файлів, які потрібні для цього першого розгортання:

mkdir -p ~/haproxy-docker
cd ~/haproxy-docker

mkdir

Після цього макет повинен бути якомога компактнішим:

~/haproxy-docker/
├── compose.yaml
└── haproxy.cfg

Тепер створіть compose.yaml та використайте цей точний вміст:

services:
  demo:
    image: hashicorp/http-echo:1.0
    command: ["-listen=:5678", "-text=Hello from the HAProxy demo backend"]
    restart: unless-stopped

  haproxy:
    image: haproxy:3.4.1
    depends_on:
      - demo
    ports:
      - "80:80"
      - "127.0.0.1:8404:8404"
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    sysctls:
      net.ipv4.ip_unprivileged_port_start: "0"
    restart: unless-stopped

Цей файл з’єднує контейнери разом, але він ще не визначає логіку запитів HAProxy. Він повідомляє Docker, які образи запустити, які порти опублікувати та звідки буде змонтована конфігурація HAProxy на хості.

Наступні параметри найбільш важливі для чистого першого розгортання:

Параметр ComposeЧому він тут
hashicorp/http-echo:1.0Надає вам крихітний, передбачуваний демо-бекенд без одночасного навчання другого веб-сервера.
haproxy:3.4.1Використовує закріплений стабільний тег замість latest, що робить посібник менш крихким з часом.
depends_onЗапускає сервіс demo перед HAProxy, що корисно для порядку першого запуску.
80:80Опублікує основний HTTP-слухач на стандартному веб-порті, який очікують читачі.
127.0.0.1:8404:8404Зберігає сторінку статистики доступною для локальної перевірки без публічного розкриття за замовчуванням.
./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:roЗмонтовує ваш видимий файл конфігурації на хості в офіційний образ HAProxy як тільки для читання.
sysctls з net.ipv4.ip_unprivileged_port_start: “0”Дозволяє контейнеру HAProxy без прав root прив’язуватися до низьких портів, таких як 80.
restart: unless-stoppedНадає вам практичне VPS за замовчуванням: перезапуск після помилки або перезавантаження, але поважає навмисну ручну зупинку.

Ще один деталь має значення тут: у цьому файлі немає користувацької мережі Docker, оскільки Docker Compose автоматично створює мережу за замовчуванням. Це дає вам DNS імен сервісів всередині проекту, тому HAProxy зможе досягти бекенду як demo:5678 без додаткового з’єднання.

⚠️ Попередження: Порт 80 — це привілейований порт, тому рядок sysctls не є декоративним. Зміна відображення хоста на 8080:80 не усуває вимогу привілейованого порту всередині контейнера, якщо HAProxy все ще прив’язується до :80 внутрішньо.

Напишіть та перевірте мінімальний haproxy.cfg

З контейнерною розводкою на місці, HAProxy все ще потребує інструкцій щодо того, де надходить трафік, куди він повинен йти та як перевіряється здоров’я backend. Створіть haproxy.cfg далі:

global
    log stdout format raw local0

defaults
    mode http
    timeout connect 5s
    timeout client 30s
    timeout server 30s

frontend http
    bind :80
    default_backend demo_backend

backend demo_backend
    balance roundrobin
    server demo1 demo:5678 check

frontend stats
    bind :8404
    stats enable
    stats refresh 10s
    stats uri /stats

Це мінімальна конфігурація, але вона не одноразова. log stdout format raw local0 — це вибір логування, дружній до контейнерів, оскільки Docker може легко виводити stdout, а mode http у defaults зберігає весь приклад у режимі HTTP, тому поведінка listener та backend залишаються послідовними та читаними.

✏️ ПРИМІТКА: Одна деталь варта уваги перед розбором розділу: balance roundrobin встановлюється явно, оскільки новіші версії HAProxy змінили алгоритм backend за замовчуванням на random, а roundrobin легше навчати передбачувано з першого разу.

Ось простий розбір кожного розділу:

РозділКлючові рядкиЩо він робить
globallog stdout format raw local0Надсилає журнали до stdout, щоб логування Docker залишалося простим.
defaultsmode http, timeoutsВстановлює базову поведінку HTTP та розумні значення timeout.
frontend httpbind :80, default_backend demo_backendСтворює публічний listener та підключає його до визначення backend.
backend demo_backendbalance roundrobin, server demo1 demo:5678 checkПовідомляє HAProxy, який сервіс використовувати та моніторити його здоров’я.
frontend statsbind :8404, stats enable, stats uri /statsДодає необов’язкову локальну сторінку перевірки, щоб ви могли бачити статус під час виконання пізніше.

Ви можете помітити одну відсутню річ: option forwardfor. Це пропущення навмисне в базовому шляху. Збереження оригінальної IP-адреси клієнта корисно пізніше, але це перше розгортання стосується доведення маршрутизації та здоров’я backend, а не навчання поведінці заголовків із демо-контейнером, який не робить цей сигнал особливо цінним.

💡 Порада: Завжди перевіряйте конфігурацію HAProxy перед запуском повного стека. Оскільки ця конфігурація посилається на backend за його назвою сервісу Compose (demo), спочатку запустіть цей backend, щоб HAProxy міг його розпізнати під час перевірки.

Запустіть перевірку з того ж каталогу проекту:

docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg

success

Якщо друга команда закінчується на Configuration file is valid, ви вже довели, що HAProxy може правильно розпарсити файл та розпізнати ціль backend перед запуском будь-якого активного listener.

Запустіть Stack і доведіть, що Proxy працює

Після валідації конфігурації запустіть stack у відокремленому режимі:

Оскільки крок валідації вже запустив demo, ця команда в основному запускає HAProxy та узгоджує повний стек з двома сервісами:

docker compose up -d

start-cmpose

Потім перевірте, чи обидва контейнери живі:

docker compose ps

compose-status

Цей вигляд процесів — лише перша контрольна точка. Він підтверджує, що Docker запустив контейнери, але ще не те, що HAProxy успішно маршрутизує трафік на backend. Наступний запит перевіряє фактичний шлях передачі даних.

Тепер запустіть фактичний тест маршрутизації з самого VPS:

curl -i http://127.0.0.1

valid

Сигнал успіху — це HTTP/1.1 200 OK плюс тіло відповіді, що містить Hello from the HAProxy demo backend. Деякі збірки http-echo обгортають цей текст у невеликий HTML-відповідь, тому зосередьтеся на фразі в тілі більше, ніж на точному форматуванні.

Якщо вам потрібен доказ на рівні браузера, відкрийте http://YOUR_SERVER_IP з іншої машини.

browser-valid

Для другої поверхні валідації перевірте локальну сторінку статистики з VPS:

curl http://127.0.0.1:8404/stats

На сторінці статистики найбільш корисні сигнали — це frontend з назвою http, backend з назвою demo_backend, рядок сервера з назвою demo1, статус показаний як UP, і зазвичай значення останньої перевірки, як-от L4OK in 0ms. Також пам’ятайте про одну невелику особливість Docker: коротка синтаксис depends_on контролює порядок запуску, але вона не чекає, поки сервіс стане здоровим. Якщо перший curl один раз не вдасться відразу після запуску, почекайте кілька секунд і спробуйте ще раз, перш ніж припускати, що конфігурація неправильна.

Різницю між станом процесу та реальним успіхом легше зберігати в табличній формі:

СтанЩо це вам говорить
Контейнери запущеніDocker запустив процеси.
curl -i http://127.0.0.1 повертає 200 OK і фразу demoHAProxy насправді маршрутизує трафік на backend.
Сторінка статистики показує demo1 як UPHAProxy бачить backend як здоровий.

Поширені помилки при першому запуску та швидкі способи їх усунення

mistakes

Якщо налаштування не працює одразу, не поддавайтеся спокусі переписати обидва файли одночасно. Більшість збоїв при першому запуску на цьому стеку передбачувані, і їх набагато легше виправити, коли ви змінюєте одну змінну за раз.

Використовуйте цю матрицю як швидкий рівень діагностики:

  • HAProxy негайно завершує роботу

    Ймовірна причина: haproxy.cfg відсутній.

    Швидке виправлення: Переконайтеся, що haproxy.cfg існує поруч з compose.yaml.

    Чому це відбувається: Офіційний образ не поставляється з готовою конфігурацією.


  • Помилка говорить, що не може відкрити /usr/local/etc/haproxy/haproxy.cfg

    Ймовірна причина: Неправильний шлях bind-mount.

    Швидке виправлення: Перевірте ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro точно.

    Чому це відбувається: HAProxy не може запуститися без дійсного файлу конфігурації.


  • Помилка говорить Permission denied на порту 80

    Ймовірна причина: Проблема з прив’язкою привілейованого порту.

    Швидке виправлення: Збережіть net.ipv4.ip_unprivileged_port_start: “0” у Compose або перемістіть HAProxy та опублікований порт на 8080.

    Чому це відбувається: Контейнер запускається як непривілейований користувач haproxy.


  • Ви змінили відображення на 8080:80 і все ще отримуєте помилку прив’язки

    Ймовірна причина: HAProxy все ще прив’язується до :80 всередині контейнера.

    Швидке виправлення: Змініть як відображення хоста, так і внутрішній рядок bind, якщо ви переходите від порту 80.

    Чому це відбувається: Правило привілейованого порту застосовується також всередині контейнера.


  • Порт 80 уже використовується

    Ймовірна причина: Інший сервіс володіє портом хоста.

    Швидке виправлення: Повторно запустіть перевірку ss і зупиніть або перемістіть конфліктуючий сервіс.

    Чому це відбувається: Тільки один процес може прослуховувати один і той же порт хоста.


  • Перевірка синтаксису повідомляє про unknown keyword або помилки, специфічні для рядка

    Ймовірна причина: Помилка в конфігурації HAProxy.

    Швидке виправлення: Повторно запустіть перевірку синтаксису і виправте точний рядок, який вона повідомляє.

    Чому це відбувається: Парсер HAProxy суворий, що корисно, коли ви використовуєте його навмисно.


  • Контейнери запущені, але curl не повертає демо-відповідь

    Ймовірна причина: Неправильний шлях маршрутизації.

    Швидке виправлення: Повторно перевірте default_backend demo_backend, server demo1 demo:5678 check та назву сервісу demo.

    Чому це відбувається: Запущений контейнер не є доказом правильного шляху від фронтенду до бекенду.


  • Локальний curl працює, але сайт недоступний ззовні

    Ймовірна причина: Брандмауер або правило безпеки постачальника.

    Швидке виправлення: Відкрийте порт 80 у UFW та будь-якому брандмауері на стороні постачальника.

    Чому це відбувається: Локальна публікація може працювати навіть коли публічний доступ все ще заблокований.

⚠️ Попередження: Змінюйте одне за раз. Якщо ви сліпо редагуєте як compose.yaml, так і haproxy.cfg, ви значно ускладнюєте визначення того, чи є збій проблемою шляху файлу, проблемою порту чи проблемою маршрутизації.

Коли вам потрібні швидкі докази, тримайте ці команди поруч:

docker compose logs haproxy
docker compose ps
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
sudo ss -tlnp | grep -E ':(80|8404)s' || true

Це три шаблони сигналізації, які найбільше варто розпізнавати з першого погляду:

[ALERT] ... Cannot open configuration file /usr/local/etc/haproxy/haproxy.cfg : No such file or directory
[ALERT] ... Starting frontend http: cannot bind socket (Permission denied) [0.0.0.0:80]
[ALERT] ... parsing [/usr/local/etc/haproxy/haproxy.cfg:12] : unknown keyword 'chekc'; did you mean 'check' maybe?

Це заспокійлива частина невеликого першого розгортання: форми збоїв зазвичай також невеликі. Вам не потрібно починати спочатку. Вам потрібно визначити, який рівень скаржиться, і спочатку виправити цю одну річ.

Куди Йти Після Встановлення

Коли демо з одним бекендом працює, архітектура вже корисна. Наступний реальний крок — замінити демо-контейнер вашим фактичним додатком, зберігаючи ту саму структуру HAProxy. Після цього додайте HTTPS/TLS як окремий наступний крок і розглядайте маршрутизацію на основі домену та ACL як окремі теми, а не поспішайте з ними на цьому першому встановленні.

end

Коли ви будете готові до більш ніж одного бекенду, той самий шаблон стає видимо значущим:

backend app_backend
    balance roundrobin
    server app1 app1:8080 check
    server app2 app2:8080 check

Для безпечного редагування конфігурації, змонтованої через bind, спочатку перевірте, а потім плавно перезавантажте HAProxy:

docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
docker compose kill -s HUP haproxy

📝 Примітка: Сторінка статистики навмисне локальна в цьому посібнику. Якщо ви коли-небудь виставите її публічно, спочатку додайте аутентифікацію та елементи керування доступом.

Це повертає вас до первісної проблеми: ви хотіли одні чисті вхідні двері перед сервісом, не перетворюючи перше встановлення на повноцінний проект операцій. Тепер у вас є робочий шлях. Що ще важливіше, у вас також є правильна ментальна модель: HAProxy спочатку отримує трафік, пересилає його туди, де він належить, і дає вам чистіший спосіб розвивати стек на самокеруваному VPS без втрати контролю над конфігурацією.