Перенесення WordPress в Docker



Міграція з Raspberry Pi 4 на Raspberry Pi 5 стала чудовою нагодою впорядкувати інфраструктуру та перейти на контейнеризацію: більшість сервісів, включно з WordPress, я переніс із класичних системних сервісів у Docker. Такий підхід дозволив розділити компоненти, спростити керування та зробити систему значно стабільнішою. До того ж контейнеризація забезпечила гнучкість, швидке масштабування та зручність під час подальших оновлень.

Одне із таких перенесень торкнулося і цього сайту на WordPress. Це виявилося не просто, проте чітко описавши кроки міграції все вийшло як і очікувалося.

Вступ

Docker дозволяє ізолювати компоненти – WordPress, PHP-FPM, MySQL і Nginx – у незалежні, легко керовані контейнери. Це суттєво спрощує майбутні оновлення, відновлення після збоїв, переноси на інші хости та масштабування. Водночас перенесення конфігурації Nginx потребує уважності: шляхи до root, FastCGI, SSL та ACME-каталогів завжди відрізняються між хостом і контейнером, що часто стає причиною помилок.

Окрему увагу варто приділити Certbot: у Docker не працює nginx-плагін, тому важливо вчасно перемкнутись на webroot-метод. Після цього оновлення сертифікатів відбуватиметься стабільно.

Після старту контейнерів і перевірки роботи сайту локально, фінальним кроком стає перенаправлення портів на маршрутизаторі – і WordPress уже повністю працює у новому контейнерному середовищі.

Я міграцію робив всередині локальної мережі між двома Raspberry Pi, тому в цьому випадку DNS записи залишаються прив’язаними до тієї ж самої зовнішньої IP адреси.

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

Тепер послідовно почнемо процес міграції!

Підготовка Raspberry Pi

Оскільки це не просто міграція, а міграція на нову більш сучасну апаратну платформу, то звісно я оновив і операційну систему Raspberry Pi OS з версії Bookworm до Trixie.

Операційну систему переніс на швидкий NVMe і почав визначати етапи міграції:

  • Установка Docker
  • Написання docker compose файлу
  • Перенесення даних
  • Зміна конфігурацій
  • Перевірка та тестування

Звісно ці пункти я буду розписувати по кожному компоненту, адже вони всі взаємопов’язані між собою.

Установка Docker

Процедура установки докера описана на офіційному сайті, я лише повторю команди які я виконав:

sudo apt install ca-certificates curl gnupg

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) \
  signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Щоб докер щоразу не запитував права на виконання, або виконувався без помилок, я його додав в свою групу

sudo usermod -aG docker $USER
newgrp docker

Таким чином докер встановлено. Необхідно тепер створювати необхідні docker-контейнери із потрібними параметрами використовуючи централізоване рішення docker compose.

Визначення дерева ресурсів

Файл docker compose буде розміщений в головній директорії проекту. Я вирішив цю директорію створити в домашній, тому повний шлях буде виглядати так:

~/Docker/Sites

В цій директорії будуть створені наступні піддиректорії та файли:

  • docker-compose.yml – головний файл конфігурації докерів
  • .env – файл із змінними оточення
    • MYSQL_ROOT_PASSWORD=<strong root password>
    • MYSQL_USER=<username>
    • MYSQL_PASSWORD=<strong user password>
  • ostrich – директорія де буде зберігатися вміст вордпреса (wp-admin, wp-content, wp-config.php тощо…)
  • certificates – у цій директорії Certbot тимчасово створює ACME challenge-файли, які Let’s Encrypt використовує для підтвердження домену

Таким чином для кожного контейнера буде змонтовано відповідну директорію. Такі налаштування відбуваються вже в docker compose.

Щодо самих докер контейнерів, то їх буде чотири:

  • webserver – це веб сервер nginx який буде працювати на портах 80 та 443
  • ostrich_wp – це докер самого вордпресса, на момент написання статті це була версія 6.9
  • ostrich_db – це докер бази даних MySQL Community Server
  • certbot – докер, який буде оновлювати сертифікат, але буде запускатися одноразово по виклику крона

Написання docker compose файлу

Для кращого розуміння, код docker-compose.yml я буду описувати по блокам. Так можна буде поступово створювати ресурси та контролювати кожен етап взаємодії послідовно.

Опис сервісів починається з блоку

services:

База даних

З самого початку почну описувати сервіси. Перший з них – докер бази даних на основі MySQL мажорної версії 8.4. Для безпеки обміну даними, конфіденційні значення змінних оточення винесено в окремий файл .env. а звичайні змінні можна прописувати безпосередньо в параметрі environment.

