Docker z Let’s Encrypt i Certbot – wdrożenie sklepu internetowego PrestaShop z własnym modułem przy użyciu Docker Compose

Artykuł przedstawia krok po kroku – jak wdrożyć demonstracyjny sklep internetowy z wykorzystaniem oprogramowania PrestaShop, Docker (Docker Compose), LetsEncrypt Certbot i Nginx. Warto podkreślić, iż sklep będzie cyklicznie resetowany, aby mógł realnie pełnić funkcję demonstracyjną customowego modułu.

Zagadnienia, jakie będą poruszane to:

  • Wyjaśnienie konfiguracji zawartej w plikach wykorzystanych do wdrożenia
  • Na co zwrócić uwagę w trakcie deploymentu tytułowego stacka
  • Automatyczne odnawianie certyfikatu SSL
  • Nginx jako proxy dla kontenera dockerowego
  • Automatyczne wdrożenie własnego modułu do PrestaShop

Rezultatem wdrożenia będzie sklep internetowy PrestaShop (wraz z bazą danych) zabezpieczony certyfikatem SSL oraz posiadający pełnoprawny backoffice, w którym możemy przeprowadzać jego dalszą konfigurację poprzez np. dodanie produktów. Całość zostanie przeprowadzona z wykorzystaniem kontenerów i Dockera.


Prestashop i Docker – Konfiguracja kontenera Nginxa

W tej sekcji skupimy się na plikach konfiguracyjnych niezbędnych do realizacji wdrożenia. Ich przygotowanie wymaga podstawowej znajomości Nginxa i Certbota. Zatem zaczynajmy!

Na początek skonfigurujemy serwis Nginxa, dzięki któremu sklep będzie widoczny w przeglądarce.

 nginx:
   image: nginx:stable
   restart: always
   ports:
     - "80:80"
     - "443:443"
   volumes:
     - web-root:/var/www/html
     - ./nginx-conf:/etc/nginx/conf.d
     - certbot-etc:/etc/letsencrypt
     - certbot-var:/var/lib/letsencrypt
   depends_on:
     - prestashop

Za co odpowiada powyższy fragment kodu?

Kontener Nginx będzie w tym przypadku pełnił rolę serwera proxy. Na początku został wskazany obraz, który zostanie wykorzystany do utworzenia kontenera. Wdrożenia produkcyjne zwykle powinny wykorzystywać wersje stable. Więcej o wersji stable oprogramowania dostępne np. tutaj.

Do kontenera zostaną podmontowane pliki przechowujące konfigurację Nginx dla domeny sklepu oraz trzy nazwane wolumeny współdzielone z kontenerem Certbot. Umożliwi to, przy użyciu LetsEncrypt, na szyfrowane połączenie z wykorzystaniem certyfikatu SSL do powstającego demonstracyjnego sklepu:

  • web-root
  • certbot-etc
  • certbot-var

Wewnątrz pliku nginx-conf znajduje się konfiguracja domeny sklepu składającego się z dwóch bloków server. Poniżej pierwszy blok:

server {
   server_name demo.example.com www.demo.example.com;

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

   location / {
       return 301 https://$host$request_uri;
   }
}

Jest on skonfigurowany dla portu 80, którego zgodnie z dokumentacją nie trzeba jawnie deklarować w dyrektywie listen:

If the directive is not present then either *:80 is used if nginx runs with the superuser privileges, or *:8000 otherwise.

Pierwszy blok location jest wymagany przez plugin webroot Certbota (mający tu  zastosowanie fragment dokumentacji). Drugi blok location to przekierowanie ruchu z HTTP na HTTPS.

Drugi blok server{} jest nieco bardziej skomplikowany.

server {
   listen 443 ssl http2;
   server_name demo.example.com www.demo.example.com;

   ssl_protocols TLSv1.2 TLSv1.3;
   ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
   ssl_prefer_server_ciphers off;

   ssl_session_timeout 1d;
   ssl_session_cache shared:MozSSL:10m;
   ssl_session_tickets off;

   ssl_certificate /etc/letsencrypt/live/demo.example.com/fullchain.pem;
   ssl_certificate_key /etc/letsencrypt/live/demo.example.com/privkey.pem;

   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-Host $http_host;
       proxy_set_header X-Forwarded-Proto $scheme;

       proxy_pass http://prestashop;
   }
}

