Dokerizace WordPress

Dokerizace služeb, webů využívajících Nginx, MariaDB, PHP je při použití docker compose velmi jednoduchá. Níže uvedený návod předpokládá, že máte už na svém serveru nainstalován Docker a Docker Compose. Rovněž předpokládám, že vám na serveru poběží tzv. reverzní proxy, která bude jednotlivé requesty rozhazovat dle doménového jména na jednotlivé virtuální hosty, v našem případě NGINX kontejnery.

Původní řešení počítalo s tím, že i samotná reverzní proxy v podobě Nginx poběží jako samostatný kontejner. To se ukázalo jako problematické a to především z pohledu předávání reálné IP adresy na jednotlivé virtuální hosty, další kontejnery s Nginx.

Celé řešení dokerizace se skládá z třech kontejnerů:

  • Kontejneru s MariaDB / MySQL
  • Kontejneru s PHP-FPM
  • Kontejneru s webovým serverem Nginx

Struktura projektu

Struktura projektu
Struktura projektu
  • db – namapované úložiště datových souborů pro MariaDB
  • nginx – adresář s konfigurací pro webový server Nginx
  • php – konfigurace php-fpm a msmtp, kromě konfigurace obsahuje taky Dockerfile
  • www – samotná vebová prezentace, instalace WordPress.

Obecné problémy

Obecné problémy na které jsem při dokerizaci narazil:

  • Práva u připojených volumes. V každém kontejneru běží služba pod jiným uživatelem. Typicky např. NGINX běží pod uživatelem www-data (např. UID 33). Proto musí mít tento uživatel přístup k datům a namapované konfiguraci. Pozor, nehrá roli název role, ale její UID. Tzn. adresář www s WordPress instalací musí mít jako vlastníka nastaveného uživatele s UID 33. Jestli takový uživatel neexistuje, je třeba jej na hostitelském systému založit. Taky různé distribuce a images mají různou sadu přednastavených uživatelů. Je dobré si po nastartování kontejneru zkontrolovat seznam uživatelů a případně upravit práva u adresářů s konfigurácí a datovými soubory na hostitelském systému:

sudo docker exec blog-nginx cat /etc/passwd

  • Transakční log u MariaDB. Občas se stane, že i po korektním zastavení kontejneru s MariaDB se nepodaří databázi nastartovat díky existenci tzv. transakčného logu. Zatím jsem tomu nevěnoval moc času a tak vždy, když se tento problém objeví, pomůže z datové složky smazat soubor s názvem tc.

  • IP adresa klienta. V původní konfiguraci běžela i reverzní proxy v Docker kontejneru. Bohužel, v takovém případě není možné na webové servery předat reálnou IP adresu volající strany. Důvod je jednoduchý, jelikož proxy běží v kontejneru, jako reálná adresa volajícího se zobrazí IP adresa kontejneru v rámci interní sítě. Určité řešení existuje, např. Když použijete network_mode: host. Nakonec jsem se i tak rozhodl pro vytažení proxy mimo kontejner.

  • MariaDB, práva uživatele – jestli zakládáte uživatele do databáze, musíte uvést jako hosta rozsah adres vnitřní sítě v Dockeru. Typicky např. 172.25.0.0/24. Prosté localhost nebude stačit, jelikož PHP skript běží mimo MariaDB kontejner a tak se nebude schopen k DB připojit. Z pohledu bezpečnosti rozhodně nedoporučuji použití ‘%’.

MariaDB / MySQL

Kontejner je přístupný pouze pro PHP. Důležité je, aby data z databáze byly vytaženy z kontejneru na hostitelský file systém. V opačném případě nebudete moci jednoduše obnovovat jednotlivé kontejnery (přišli byste o data), a při zálohování budete muset zálohovat celý kontejner. Kontejner tedy nevystavuje pro hosta žádné porty, nejsou potřeba. Komunikace probíhá pouze mezi kontejnery v rámci interní sítě, která se vytvoří sama při docker-compose up. Bude mít název xzy_default, kde xzy je název adresáře ve kterém se nachází docker-compose.yml.

Pro kontrolu, jaké kontejnery jsou do této sítě připojeny použijte příkaz:


sudo docker network inspect xyz_default

Část z docker-compose.yml


mariadb: 
    image: mariadb:latest
    restart: always
    container_name: ${COMPOSE_PROJECT_NAME}-mariadb
    environment:
        - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
    volumes:
        - ${PROJECT_DIR}/db:/var/lib/mysql

Parametry COMPOSE_PROJECT_NAME, MYSQL_ROOT_PASSWORD, PROJECT_DIR jsou součástí .env souboru.

Environment – nastavuje především heslo pro root uživatele, je možné nastavit i další věci, koukněte do dokumentace na Docker Hubuhttps://hub.docker.com/_/mariadb.

Volumes – nastavte na adresář, ve kterém chcete mít uložena data. Nezapomeňte na práva.

PHP-FPM

Jediný kontejner, který se vytváří buildem z vlastní konfigurace, díky použití MSMTP a použití PHP modulů není možné použít přímo oficiální image a je potřeba si vybuildit vlastní image. Aby vám fungovala PHP mail funkce, instaluje se do image balíček msmtp. Do kontejneru pak stačí pouze namapovat konfiguraci. Pro jednoduchost jsem zvolil řešení, kdy msmtp funguje jako SMTP relay pro GMAIL SMTP. Kontejner musí být zlinkovaný s kontejnerem MariaDB.

Část z docker-compose.yml