Під час створення контейнера буде автоматично створено volume, в якому будуть зберігатися дані бази даних, також ці дані будуть автоматично монтуватися при старті контейнера.

Для комунікації між докер контейнерами буде створена мережа ostrich_net, про неї я згадаю пізніше, але щоб далі не повторюватися цей блок буде застосовано для всіх сервісів.

  db:
    image: mysql:8.4
    container_name: ostrich_db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes:
      - dbdata:/var/lib/mysql
    networks:
      - ostrich_net

WordPress

Другим сервісом буде контейнер самого вордпресса. Цей контейнер має пряму залежність від БД, тому це прописуємо в параметрі depends_on. Для використання даних аутентифікації також будемо використовувати .env файл, і додатково в змінних пропишемо порт, та назву БД.

Раніше створену директорію ostrich треба примонтувати до цього докера, адже ця директорія потрібна, щоб плагіни/теми могли записувати файли (аплоад, кеш і т.д.) також щоб PHP-FPM бачив wp-config.php, wp-content, wp-admin, wp-includes

wordpress:
    depends_on:
      - db
    image: wordpress:6.9.0-fpm-alpine
    container_name: ostrich_wp
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - ./ostrich:/var/www/html
    networks:
      - ostrich_net

Веб сервер

Третій блок, один із найголовніших – це webserver. Цей контейнер відіграє роль веб сервера і бере на себе прослуховування 80 та 443 портів. Також він буде керувати всіма віртуальними хостами, які будуть прописані в його конфігурації.

Для цього контейнера також буде прописана залежність від wordpress, адже без нього сайт працювати не буде.

Директорію ostrich треба примонтувати також і для контейнера webserver, щоб nginx віддавав файли і перекидав їх PHP-запити на FPM. Додатково треба примонтувати файл конфігурації та volume де будуть зберігатися сертифікати, та відбуватися їх перевірка.

 webserver:
    depends_on:
      - wordpress
    image: nginx:stable-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./ostrich:/var/www/html
      - ./nginx/nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
      - ./certificates:/var/www/certbot:ro
    networks:
      - ostrich_net

Сертифікат

Останній контейнер – certbot. Він на відміну від інших контейнерів буде запускатися, виконувати перевірку актуальності дати сертифіката і при необхідності оновлювати його, а потім зупинятися.

В такому режимі контейнер не буде завжди висіти в фоновому режимі, що оптимізує ресурси распбері пай. так само як і для веб сервера, для сертбота необхідно примонтувати директорії із сертифікатами.

certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: ostrich_certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - ./certificates:/var/www/certbot

Щоб коректно завершити написання docker-compose.yml файла, необхідно визначити весь перелік volumes та визначити назву мережі для обміну даними між контейнером.

volumes:
  certbot-etc:
  dbdata:

networks:
  ostrich_net:
    driver: bridge

Тепер можна переходити до наступного етапу – перенесення файлів та імпорт бази даних

Перенесення файлів та імпорт БД

Ми вже визначили точки монтування, та volumes, тому достатньо просто дані скопіювати з хоста в примонтовані директорії. Ще раз нагадаю, що і куди повинно бути скопійовано:

  • Весь вміст директорії wordpress в моєму випадку в директорію ./ostrich
  • Весь вміст директорії letsencrypt в моєму випадку в volume certbot-etc
  • Файли конфігурації віртуального хоста та файл options-ssl-nginx.conf в директорію ./nginx/nginx-conf

шлях до volume може бути таким /var/lib/docker/volumes/…/_data/ і доступ до цієї директорії можливий лише з правами суперкористувача

Після того, як файли перенесено, необхідно наповнити базу даних, використовуючи бекап.

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

docker compose up -d db

Коли БД створено, можна зробити імпорт

docker exec -i wordpress mysql -u <username> -p wordpress < /tmp/ostrich_wp.sql

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

Конфігурація веб сервера

В докері вже існує базова конфігурація веб сервера, такої конфігурації достатньо для функціонування веб ресурсу. Оскільки ця стаття саме про міграцію вордпресса, тому я буду вважати, що конфігурація віртуального хоста вже існує.

При міграції з «класичного» Nginx на хості в Nginx всередині Docker не можна просто скопіювати конфігурацію один до одного, її треба доопрацювати!

Щоб сайт запрацював, важливо:

  • Змінити root на шлях всередині контейнера.
  • Перевести fastcgi_pass з Unix-сокета на ім’я_php-контейнера:порт.
  • Настроїти /.well-known/acme-challenge/ на спільний volume з Certbot.
  • Оновити шляхи до SSL-сертифікатів і options-ssl-nginx.conf згідно файлової структури контейнера.
  • Перевірити listen/server_name, щоб вони відповідали доменам і портам, описаним у docker-compose.yml.

Усе інше – кешування, заголовки безпеки, оптимізації – можна переносити практично без змін. Розгляну кожен пункт окремо:

Шлях до сайту – root

У звичайній інсталяції Nginx ми вказуємо реальний шлях на файловій системі хоста. У Docker усе інакше: всередині контейнера WordPress підмонтовується, наприклад, у /var/www/html.

на хості:

root /var/www/html/ostrich.kyiv.ua;

у докері:

root /var/www/html;

Підключення до PHP-FPM

В цьому випадку PHP працює в окремому контейнері – wordpress, і Nginx обмінюється даними з ним по мережі Docker wordpress:9000. Якщо залишити сокет, Nginx в контейнері просто не знайде PHP-FPM тому усі .php почнуть віддаватися як завантаження файлів або 502 Bad Gateway.

на хості:

fastcgi_pass unix:/run/php/php8.3-fpm.sock;

у докері:

fastcgi_pass wordpress:9000;

ACME / Certbot

У Docker був створений окремий volume /var/www/certbot, який спільний між контейнером Nginx і контейнером Certbot. Якщо цього не зробити – підтвердження домену у Let’s Encrypt завалиться, і сертифікат не оновиться.

на хості:

location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    allow all;
}

у докері:

location ^~ /.well-known/acme-challenge/ {
    root /var/www/certbot;
    allow all;
}

Шляхи до SSL-сертифікатів та файл опцій

В цілому шлях збігається з тим, як він був примонтований, тому його можна просто перевірити. Єдине що змінилося це шлях до options-ssl-nginx.conf, він проголошений в параметрі include.

на хості:

include /etc/letsencrypt/options-ssl-nginx.conf;

у докері:

include /etc/nginx/conf.d/options-ssl-nginx.conf;

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

Конфігурація та оновлення сертифіката

Конфігурація сертифіката знаходиться в volume certbot-etc, за адресою

/var/lib/docker/volumes/.../_data/renewal/ostrich.kyiv.ua.conf

На хості сертифікат оновлювався через nginx-плагін, а в докері nginx-плагіна не існує, тому треба змінити запис на webroot-метод

на хості:

[renewalparams]
authenticator = nginx
installer = nginx

у докері:

[renewalparams]
authenticator = webroot
webroot_path = /var/www/html

Після цього, сертифікат буде використовувати новий метод оновлення. Проте для оновлення сертифікату, цих дій недостатньо.

Для оновлення сертифікату додатково потрібно створити крон запис. Цей запис буде використовуватися для автоматизації регулярної перевірки та оновлення SSL-сертифіката, щоб уникнути його прострочення.

Certbot у Docker не працює постійно, тому крон періодично запускає контейнер, виконує перевірку та, за потреби, оновлює сертифікат через webroot-метод. Важливо також врахувати, що Nginx перевіряє конфігурацію та завантажує сертифікати лише під час старту, а не в реальному часі, тому він «не знає» про оновлення сертифіката, доки не буде перезапущений або не виконається reload.

Завдяки крону оновлення відбуваються автоматично, і адміністратору залишається лише подбати про коректне перезавантаження Nginx після ротації сертифіката.

Запуск та перевірка

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

Запускаємо всі контейнери однією командою:

docker compose up -d

Додатково до докера БД повинні ще зібратися докери вордпресса, веб сервера та сертбота. Перевірити статус контейнерів можна виконавши команду

docker ps

Результатом повинен бути статус кожного контейнера.

CONTAINER ID   STATUS      
0dfe7a855343   Up 3 days
385808a951cc   Up 3 days
4bdc3e884a1a   Up 3 days

Як я писав вище, контейнер сертбота запускається, виконує перевірку дати сертифіката та закривається, щоб не бути в фоні, тому по факту ми бачимо тільки три контейнери. Так і повинно бути.

Після того, як перевірка роботи сайту прошла успішно локально, можна переключити перенаправлення портів на роутері і тоді сайт стане доступним для користуваів інтернету за старими налаштуваннями DNS.

Висновки

Міграція WordPress із класичної хост-інсталяції в Docker виявляється значно структурованішою та передбачуваною, якщо розділити процес на чіткі етапи: підготовка системи, розгортання контейнерів, перенесення даних і коректне налаштування конфігурацій. Правильне визначення директорій, точок монтування, мереж і змінних оточення – критично важливе для успішної роботи всієї системи.