Powstał w oparciu o Mozilla SSL Configurator Generator, aby zapewnić odpowiedni poziom bezpieczeństwa zestawionego połączenia. Dodany w jego obrębie blok location zawiera nagłówki umożliwiające zadziałanie serwerowi Nginx jako proxy dla kontenera PrestaShop i wystawienie go pod domeną https://demo.example.com.

Więcej użytecznych informacji dotyczących nagłówków i proxy, na bazie których powstała ta część konfiguracji, można znaleźć tu, tu i tu. W dyrektywie proxy_pass parametrem jest nazwa serwisu określona w pierwszej linii konfiguracji kontenera PrestaShop.


Prestashop i Docker – bez SSL ani rusz…

Obecnie jesteśmy w epoce “Internetu HTTPS”. Każda strona powinna być zabezpieczona. Tym bardziej warto to wykonać, ponieważ Google przychylniej patrzy na pozycjonowanie takich stron.

Natomiast nie w każdym przypadku trzeba zakupić certyfikat SSL. Niejednokrotnie wystarczy darmowy, pełnoprawny certyfikat SSL uzyskany z CA LetsEncrypt przy użyciu narzędzia Certbot. Poniżej fragment konfiguracji odpowiadający za uzyskanie certyfikatu SSL dla domeny wdrażanego sklepu.

 certbot:
   image: certbot/certbot
   volumes:
     - web-root:/var/www/html
     - certbot-etc:/etc/letsencrypt
     - certbot-var:/var/lib/letsencrypt
   depends_on:
     - nginx
   command: certonly --webroot --webroot-path=/var/www/html --email mail@example.com --agree-tos --no-eff-email --staging -d demo.example.com -d www.demo.example.com
#--keep-until-expiring za --staging w produkcyjnej wersji w linii powyżej

Analogicznie jak w przypadku konfiguracji serwisu Nginxa został wskazany obraz, a następnie wolumeny, o których wspominałem wyżej. Pod nimi jest linia depends_on określająca, że najpierw ma zostać uruchomiony kontener Nginx, a jako drugi w kolejności kontener Certbota.

Cała magia dzieje się po słowie command. Kontener Certbota wykonuje określone polecenie. Ogólnie rzecz biorąc, pozwala ono na automatyczne uzyskanie certyfikatu dla domeny sklepu wskazanej jako parametr.

Najistotniejszym elementem polecenia jest --staging , umożliwiające wielokrotne uzyskiwanie testowego certyfikatu. Opcja przydaje się przy debugowaniu projektu kiedy to może się przytrafić wielokrotne zatrzymywanie i uruchamianie kontenerów. Niemniej w tym konkretnym przypadku docelowo musi być zamieniona na --keep-until-expiring dbające o to, by uzyskać prawidłowy certyfikat, a za każdym kolejnym uruchomieniem docker-compose up -d automatycznie odnowić go jeśli zajdzie taka potrzeba.


Przygotowanie konfiguracji PrestaShop

Kontener PrestaShop korzysta z podmontowanego katalogu z dysku hosta do dołączenia katalogu my_module do kontenera. Jest on później potrzebny do automatycznej instalacji modułu.

Sekcja Environment zawiera listę zmiennych, z których część jest wczytywana z pliku .env znajdującego się w tym samym katalogu co plik docker-compose.yml. Jednym z celów jest resetowanie sklepu dwa razy dziennie, dlatego zostały użyte zmienne PS_ERASE_DB i PS_INSTALL_DB umożliwiające usunięcie bieżącej bazy danych i utworzenie jej ponownie podczas procesu instalacji. Będzie to wykonywane cyklicznie w ramach zadania umieszczonego w crontabie.

