Wprowadzenie
WordPress jest jednym z najpopularniejszych systemów zarządzania treścią (CMS). Statystycznie zasila ponad 39% wszystkich stron internetowych, jakie widzisz w sieci. Jest to popularny wybór ze względu na możliwość rozbudowy za pomocą wtyczek oraz elastyczny system szablonów. Pozwala na zmianę wyglądu w kilka sekund. Co więcej, jego administracja może odbywać się za pośrednictwem interfejsu internetowego, bez konieczności posiadania dużej wiedzy technicznej.
Ponadto WordPress jest darmowy, ma otwarte oprogramowanie (open-source) i jest zbudowany na bazie MySQL z przetwarzaniem PHP. Możesz wdrożyć WordPressa na stosie LAMP (Linux, Apache, MySQL i PHP) lub stosie LEMP (Linux, Nginx, MySQL i PHP). Jednak konfiguracja tego stosu za każdym razem, gdy chcesz wdrożyć aplikację, okazuje się czasochłonna.
Na szczęście nowoczesne metody dostarczania oprogramowania, takie jak chmura obliczeniowa, Docker, oraz Docker Compose usprawniły ogólne doświadczenie programistów. Narzędzia te upraszczają proces konfiguracji dowolnego stosu, unikając narzutu związanego z instalacją i konfiguracją poszczególnych komponentów za każdym razem, gdy chcesz wdrożyć aplikację. Zamiast tego piszesz pliki konfiguracyjne, które zostaną użyte do pobrania i utworzenia obrazów oraz uruchomienia ich w kontenerach Docker, co pozwala na wdrożenie aplikacji za pomocą tylko jednego polecenia.
Kontenery to lekkie, zwirtualizowane, przenośne, zdefiniowane programowo, standaryzowane środowiska, które pozwalają oprogramowaniu działać w izolacji od innego oprogramowania uruchomionego na fizycznej maszynie hosta. Docker Compose pozwala zarządzać wieloma kontenerami i zapewniać ich komunikację. Na przykład kod źródłowy aplikacji i baza danych muszą się komunikować.
W tym samouczku będziemy budować wielokontenerową aplikację WordPress. Kompletna aplikacja WordPress wymaga trzech kontenerów: bazy danych MySQL, serwera Nginx i kodu źródłowego WordPress. Ponieważ bezpieczeństwo jest priorytetem na nowoczesnych stronach internetowych, uzyskamy certyfikat SSL od Let’s Encrypt w celu zabezpieczenia instalacji. Następnie skonfigurujemy zadanie cron do okresowego sprawdzania i odnawiania certyfikatów, aby bezpieczeństwo Twojej witryny było utrzymywane w sposób ciągły.
Wymagania wstępne
- Ponieważ jest to praktyczny samouczek, powinieneś mieć zainstalowany system Ubuntu 20.04 jako początkowe środowisko operacyjne. Powinieneś także posiadać użytkownika non-root z uprawnieniami sudo. Oto krok po kroku samouczek, który pomoże Ci skonfigurować serwer Ubuntu.
- Musisz także zainstalować Dockera. Możesz zapoznać się z tym samouczkiem dotyczącym tego, jak zainstalować i obsługiwać Dockera na Ubuntu 18.04.
- Instalacja Docker Compose. Możesz postępować zgodnie z krokiem 1 samouczka Jak zainstalować i skonfigurować Docker Compose na Ubuntu 20.04.
- Zarejestrowana nazwa domeny jest wymagana do uzyskania certyfikatu TLS/SSL od Let’s Encrypt. Na potrzeby tego samouczka użyjemy
example.com. - Skonfiguruj rekordy DNS, aby skierować ruch na swój VPS. Potrzebujesz dwóch rekordów DNS:
- Rekord A z
example.comwskazującym na publiczny adres IP Twojego serwera. - Rekord A z
www.example.comwskazującym na publiczny adres IP Twojego serwera.
- Rekord A z
Krok 1: Zdefiniowanie konfiguracji serwera WWW
Serwer WWW przechowuje pliki Twojej witryny i umożliwia użytkownikom dostęp do aplikacji internetowej. Dlatego też w pierwszym kroku zdefiniujemy konfigurację serwera WWW. Zdefiniujemy plik konfiguracji serwera Nginx, który będzie zawierał bloki lokalizacji (location blocks) specyficzne dla WordPressa. Dołączymy również bloki lokalizacji kierujące żądania weryfikacji Let’s Encrypt do klienta Certbot w celu automatycznego odnawiania certyfikatu.
Zacznijmy od utworzenia katalogu dla projektu. Możesz wybrać dowolną nazwę katalogu. W tym samouczku użyjemy wordpress_docker na potrzeby tego samouczka. Wprowadź następujące polecenie, aby utworzyć katalog i przejść do niego:
|
1 |
mkdir wordpress_docker && cd wordpress_docker |
Następnie utwórz katalog do przechowywania plików konfiguracyjnych Nginx za pomocą polecenia:
|
1 |
mkdir nginx-conf |
Użyj nano do otwarcia pliku za pomocą następującego polecenia:
|
1 |
nano nginx-conf/nginx.conf |
W tym pliku zdefiniujemy podstawowe dyrektywy dla konfiguracji bloku serwera Nginx. Obejmują one dyrektywy dotyczące nazwy serwera, katalogu głównego dokumentów (document root) oraz bloków lokalizacji (location) w celu kierowania żądań wtyczki Certbot o certyfikaty, pliki statyczne i przetwarzanie PHP. Możesz przeczytać nasz samouczek dotyczący Jak zabezpieczyć Nginx za pomocą Let’s Encrypt, aby dowiedzieć się więcej. Dodaj następujący kod do pliku, zastępując example.com swoją zarejestrowaną nazwą domeny:
|
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 |
server { listen 80; listen [::]:80; server_name example.com www.example.com; index index.php index.html index.htm; root /var/www/html; location ~ /.well-known/acme-challenge { allow all; root /var/www/html; } location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } location ~ /\.ht { deny all; } location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { log_not_found off; access_log off; allow all; } location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ { expires max; log_not_found off; } } |
Zdefiniujmy sekcje, które zostały dodane:
-
Directives:
listen: informuje serwer Nginx, aby nasłuchiwał na porcie80. Umożliwia to korzystanie z wtyczki webroot Certbota do składania żądań o certyfikaty. Po uzyskaniu certyfikatu SSL zaktualizujemy tę konfigurację, aby korzystała z portu443.server_name: definiuje to nazwę domeny, którą ta konfiguracja powinna obsługiwać. Ruch kierowany do zdefiniowanej tutaj nazwy domeny będzie trafiać do tego konkretnego bloku serwera, a tym samym do katalogu głównegoroot.root: definiuje katalog główny dla żądań do powyższej nazwy domeny. Zazwyczaj jest to katalog zawierający rzeczywiste pliki naszej strony internetowej. Ustawiliśmy ten katalog na/var/www/html. Zostanie on utworzony jako punkt montowania Dockera podczas budowania kontenera. Instrukcje dla tego procesu zdefiniujemy wewnątrz WordPress Dockerfile.index: definiuje to pliki, które będą używane jako indeksy lub punkt wejścia do serwera WWW podczas przetwarzania żądań. Przenieśliśmy index.php przed index.html, aby Nginx traktował priorytetowoindex.php.
-
Bloki Location:
location ~ /.well-known/acme-challenge: obsługuje żądania do katalogu well-known, w którym Certbot dodaje plik tymczasowy w celu zweryfikowania, czy DNS dla określonej domeny wskazuje na konkretny serwer, od którego żądamy certyfikatów SSL. Dlatego, aby ten krok zadziałał, należy dodać prawidłową domenę zamiastexample.com, którego używamy w tym samouczku.location /: pobiera żądania URI i przekazuje kontrolę do WordPressaindex.phpw celu zażądania argumentów do przetworzenia.location ~ \.php$: obsługuje przetwarzanie PHP i przekazuje żądanie do kontenera WordPress (skonfigurujemy plik konfiguracyjny dla tego w późniejszym kroku). Zdefiniowaliśmy tutaj konfiguracje specyficzne dla protokołu FastCGI, ponieważ obraz Docker WordPress będzie oparty na obrazie php:fpm. Nginx używa niezależnego procesora PHP dla żądań specyficznych dla PHP. Użyjemy procesoraphp-fpm, który jest dostarczany z obrazem Dockerphp:fpm.location ~ /\.ht: obsługuje pliki.htaccess, których Nginx nie używa. Dyrektywadeny allzapewnia, że pliki te nigdy nie zostaną udostępnione odwiedzającym witrynę.location = /favicon.ico, location = /robots.txt: jak widać w definicji, zapobiega to rejestrowaniu żądań do plików/favicon.icoi/robots.txt.location ~* \.(css|gif|ico|jpeg|jpg|js|png)$: wyłącza rejestrowanie żądań do plików statycznych i zapewnia ich buforowanie w celu zmniejszenia obciążenia serwera.
Możesz teraz zapisać i zamknąć plik, naciskając CTRL+X, Y, a następnie ENTER. To kończy pierwszy krok.
Krok 2: Definiowanie zmiennych środowiskowych
Zmienne środowiskowe są niezbędne do ułatwienia komunikacji między aplikacją WordPress a bazą danych. Zapewniają one również trwałość danych aplikacji. Zmienne środowiskowe obejmują poufne informacje, takie jak dane uwierzytelniające bazy danych, oraz informacje niepoufne, takie jak nazwa bazy danych i host.
Ze względów bezpieczeństwa zawsze dobrym pomysłem jest niedodawanie poufnych informacji do repozytoriów projektów. Dlatego zamiast ustawiać poufne wartości w pliku Docker Compose, zdefiniujemy dane uwierzytelniające MySQL wewnątrz plików .env, które nie zostaną zatwierdzone w repozytorium projektu i nie będą narażone na publiczne ujawnienie. Wewnątrz katalogu głównego projektu root ~/wordpress_docker otwórz plik .env:
|
1 |
nano .env |
|
1 2 3 |
MYSQL_ROOT_PASSWORD=your_strong_root_password MYSQL_USER=your_wordpress_database_user MYSQL_PASSWORD=strong_wordpress_database_password |
Następną rzeczą, którą musisz zrobić, jest dodanie pliku .env do plików .gitignore i .dockerignore, aby upewnić się, że nie zostanie on dodany odpowiednio do Twoich repozytoriów lub obrazów Docker.
Nie jest to konieczne w tym samouczku, ale jeśli chcesz pracować z Git do kontroli wersji, wprowadź następujące polecenie, aby zainicjować bieżący katalog jako repozytorium git:
|
1 |
git init |
Otwórz plik .gitignore za pomocą nano:
|
1 |
nano .gitignore |
Dodaj następującą linię:
|
1 |
.env |
Zapisz i zamknij plik. Następnie otwórz plik .dockerignore za pomocą nano:
|
1 |
nano .dockerignore |
Dodaj następującą linię:
|
1 |
.env |
Przy okazji możesz opcjonalnie dodać inne pliki i katalogi powiązane z rozwojem Twojej aplikacji:
|
1 2 3 |
.env .git docker-compose.yml |
Zapisz i zamknij plik po zakończeniu. To wszystko w tym kroku. Przejdźmy do definicji Docker Compose.
Krok 3: Konfiguracja usług za pomocą Docker Compose
Docker Compose używa pliku docker-compose.yml do budowania obrazów. Plik ten zawiera definicje usług dla kompletnej konfiguracji aplikacji. Definicje usług to w zasadzie instrukcje dotyczące sposobu uruchamiania kontenera. Usługa to faktycznie działający kontener.
Docker Compose umożliwia definiowanie różnych usług dla aplikacji wielokontenerowych poprzez łączenie poszczególnych usług za pomocą współdzielonych sieci i wolumenów. Zobaczysz to w działaniu, ponieważ zdefiniujemy trzy kontenery dla naszej aplikacji: serwer WWW, instalację WordPressa i bazę danych. Dodamy czwarty kontener do uruchomienia klienta Certbot w celu odnawiania certyfikatów.
Wprowadź następujące polecenie, aby utworzyć plik docker-compose.yml:
|
1 |
nano docker-compose.yml |
Pierwsza linia w pliku docker-compose.yml to linia definicji wersji. Dla naszej ustawiliśmy wartość 3. Następnie możesz zacząć definiować swoje usługi. Dodaj poniższy fragment kodu w pliku, aby zdefiniować usługę db:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
version: '3' services: #Usługa MySQL db: image: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network |
Omówmy to, co mamy w definicjach usługi db poniżej:
image: określa obraz, na którym będzie bazować kontener. Zawsze lepiej jest określić konkretną wersję (mysql:8.0) niż używać tagu latest (mysql:latest) ponieważ przyszłe wersje obrazów MySQL mogą kolidować z naszą aplikacją, jeśli zdarzy nam się przebudować ten obraz. Więcej informacji na temat najlepszych praktyk dotyczących plików Dockerfile w oficjalnej dokumentacji Dockerfile.container_name: tutaj określamy nazwę kontenera.restart: ta dyrektywa określa zachowanie kontenera po restarcie. Domyślnie jest tono, ale ustawiliśmy ją na zawszerestart, chyba że zostanie zatrzymany ręcznie.env_file: ta dyrektywa służy do określenia lokalizacji pliku ze zmiennymi środowiskowymi (.env) używanymi przez naszą aplikację.environment: służy do określania dodatkowych zmiennych środowiskowych. W tym samouczku określiliśmy zmiennąMYSQL_DATABASEdo przechowywania nazwy bazy danych dla naszej aplikacji. Nazwa bazy danych może być zawarta wdocker-compose.yml.volumes: służy do określania lokalizacji montowania. W naszym przykładzie zamontowaliśmy nazwany wolumen o nazwie dbdata w katalogu/var/lib/mysqlw kontenerze, który zazwyczaj jest standardowym katalogiem danych dla MySQL.command: ta dyrektywa określa polecenie, które zastąpi domyślną instrukcję CMD dla obrazu. Dodaliśmy opcję do standardowego poleceniamysqlduruchamiającego serwer MySQL wewnątrz kontenera. Dodana przez nas opcja to--default-authentication-plugin=mysql_native_password, co aktualizuje domyślną wtyczkę uwierzytelniania dla MySQL, aby używała uwierzytelniania hasłem (mysql_native_password). Jest to konieczne do działania PHP (aplikacji WordPress), ponieważ używają one nazwy użytkownika i hasła do uzyskania dostępu do bazy danych. W nowszych wersjach MySQL domyślna wtyczka uwierzytelniania uległa zmianie. Jednak większość aplikacji korzysta z uwierzytelniania hasłem. Dlatego musisz zmienić to ustawienie, aby aplikacja działała.networks: ta dyrektywa służy do określenia, że usługadbpowinna dołączyć do sieciapp-network, którą zdefiniujemy w dalszej części samouczka.
Następnie zdefiniujmy konfigurację usługi dla naszej aplikacji WordPress. Nazwiemy usługę oraz container_name app. Dodaj następujący fragment kodu poniżej definicji usługi db, pamiętając o odpowiednich wcięciach:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#Usługa kodu aplikacji WordPress app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network |
Podobnie jak zrobiliśmy to z usługą db, nazwaliśmy nasz kontener i zdefiniowaliśmy politykę restartu. Kilka dodatkowych opcji, które dodaliśmy, opisano poniżej:
depends_on: ta dyrektywa zapewnia, że kontenery są uruchamiane w kolejności zależności. W naszym przypadku kontenerappzależy od konteneradb. W związku z tym uruchomi się on po uruchomieniu konteneradb. Musi to nastąpić w tej kolejności, ponieważ aplikacja WordPress wymaga do działania dostępności bazy danych MySQL.image: jak widać we fragmencie kodu, będziemy używać obrazu WordPress w wersji 5.1.1 fpm alpine image. Wyjaśniliśmy już kwestię procesoraphp-fpm, którego Nginx wymaga do przetwarzania PHP. Ten obraz się tym zajmuje. Obraz alpine oparty na projekcie Alpine Linux pomaga zachować mniejszy rozmiar obrazu. Jeśli potrzebujesz więcej informacji na temat wariantów obrazów, możesz skorzystać z tego linku do obrazów WordPress w Docker Hub.env_file: określa lokalizację pliku.envzawierającego dane uwierzytelniające bazy danych.environment: ta dyrektywa definiuje dodatkowe zmienne środowiskowe. W naszym przypadku definiujemy zmienne, których oczekuje WordPress, i przypisujemy im wartości zmiennych z naszego pliku.env. Są toWORDPRESS_DB_USER,WORDPRESS_DB_PASSWORD, orazWORDPRESS_DB_HOST, która odnosi się do serwera MySQL działającego w kontenerzedb, dostępnego z domyślnego portu MySQL3306. Na koniec widoczna jest zmiennaWORDPRESS_DB_NAME, którą ustawiliśmy na wordpress. Ta sama wartość jest określona w definicji usługi MySQL w kontenerze db:MYSQL_DATABASE=wordpress.volumes: ta dyrektywa montuje wolumen o nazwie app w punkcie montowania/var/www/html, utworzonym przez obraz WordPress. Nazewnictwo wolumenów umożliwia udostępnianie kodu aplikacji innym kontenerom.networks: na koniec dodajemy kontener app do sieciapp-network, aby zapewnić mu komunikację z innymi kontenerami w sieci.
To wszystko, jeśli chodzi o kontener usługi app dla obrazu WordPress. Zdefiniujmy teraz usługę webserver dla obrazu Nginx. Najpierw dodaj następujący fragment kodu poniżej definicji usługi app w swoim pliku docker-compose.yml:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#Usługa serwera WWW Nginx webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network |
Wyjaśniliśmy już opcję depends_on. W przypadku tej usługi webserver kontener uruchomi się po uruchomieniu kontenera app. Kontener serwera WWW bazuje na alpine Nginx image. Ma on podobną politykę restartu jak poprzednie definicje usług. Inne opcje w definicji usługi webserver obejmują:
ports: wiąże porty między maszyną hosta a kontenerem. W Kroku 1, zdefiniowaliśmy port80w plikunginx.conf. Ten port jest mapowany na port80w kontenerze.volumes: mamy połączenie montowań typu bind i nazwanych wolumenów w tej opcji:app:/var/www/html: ta definicja wolumenu montuje aplikację WordPress w katalogu/var/www/html, który wcześniej ustawiliśmy jako katalog główny (root) w bloku serwera Nginx../nginx-conf:/etc/nginx/conf.d: ta definicja montuje typu bind katalog konfiguracyjny Nginx na maszynie hosta do katalogu konfiguracyjnego Nginx zdefiniowanego dla kontenera. Dzięki temu wszelkie zmiany na maszynie hosta są automatycznie odzwierciedlane w kontenerze.certbot-etc:/etc/letsencrypt: ta definicja montuje certyfikaty i klucze Let’s Encrypt dla domeny do odpowiedniego katalogu w kontenerze.
networks: podobnie jak w poprzednich definicjach usług, dyrektywanetworksdodaje usługę webserver doapp-networks.
Skoro skończyliśmy z definicją serwera WWW, dodajmy instrukcje dla usługi Certbot. Zajmie się ona pobieraniem certyfikatów TLS/SSL z Let’s Encrypt. Jeśli chcesz dowiedzieć się więcej o zabezpieczaniu serwera Nginx, ten samouczek dotyczący jak zabezpieczyć Nginx za pomocą Let’s Encrypt jest dobrym źródłem.
Następnie dodaj poniższy fragment kodu pod usługą webserver. Pamiętaj, aby ustawić poprawną nazwę domeny i adres e-mail:
|
1 2 3 4 5 6 7 8 9 10 |
#usługa certbot certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com |
Obraz certbot uruchomi się dopiero po tym, jak webserver zostanie uruchomiony, ze względu na dyrektywę depends_on. Docker Compose pobierze obraz Certbot z Docker Hub zgodnie z definicją.
W definicji wolumenów kontener Certbot będzie udostępniać certyfikaty i klucz domeny w certbot-etc z kontenerem Nginx webserver oraz kod aplikacji z kontenerem app .
W definicji command określiliśmy podpolecenie do uruchomienia domyślnego polecenia Certbot certonly z dodatkowymi opcjami wymienionymi poniżej:
-
--webroot: określa użycie wtyczki webroot, która umieszcza pliki w folderze webroot w celu uwierzytelnienia.--webroot-path: określa ścieżkę do katalogu webroot.--agree-tos: określa, że zgadzasz się na Warunki świadczenia usług ACME.--no-eff-email: określa, że nie chcesz udostępniać swojego adresu e-mail organizacji EFF. Możesz to pominąć, jeśli chcesz go udostępnić.--staging: informuje Certbota, że chcesz najpierw uzyskać certyfikaty testowe ze środowiska stagingowego Let’s Encrypt w celu przetestowania konfiguracji przed uzyskaniem rzeczywistego certyfikatu. Let’s Encrypt posiada limity żądań dla domen (rate limiting). Dlatego wcześniejsze przetestowanie konfiguracji pomoże uniknąć nałożenia limitów na Twoją domenę.-d: ta opcja przyjmuje nazwy domen dla żądania certyfikatu. W tym samouczku uwzględniliśmyexample.comorazwww.example.com. Proszę podać swoją rzeczywistą zarejestrowaną domenę.
Nasz plik docker-compose.yml jest prawie gotowy. Musisz jednak dodać również definicje sieci i wolumenów pod usługą Certbot:
|
1 2 3 4 5 6 7 8 9 10 |
#Wolumeny volumes: certbot-etc: app: dbdata: #Sieci networks: app-network: driver: bridge |
Klucz volumes definiuje wolumeny, które mają być udostępniane wszystkim usługom (kontenerom) zdefiniowanym w tym pliku compose: certbot-etc, app, oraz dbdata. Zawartość wolumenów tworzonych przez Dockera jest przechowywana w katalogu zarządzanym przez Dockera w systemie plików hosta: /var/lib/docker/volumes/. Zawartość każdego wolumenu jest następnie montowana w dowolnym kontenerze, który z niego korzysta. Umożliwia to współdzielenie danych i kodu między kontenerami.
Klucz networks definiuje sieć typu bridge, która umożliwia komunikację między kontenerami. Kontenery w tej samej sieci bridge, takie jak webserver oraz db mogą komunikować się bezpiecznie przez porty bez wystawiania ruchu na sieć zewnętrzną. Udostępniamy tylko port 80 , aby umożliwić dostęp do stron front-endowych witryny.
Kompletny plik docker-compose.yml będzie wyglądał następująco:
|
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 |
version: '3' services: #Usługa MySQL db: image: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network #Usługa kodu aplikacji WordPress app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network #Usługa serwera WWW Nginx webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #Usługa certbot certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com #Wolumeny volumes: certbot-etc: app: dbdata: #Sieci networks: app-network: driver: bridge |
Możesz zapisać i zamknąć plik. W następnym kroku uruchomimy i przetestujemy kontener oraz żądania certyfikatów.
Krok 4: Uruchamianie kontenerów i uzyskiwanie certyfikatów SSL
Największą zaletą Docker Compose jest to, że po zdefiniowaniu wszystkich usług w pliku docker-compose.yml pliku, możesz uruchomić wszystkie kontenery za pomocą tylko jednego polecenia: docker-compose up. Polecenie to uruchamia każdą określoną instrukcję. Jeśli żądania domeny zakończą się pomyślnie, powinieneś zobaczyć prawidłowy kod wyjścia w swoim terminalu. Wprowadź następujące polecenie, aby utworzyć kontenery. Flaga -d służy do uruchamiania kontenerów w tle:
|
1 |
docker-compose up -d |
Jeśli widzisz dane wyjściowe jak na poniższym zrzucie ekranu, oznacza to, że usługi zostały pomyślnie utworzone:
To potwierdzić status usług, uruchom polecenie docker-compose ps command:
|
1 |
docker-compose ps |
Wynik polecenia jest taki, jak pokazano poniżej, jeśli wszystko zakończyło się pomyślnie. Stan kontenerów app, db, oraz webserver powinien być ustawiony na up, a kontener certbot powinien mieć status Exit0:
Jeśli widzisz cokolwiek innego niż Up w kolumnie stanu dla app, db lub webserver, lub status Exit inny niż 0 dla kontenera certbot, oznacza to, że coś poszło nie tak. Możesz sprawdzić logi każdego kontenera za pomocą polecenia docker-compose logs, i podać service_name:
|
1 |
docker-compose logs service_name |
Na przykład możesz sprawdzić logi kontenera certbot, wprowadzając następujące polecenie:
|
1 |
docker-compose logs certbot |
Aby sprawdzić, czy certyfikaty zostały zamontowane w kontenerze webserver, użyj polecenia docker-compose exec command:
|
1 |
docker-compose exec webserver ls -la /etc/letsencrypt/live |
Jeśli użyłeś rzeczywistej zarejestrowanej nazwy domeny innej niż example.com i żądania certyfikatów zakończyły się pomyślnie, powinieneś zobaczyć wynik podobny do tego:
Po potwierdzeniu, że żądanie certyfikatu zakończyło się pomyślnie, możesz edytować plik docker-compose.yml i usunąć flagę --staging. Otwórz plik za pomocą nano:
|
1 |
nano docker-compose.yml |
Przewiń w dół do sekcji definicji usługi Certbot, w opcji command i zastąp flagę --staging flagą --force-renewal. Informuje to Certbota, że żądasz odnowienia certyfikatu dla tej samej domeny. Twoja definicja usługi Certbot powinna teraz wyglądać następująco:
|
1 2 3 4 5 6 7 8 9 10 |
#certbot service certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com |
Zapisz plik po zakończeniu edycji.
Wprowadź następujące polecenie, aby ponownie utworzyć kontener certbot. Dołączona flaga --no-deps informuje Compose, aby pominąć ponowne uruchamianie usługi serwera WWW, ponieważ już działa:
|
1 |
docker-compose up --force-recreate --no-deps Certbot |
Polecenie generuje następujący zrzut ekranu, pokazujący, że żądanie certyfikatu zakończyło się pomyślnie:
To wszystko w tym kroku. W następnym kroku zmodyfikujesz plik konfiguracyjny Nginx, aby dołączyć certyfikat SSL.
Krok 5: Włączenie SSL w konfiguracji Nginx i definicji usługi
Aby Nginx obsługiwał ruch przez bezpieczny protokół SSL, najpierw zmodyfikujesz plik konfiguracyjny Nginx, aby dodać przekierowanie z HTTP do HTTPS. Następnie musisz określić lokalizacje certyfikatu i klucza, a na koniec dodać parametry bezpieczeństwa i nagłówki.
Przed modyfikacją pliku konfiguracyjnego należy pobrać zalecane parametry bezpieczeństwa Nginx z repozytorium GitHub Certbota za pomocą curl przy użyciu następującego polecenia:
|
1 |
curl -sSLo nginx-conf/options-ssl-nginx.conf |
Polecenie uruchamia się i zapisuje pobrane parametry w pliku o nazwie options-ssl-nginx.conf, wewnątrz katalogu nginx-conf. Usuń plik konfiguracyjny Nginx, abyśmy mogli utworzyć nowy za pomocą następujących poleceń:
|
1 2 |
rm nginx-conf/nginx.conf nano nginx-conf/nginx.conf |
W teraz pustym pliku nginx.conf, dodaj następujący kod, który zawiera przekierowanie z HTTP do HTTPS, protokołów uwierzytelniania SSL oraz nagłówków bezpieczeństwa. Tak jak zrobiono to wcześniej, zastąp domenę example.com swoją własną zarejestrowaną domeną:
|
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 |
server { listen 80; listen [::]:80; server_name example.com www.example.com; location ~ /.well-known/acme-challenge { allow all; root /var/www/html; } location / { rewrite ^ https://$host$request_uri? permanent; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com www.example.com; index index.php index.html index.htm; root /var/www/html; server_tokens off; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; include /etc/nginx/conf.d/options-ssl-nginx.conf; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Bezpieczeństwo-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # włącz strict transport security tylko wtedy, gdy rozumiesz konsekwencje location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } location ~ /\.ht { deny all; } location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { log_not_found off; access_log off; allow all; } location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ { expires max; log_not_found off; } } |
W pierwszym bloku serwera, który obsługuje niezabezpieczone żądania przy użyciu portu 80, określamy katalog główny (webroot) dla żądań odnowienia Certbota. Dołączamy również dyrektywę przekierowania przekierowującą żądania HTTP do HTTPS.
Drugi blok serwera obsługuje bezpieczny ruch HTTPS przychodzący na porcie 443. Jak widać, włączamy również SSL oraz HTTP2. HTTP/2 poprawia wydajność serwera. Więcej na ten temat można przeczytać w oficjalnej dokumentacji Nginx dotyczącej HTTP/2.
W tym bloku określiliśmy również, że Nginx zawiera lokalizacje certyfikatu SSL i klucza, a także zalecane parametry bezpieczeństwa Certbota, które narzędzie curl zapisało w nginx-conf/options-ssl-nginx.conf.
Dodatkowe nagłówki bezpieczeństwa służą poprawie ocen Twojej witryny w serwisach testujących bezpieczeństwo, takich jak Security Headers oraz SSL Labs. Możesz skorzystać z linków w tych nagłówkach, aby dowiedzieć się więcej: X-Frame-Options, Referrer Policy, X-Content-Type-Options, X-XSS-Protection, Content-Security-Policy. Zakomentowaliśmy nagłówek HTTP Strict Transport Security (HSTS). Możesz przeczytać o jego funkcjonalności preload i zdecydować, czy chcesz go włączyć.
Pozostałe dyrektywy, takie jak root, index, oraz bloki location specyficzne dla WordPressa pozostają bez zmian, tak jak omówiono w Kroku 1. Po zakończeniu edycji możesz zapisać i zamknąć plik.
Teraz, gdy włączyliśmy ruch HTTPS korzystający z portu 443,, musimy również włączyć ten port w definicji usługi serwera WWW. Wprowadź następujące polecenie, aby otworzyć plik docker-compose.yml za pomocą nano:
|
1 |
nano docker-compose.yml |
W sekcji serwera WWW (webserver) pod opcją ports dodaj mapowanie dla portu 443 jak wyróżniono poniżej:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network |
Kompletny plik docker-compose.yml powinien teraz wyglądać następująco:
|
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 |
version: '3' services: #Usługa MySQL db: image: mysql:8.0 container_name: db restart: unless-stopped env_file: .env environment: - MYSQL_DATABASE=wordpress volumes: - dbdata:/var/lib/mysql command: '--default-authentication-plugin=mysql_native_password' networks: - app-network #Usługa kodu aplikacji WordPress app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network #Usługa serwera WWW Nginx webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #Usługa certbot certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com #Wolumeny volumes: certbot-etc: app: dbdata: #Sieci networks: app-network: driver: bridge |
Gdy upewnisz się, że wszystko wygląda poprawnie, zapisz i zamknij plik. Następnie uruchom poniższe polecenie, aby ponownie utworzyć usługę webserver :
|
1 |
docker-compose up -d --force-recreate --no-deps webserver |
|
1 |
docker-compose ps |
Teraz, gdy wszystkie kontenery są uruchomione, możesz przejść do konfiguracji WordPressa z poziomu interfejsu internetowego.
Krok 6: Dokończ konfigurację WordPressa z poziomu interfejsu internetowego
Przejdź do nazwy domeny swojego serwera, aby kontynuować instalację. Powinna pojawić się strona główna konfiguracji WordPress. Zaprasza ona do wyboru języka przed kontynuowaniem:
Wybierz język i kliknij Continue, aby przejść do następnej strony:
Na tej stronie wpisz tytuł swojej witryny, wybierz łatwą do zapamiętania nazwę użytkownika i silne hasło. Ze względów bezpieczeństwa zaleca się nie używać Admin jako nazwy użytkownika. Wprowadź swój adres e-mail i kliknij przycisk Install WordPress, aby rozpocząć instalację WordPressa.
Po zakończeniu instalacji zostaniesz przeniesiony do ekranu logowania, na którym podasz ustawioną nazwę użytkownika i hasło. Po wprowadzeniu poprawnych danych logowania powinieneś zobaczyć kokpit WordPressa:
Pomyślnie zainstalowałeś WordPressa! Następnie musisz podjąć kroki, aby zapewnić automatyczne odnawianie certyfikatów SSL.
Krok 7: Konfiguracja automatycznego odnawiania certyfikatu SSL
Certyfikaty TLS/SSL Let’s Encrypt są ważne tylko przez 90 dni. Do Ciebie należy utworzenie konfiguracji automatycznego odnawiania, aby upewnić się, że nie wygasną. Możesz to osiągnąć, tworząc skrypt i harmonogramując go za pomocą narzędzia cron job. W tym kroku pokażemy Ci, jak utworzyć skrypt, który odnowi certyfikaty. Następnie zaplanujemy go za pomocą narzędzia cron job, aby okresowo go uruchamiać i odnawiać certyfikaty, jeśli zbliża się termin ich wygaśnięcia.
Wewnątrz katalogu projektu wordpress_docker otwórz skrypt o nazwie ssl_renewer.sh za pomocą nano:
|
1 |
nano ssl_renewer.sh |
Dodaj następujący kod do skryptu, aby obsłużyć automatyczne odnawianie i ponowne ładowanie konfiguracji Nginx. Pamiętaj, aby zastąpić wyróżnioną nazwę użytkownika swoją nazwą użytkownika inną niż root:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose –ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
W tym skrypcie przypisujemy plik binarny docker-compose do zmiennej o nazwie COMPOSE. Dołączamy również opcję –ansi never, która nakazuje skryptowi uruchamianie poleceń docker-compose bez znaków sterujących ANSI control. Następnie przypisujemy plik binarny Docker do zmiennej o nazwie DOCKER.
Skrypt przechodzi następnie do naszego katalogu projektu wordpress_docker i wykonuje następujące polecenia:
docker-compose run: uruchamia kontener certbot i nadpisuje polecenie podane w definicji usługi certbot. Zamiast uruchamiać podpolecenie certonly, uruchamia podpolecenie renew, które odnowi certyfikaty SSL/TLS od Let’s Encrypt, jeśli zbliża się termin ich wygaśnięcia.docker-compose kill: wysyła sygnał SIGHUP do kontenerawebserver, aby ponownie załadować konfiguracje Nginx. Możesz zapoznać się z tym samouczkiem od Dockera na temat jak użyć oficjalnego obrazu Docker Nginx.docker system prune: to polecenie usuwa wszystkie nieużywane kontenery i obrazy.
Zapisz i zamknij plik po zakończeniu edycji. Następnie uruchom następujące polecenie, aby uczynić go wykonywalnym:
|
1 |
chmod +x ssl_renewer.sh |
Po uczynieniu go wykonywalnym otwórz plik crontab użytkownika root, aby okresowo uruchamiać skrypt w określonych odstępach czasu:
|
1 |
sudo crontab -e |
Narzędzie crontab poprosi Cię o wybór preferowanego edytora, jeśli używasz go po raz pierwszy:
Wybierz preferowany edytor i naciśnij Enter, aby otworzyć plik. Na samym dole pliku dodaj następującą linię:
|
1 |
*/5 * * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
To ustawia interwał na pięć minut, aby umożliwić nam przetestowanie, czy nasz skrypt odnawiania zadziała. Określiliśmy również plik dziennika, który będzie przechowywał dane wyjściowe zadania: cron_docker.log.
Odczekaj pięć minut i sprawdź plik cron.log , aby sprawdzić, czy skrypt pomyślnie obsłużył żądanie odnowienia:
|
1 |
tail -f /var/log/cron_docker.log |
Jeśli żądania zakończyły się sukcesem, powinieneś zobaczyć coś podobnego do poniższego zrzutu ekranu:
Teraz, gdy przetestowaliśmy i potwierdziliśmy, że to działa, możesz zmodyfikować plik crontab , aby określić codzienne odnawianie. Na przykład możesz chcieć określić, że skrypt ma być uruchamiany codziennie o godzinie 18:00. Aby to zrobić, zmodyfikuj ostatnią linię pliku crontab , aby wyglądała tak:
|
1 |
0 18 * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
Dodatkowo musisz usunąć flagę –dry-run ze skryptu ssl_renewer.sh , aby upewnić się, że podczas jego uruchomienia nastąpi rzeczywiste odnowienie. Powinien on wyglądać następująco:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
Następnie zapisz i zamknij plik. Po wykonaniu tej czynności zadanie cron utrzyma ważność Twoich certyfikatów, odnawiając je przed upływem 90 dni.
Podsumowanie
Jeśli dotarłeś tak daleko w tym samouczku, możesz uważać się za krok bliżej do zostania inżynierem DevOps. Udało Ci się stworzyć skrypt konfiguracyjny Nginx, utworzyć plik docker-compose.yml i zdefiniować kilka usług niezbędnych do uruchomienia aplikacji WordPress za pomocą Docker i Docker Compose. Uzyskałeś certyfikaty SSL/TLS od Let’s Encrypt, aby upewnić się, że Twój serwer WWW jest bezpieczny. Na koniec utworzyłeś zadanie cron, aby upewnić się, że certyfikaty nie wygasną. Dobra robota!
Jeśli chcesz głębiej wejść w DevOps, zapoznaj się z dodatkowymi zasobami na temat kontenerów na naszym blogu:
- Wprowadzenie do Kubernetes
- Jak wdrożyć aplikację Node.js (Express.js) za pomocą Docker na Ubuntu 20.04
- Wdrażanie aplikacji PHP w klastrze Kubernetes na Ubuntu 18.04.
- Wdrażanie Laravel, Nginx i MySQL za pomocą Docker Compose
Miłego korzystania!










Komentarze
Brak komentarzy. Bądź pierwszy.