fpm:
    build:
    context: ./php
    restart: always
    container_name: ${COMPOSE_PROJECT_NAME}-php
    hostname: ${HOST_NAME}
    volumes:
        - ${PROJECT_DIR}/www/domain.cz/html:/var/www/domain.cz/html
        - ${PROJECT_DIR}/php/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro
        - ${PROJECT_DIR}/php/msmtprc:/etc/msmtprc:ro
        - ${PROJECT_DIR}/php/etc/aliases:/etc/aliases:ro
    links:
        - mariadb

Soubor php/Dockerfile


FROM php:7.2-fpm-alpine
RUN apk add msmtp
RUN ln -sf /usr/bin/msmtp /usr/sbin/sendmail 
RUN docker-php-ext-install mysqli

Soubor php/www.conf


[www] 
  user = xfsgroup = xfs

  listen = nginx:9000

  pm = dynamic
  pm.max_children = 5
  pm.start_servers = 2
  pm.min_spare_servers = 1
  pm.max_spare_servers = 3

  catch_workers_output = yes

  php_flag[display_errors] = on
  php_admin_value[error_log] = /var/log/fpm-php.www.log
  php_admin_flag[log_errors] = on

Zvolený image s alpine linux neobsahuje standardně uživatele www-data s UID 33. Obsahuje ale uživatele xfs se stejným UID proto, PHP poběží pod tímto uživatelem. Alternativou je založení vlastního uživatele v Dockerfile a ten pak použít pro složku s webovou prezentací a NGINX server.

Soubor php/msmtprc


# Set default values for all following accounts.
defaults
auth           on
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
syslog         on

# Gmail
account        gmail
host           smtp.gmail.com
port           587
from           from@domain.cz
user           gmailuser@gmail.com
password       gmailpassword

# Set a default account
account default : gmail
aliases        /etc/aliases

  • from – není podstatné, maily, které odejdou z mail funkce budou mít i tak jako odesílatele nastavenou emailovou adresu zvoleného Google účtu.
  • user – google account user
  • password – heslo ke google účtu. Nikdy nepoužívejte přímo heslo k vašemu google účtu. Založte si speciální Google účet, nebo si u svého google účtu vygenerujte tzv. Application password v Google Accounts. Je to heslo, které je pak určeno např. pro aplikace, které nepodporují 2FA autentizaci.

Soubor aliases


root: user@domain.cz
default: user@domain.cz

NGINX

Je zvolen image s NGINX Amplify (https://amplify.nginx.com), který slouží pro monitoring NGINX instance. Pro 5 instancí je tato super služba zdarma. Jestli nechcete používat Amplify, použijte image s alpine. Tento kontejner vystavuje jako jediný port na hostovaný systém.

Aktualizace (3.2.2019)

Docker file pro Nginx Amplify najdete v Nginx Github Repository https://github.com/nginxinc/docker-nginx-amplify. Build provedete pomocí příkazu:


docker build -t nginx-amplify .

Část z docker-compose.yml


nginx:
    image: nginx-amplify:latest
        restart: always
        hostname: blog
        container_name: ${COMPOSE_PROJECT_NAME}-nginx
        environment:
              - API_KEY=${API_KEY}
              - AMPLIFY_IMAGENAME=${AMPLIFY_IMAGENAME}    
    volumes:
              - ${PROJECT_DIR}/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
              - ${PROJECT_DIR}/nginx/domain.cz:/etc/nginx/sites-enabled/domain.cz:ro
              - ${PROJECT_DIR}/www/domain.cz:/var/www/domain.cz
    ports:
              - "8080:80"      
    links:
              - fpm

Parametry

  • API_KEY – api klíč k Nginx Amplify. Získáte jej po přidání nového systému do Nginx Amplify.
  • AMPLIFY_IMAGENAME – název, který se pak bude zobrazovat v Amplify. Slouží rovněž k tomu, aby Amplify bylo schopné sloučit statistiky z více instancí stejné služby do jednoho reportu.

Volumes

  • ${PROJECT_DIR}/nginx/nginx.conf:/etc/nginx/nginx.conf:ro – Základní NGINX konfigurace
  • ${PROJECT_DIR}/nginx/domain.cz:/etc/nginx/sites-enabled/domain.cz:ro – Konfigurace konkrétní domény, virtuálního hosta
  • ${PROJECT_DIR}/www/domain.cz:/var/www/domain.cz – Adresář s webovou prezentací

Pro správnou funkci Amplify je nutné upravit v konfiguraci logování v nginx.conf:


##
# Logging Settings
##
        log_format  main_ext  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '"$host" sn="$server_name" '
                      'rt=$request_time '
                      'ua="$upstream_addr" us="$upstream_status" '
                      'ut="$upstream_response_time" ul="$upstream_response_length" '
                      'cs=$upstream_cache_status' ;

        access_log  /var/log/nginx/access.log  main_ext;
        error_log  /var/log/nginx/error.log warn;

V konfiguraci virtuálního hosta pak musíte NGINX sdělit,na jakém hostname a portu běží PHP-FPM instance.

Část z konfigurace virtualního hosta


location ~ .php$ {
    root /var/www/domain.cz/html;
        fastcgi_pass   xzy-php:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
        fastcgi_param HTTPS on;
  }

Jestli chcete aby váš NGINX použil reálnou IP adresu je nutné v nastavení reverzní přidat:


location / {
    proxy_set_header        Host $host;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;     
    proxy_pass http://localhost:8080;
}

Jediné co pak ještě musíte udělat, je správné nastavení připojení k DB ve WordPress. Nepoužívejte přímo IP adresu. Po opětovném vytvoření kontejnerů se může tato IP adresa změnit. Použijte host names, název kontejneru použitého v docker-compose.yml. Tzn. pro databázy ${COMPOSE_PROJECT_NAME}-mariadb.

A to je celé :).