Oczywiście, zanim wystartuje kontener PrestaShop, musi być uruchomiony kontener MySQLa. Za co odpowiada wcześniej wspomniana instrukcja depends_on.

 prestashop:
   image: prestashop/prestashop:1.7.8
   volumes:
     - ./my_module:/var/www/html/modules/my_module
   restart: always
   environment:
     PS_DOMAIN: demo.example.com
     PS_FOLDER_ADMIN: ${PS_FOLDER_ADMIN}
     PS_FOLDER_INSTALL: ${PS_FOLDER_INSTALL}
     PS_ENABLE_SSL: 1
     PS_INSTALL_AUTO: 1
     PS_ERASE_DB: 1
     PS_INSTALL_DB: 1
     DB_SERVER: ${DB_SERVER}
     DB_USER: ${DB_USER}
     DB_PASSWD: ${DB_PASSWD}
     DB_NAME: ${DB_NAME}
     DB_PREFIX: ${DB_PREFIX}
     ADMIN_MAIL: ${ADMIN_MAIL}
     ADMIN_PASSWD: ${ADMIN_PASSWD}
   depends_on:
     - mysql


Nie może być sklepu internetowego PrestaShop bez bazy danych

W przypadku kontenera MySQLa nie ma nic szczególnego. Wskazujemy użyty obraz, wolumin nazwany mający przechowywać konfigurację i kilka zmiennych środowiskowych, które wykorzystuje kontener PrestaShop. Oczywiście nic nie stoi na przeszkodzie by pliki bazy danych podmontować bezpośrednio na dysku hosta. Dzięki temu możemy np. wykonać backup bazy danych w tradycyjny sposób.

 mysql:
   image: mysql:5.7
   restart: always
   volumes:
     - mysql:/var/lib/mysql
   environment:
     MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
     MYSQL_DATABASE: ${MYSQL_DATABASE}
     MYSQL_USER: ${MYSQL_USER}
     MYSQL_PASSWORD: ${MYSQL_PASSWORD}


Konfiguracja zmiennych środowiskowych w zewnętrznym pliku

Kontenery MySQL i PrestaShop wykorzystują zmienne środowiskowe. Ich wartości nie są jednak jawnie dostępne z poziomu pliku docker-compose.yml, ponieważ znajdują się w pliku .env. Poniżej fragmenty wraz z opisami.

# Prestashop
PS_FOLDER_ADMIN=nazwa_folderu_panelu_admina
PS_FOLDER_INSTALL=nazwa_folderu_instalacyjnego
DB_SERVER=mysql
DB_USER=uzytkownik_bazy_danych
DB_PASSWD=haslo_uzytkownika
DB_NAME=nazwa_bazy_danych
DB_PREFIX=prefix_
ADMIN_MAIL=demo@example.com
ADMIN_PASSWD=jakies_trudne_haslo

Ze względów bezpieczeństwa nazwa folderu panelu admina powinna zostać zmieniona z domyślnego admin, ponieważ będzie to nazwa backoffice sklepu eksponowana w adresie url.

Z podobnych względów należy zmienić nazwę folderu instalacyjnego i prefiks tabel w zmiennej DB_PREFIX. W zmiennej  DB_SERVER wskazujemy nazwę kontenera MySQL. Mail określony w zmiennej ADMIN_MAIL będzie służył do logowania do backoffice sklepu.

# Database
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=nazwa_bazy_danych
MYSQL_USER=uzytkownik_bazy_danych
MYSQL_PASSWORD=haslo_uzytkownika

Powyższy fragment dotyczy zmiennych wykorzystywanych przez kontener MySQL.


Czas na moduł do PrestaShop

Przeprowadziliśmy konfigurację docker-compose i nginx proxy oraz zadbaliśmy o certyfikat SSL dla domeny sklepu. Czas na skrypt bash, który automatycznie zresetuje sklep demo i zainstaluje customowy moduł:

#!/bin/bash

cd /path/to/project/main/dir

DOCKER_COMPOSE=$(whereis docker-compose | awk '{ print $2 }')

echo "restarting services"
$DOCKER_COMPOSE down
$DOCKER_COMPOSE up -d
$DOCKER_COMPOSE exec -T prestashop bash -c 'rm -r /var/www/html/install.lock'

#need to sleep a little to allow containers be fully operable
sleep 30

echo "module installation"
$DOCKER_COMPOSE exec -T prestashop bash -c 'php bin/console prestashop:module install my_module'
$DOCKER_COMPOSE exec -T prestashop bash -c 'rm -r /var/www/html/var/cache/pro*'
echo "finished module installation"
echo "finished the process"

Kilka słów na temat powyższego skryptu:

  • Jeśli planujemy użyć crontaba to każde polecenie docker-compose exec musi być wykonywane z parametrem -T, ponieważ nie chcemy w takim wypadku interaktywnej powłoki.
  • Usunięcie pliku install.lock po wykonaniu docker-compose up -d jest wymagane ze względu na pewien rodzaj buga z obrazem PrestaShop prowadzącego do zapętlenia procesu instalacji. Polega on na tym, że sklep jest wdrożony, ale oprogramowanie PrestaShop uważa, że nie jest i próbuje go instalować. Co ciekawe, problem ten nie występuje za każdym razem. Natomiast usunięcie tego pliku definitywnie rozwiązuje problem.
  • Wystąpienie instrukcji sleep z parametrem 30 daje czas (30s) na pełne uruchomienie kontenerów, bo tylko wtedy moduł się zainstaluje.
  • Instalacja modułu bez wyczyszczenia cache psuje sklep (sic!); tworzą się dwa katalogi prod i nietypowo wyglądający pro_. Oba muszą zostać usunięte.

Na koniec skrypt jest dodany do pliku Crontab lokalnego użytkownika:

0 */12 * * * /path/to/project/main/dir/deploy_script.sh > /path/to/project/main/dir/file.log 2>&1

Warto nadmienić, że ewentualne błędy będą w powyższym wpisie crona przekierowywane do pliku file.log.


Kompletny plik docker-compose.yml do uruchomienia sklepu internetowego PrestaShop za pomocą Docker

Opisane wyżej konfiguracje Nginxa, Certbota, PrestaShop i MySQL muszą zostać uzupełnione o poniższy fragment volumes dodany na samym dole, aby całość była zgodna ze specyfikacją docker-compose:

volumes:
 certbot-etc:
 certbot-var:
 web-root:
 mysql:

Wtedy powstaje plik, który możemy uruchomić poleceniem docker-compose up -d:


Docker i Let’s Encrypt – na co zwracać uwagę

Przed wdrożeniem kontenera Certbota umożliwiającego uzyskanie certyfikatu opartego o CA Let’s Encrypt należy zastanowić się, w jaki sposób ów certyfikat ma być odnawiany. W opisanym przykładzie założeniem był reset sklepu dwa razy na dobę. Przygotowany w Bash skrypt wyłącza oraz usuwa aktywne kontenery, a następnie ponownie je uruchamia razem z kontenerem Certbota.

Jeśli produkcyjny deployment miałby być jednorazowy i aplikacja miałaby stale działać to jedyne co należałoby cyklicznie uruchamiać to kontener Certbota, a następnie przeładowanie konfiguracji Nginxa. Wszystko również byłoby wykonane za pomocą skryptu Bash dodanego do tablicy Crona.

Na pewno należy zwrócić uwagę by katalogi /var/www/html, /etc/letsencrypt i /var/lib/letsencrypt były podpięte w formie woluminów do kontenerów Certbota i Nginxa. Poza tym istotne jest zawarcie wszystkich parametrów automatyzujących akceptacje zgód etc. w poleceniu wykonywanym w command.


Rezultat wdrożenia

To już wszystko. Można w przeglądarce wpisać domenę, pod którą uruchomiliśmy sklep i naszym oczom ukaże się domyślny layout z przykładowymi produktami:

Wdrożony sklep

A tak wygląda backoffice:

Backoffice wdrożonego sklepu

Całość wdrożona na czystej instalacji Ubuntu Server 20.04.3 LTS.


Autor

Piotr Bracha — pracuje jako administrator systemów. Karierę zaczynał jako młodszy administrator systemów w call center, gdzie miał możliwość poznania administrowania systemem CentOS i centralą telefoniczną Asterisk. Obecnie rozwija się w technologiach kontenerowych i chmurowych. Fan automatyzacji i porządku – w kodzie, ale też w środowisku pracy. Poza pracą uwielbia motoryzację i strzelectwo. Więcej artykułów autorstwa Piotra znajdziesz na Medium.




.

Leave a Comment

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *