Miliony uživatelů se připojují k internetu, aby získali přístup k informacím pro různé účely, včetně učení, zábavy, zpráv a sdílení pokroku ve svých životech’ s přáteli. Proto je při nasazování aplikace ve vašem nejlepším zájmu implementovat vysoce bezpečnou a škálovatelnou infrastrukturu pro vaši aplikaci. Cloud nabízí různé způsoby, jak zabezpečit a škálovat Django aplikaci. Horizontální škálování je jednou z metod, která vám umožní spustit několik kopií vaší aplikace. To zajišťuje, že je odolnější vůči chybám a vysoce dostupná. Zvyšuje to také její výkon pro zpracování více požadavků současně.
Horizontální škálování aplikace Django
Aplikaci Django můžete horizontálně škálovat zprovozněním několika aplikačních serverů, na kterých běží aplikace Django a její WSGI HTTP server (jako je Gunicorn nebo uWSGI). Poté budete muset nastavit infrastrukturu pro distribuci příchozích požadavků mezi tyto aplikační servery. Load balancer a reverzní proxy jako Nginx mohou vaší infrastruktuře pomoci s distribucí provozu. Nginx může nasadit SSL certifikáty zajišťující zabezpečená připojení k vaší aplikaci prostřednictvím HTTPS. V neposlední řadě může Nginx také poskytovat ukládání statického obsahu do mezipaměti, aby se minimalizovalo zatížení vašeho serveru.
Konfigurace těchto různých komponent samostatně a zajištění jejich vzájemné komunikace může být skličující úkol. Naštěstí použití Docker zjednodušuje proces konfigurace a zajišťuje, že se různé komponenty chovají stejně bez ohledu na to, kde jsou nasazeny.
Co budete v tomto návodu dělat
V tomto návodu se naučíte, jak horizontálně škálovat kontejnerizovanou aplikaci Django, poskytovanou pomocí WSGI HTTP serveru Gunicorn. Zprovozníte dva aplikační servery, z nichž každý má nainstalovaný Docker a na kterých běží stejná kopie kontejneru aplikace Django a Gunicorn.
Svou aplikaci také zabezpečíte pomocí Let’s Encrypt SSL certifikátu zprovozněním a konfigurací třetího proxy serveru, na kterém poběží kontejner reverzní proxy Nginx a kontejner klienta Certbot. Certbot je balíček, který pomáhá se správou SSL certifikátů od certifikační autority Let’s Encrypt. Získává certifikát, konfiguruje bloky serveru Nginx s umístěním certifikátu a spravuje automatické obnovování. Dělá to tak, že nakonfiguruje úlohu cron, která periodicky kontroluje, zda platnost certifikátu brzy nevyprší a zda je třeba jej obnovit. Udržováním vašeho SSL certifikátu v aktuálním stavu bude mít váš web na SSL Labs.
vždy vysoké hodnocení bezpečnosti. Třetí proxy server stojí před vaší distribuovanou architekturou a přijímá veškerý příchozí externí provoz. Poté distribuuje provoz na vaše aplikační servery. Aplikační servery jsou umístěny za firewallem, který k nim umožňuje přístup pouze proxy serveru.
Tento návod je druhým v sérii tří návodů pro práci s Django, Dockerem a Kubernetes. Nejprve byste měli postupovat podle kroků popsaných v návodu na téma Vytvoření aplikace Django a Gunicorn s Dockerem na Ubuntu. V tomto návodu jsme nastavili základní kód projektu, Dockerfile a připojili aplikaci k MinIo Simple Storage Service (S3) pro poskytování našich statických souborů.
Požadavky
Chcete-li postupovat podle tohoto návodu, budete potřebovat následující:
- Čtyři servery Ubuntu 20.04:
Pokud jste postupovali podle kroků v předchozím návodu, Vytvoření aplikace Django a Gunicorn s Dockerem na Ubuntu, již máte dva ze čtyř serverů:
-
Na prvním serveru poběží instance databáze PostgreSQL. Postupujte podle kroků 1 a 2 v návodu: Vytvoření aplikace Django a Gunicorn s Dockerem na Ubuntu pro nastavení databáze. Konfigurace Postgres by měly být upraveny tak, aby umožňovaly externí připojení pouze z IP adres vašich aplikačních serverů.
-
The Druhý a třetí server budou hostovat kontejnery pro kód vaší aplikace. Druhý server by vám již měl běžet z předchozího návodu. Budeme upravovat jeho firewall tak, aby povoloval externí připojení pouze z IP adresy proxy serveru. Můžete postupovat podle kroků 1 až 4 tohoto podrobného návodu, který vám pomůže nastavit váš server Ubuntu na CloudSigma.
-
Ten čtvrtý server bude proxy server zajišťující vyrovnávání zátěže a distribuci provozu do dvou kontejnerů aplikačních serverů.
-
Docker by měl být nainstalován na obou aplikačních serverech a na proxy serveru.
Po provedení kroků v úvodním návodu, byste již měli mít Docker nainstalovaný na jednom ze serverů. Můžete postupovat podle kroků 1, 2 a 3 v našem návodu na instalaci a provozování Dockeru. Nezapomeňte přidat výše vytvořeného sudo uživatele do skupiny Docker.
- Získejte registrovaný doménový název a nastavte jeho DNS záznamy tak, aby ukazovaly na proxy serveru. Pro demonstrační účely budeme používat example_domain.com.
-
Nastavte službu objektového úložiště S3. Jako službu úložiště jsme v úvodním návodu použili MinIO. Proto postupujte podle vysvětlení v Kroku 5 v úvodním návodu pro nastavení vašeho MinIO úložného bucketu.
Krok 1: Ověření funkčnosti prvního aplikačního serveru Django
Jak je vysvětleno v části Požadavky, tato příručka navazuje na návod na Sestavení aplikace Django a Gunicorn s Dockerem na Ubuntu. Pokud přicházíte z tohoto návodu a již jste tyto kroky implementovali, měli byste mít spuštěný první server. Kód aplikace vychází z dokumentace Django k návodu na aplikaci Polls. Je důležité, abyste si tyto kroky prostudovali, abyste porozuměli výchozímu nastavení. Pokud jste kroky v návodu implementovali, můžete tento první krok přeskočit.
V opačném případě můžete do svého serveru jednoduše naklonovat dockerizovanou větev. Začněte přihlášením k prvnímu aplikačnímu serveru a spusťte následující příkaz git pro naklonování větvě django-polls-docker z repozitáře django-polls:
|
1 |
git clone --single-branch --branch django-polls-docker --depth 1 https://github.com/jaymoh/django-polls.git |
Dále přejděte do adresáře django-polls :
cd django-polls
V tomto adresáři najdete soubor Dockerfile používaný Dockerem k sestavení obrazu aplikace, adresář django-polls obsahující kód aplikace v Pythonu a soubor env obsahující seznam proměnných prostředí, které budou předány do kontejneru při spuštění za účelem úpravy jeho chování. V souboru Dockerfile, definujeme závislosti balíčků Django prostřednictvím souboru requirements.txt . Kromě toho musíme deklarovat port 8000 pro příjem příchozího provozu a nastavit jej pro spuštění serveru gunicorn se 3 workery. Chcete-li se dozvědět více o instrukcích v Dockerfile, podívejte se na Krok 7 v návodu Sestavení aplikace Django a Gunicorn s Dockerem na Ubuntu.
Obraz Dockeru můžete sestavit pomocí příkazu:
docker build -t django-polls:v1 .
Po sestavení obrazu Dockerem můžete vypsat dostupné obrazy na serveru pomocí následujícího příkazu:
docker images
Zde je výstup po spuštění příkazu:
Dále musíme upravit soubor env používaný ke konfiguraci běhového prostředí. Tento soubor se předává do kontejneru Docker při jeho spouštění. Otevřete soubor env v editoru nano:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
DJANGO_SECRET_KEY=your_secret_key DEBUG= DJANGO_LOGLEVEL=info DJANGO_ALLOWED_HOSTS=your_server_IP_address DB_ENGINE=postgresql_psycopg2 DB_DATABASE=polls_db DB_USERNAME=hackins DB_PASSWORD=your_database_password DB_HOST=your_database_host DB_PORT=your_database_port STATIC_DEFAULT_FILE_STORAGE=storages.backends.s3boto3.S3Boto3Storage STATIC_MINIO_BUCKET_NAME=test-bucket MINIO_ACCESS_KEY=your_minio_access_key MINIO_SECRET_KEY=your_minio_secret_key MINIO_URL=your_minio_url:your_minio_port |
Soubor env obsahuje zástupný text, který musíte upravit a vyplnit správnými hodnotami:
-
DJANGO_SECRET_KEY: Vygenerujte jedinečnou, nepředvídatelnou hodnotu, jak je vysvětleno v Django docs. K vygenerování náhodného řetězce a jeho nastavení do proměnné můžete použít tento příkaz: python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'.
-
DJANGO_ALLOWED_HOSTS: tato hodnota se používá k zabezpečení aplikace před útoky typu HTTP Host Header. Můžete ji nastavit na *, což je zástupný znak odpovídající všem hostitelům ve vývojovém režimu. Při nasazení aplikace do produkčního prostředí ji nastavte na svůj registrovaný název domény. Pro naši ukázku je to example_domain.com.
-
DB_DATABASE: nastavte tuto hodnotu na název databáze PostgreSQL, kterou jste vytvořili v části Prerequisites, v našem případě je to polls_db.
-
DB_USERNAME: nastavte tuto hodnotu na uživatelské jméno, které jste zvolili pro svou databázi.
-
DB_PASSWORD: nastavte tuto hodnotu na heslo, které jste zvolili pro svou databázi.
-
DB_HOST: nastavte tuto hodnotu na hostitele, na kterém běží vaše databázová instance, jak jste ji nastavili v části Prerequisites. To je vysvětleno v krocích 1 a 2 návodu Building a Django and Gunicorn Application with Docker on Ubuntu pro nastavení databáze.
-
DB_PORT: nastavte tuto hodnotu na port vaší databáze.
Po dokončení úprav soubor uložte a zavřete. S nastavenými přihlašovacími údaji k databázi můžeme vytvořit schéma databáze spuštěním kontejneru a přepsáním příkazu CMD nastaveného v Dockerfile. Více informací naleznete v Dockerfile entry point from official Docs. Poté spusťte následující příkaz:
|
1 |
docker run --env-file env django-polls:v1 sh -c "python manage.py makemigrations && python manage.py migrate" |
V tomto příkazu spouštíme obraz django-polls:v1 a předáváme mu dříve upravený soubor env. Část: sh -c "python manage.py makemigrations && python manage.py migrate” vytvoří databázové schéma definované kódem aplikace. Pokud příkaz spouštíte poprvé, měli byste vidět podobný výstup indikující vytvoření databázového schématu:
Jakmile máme schéma vytvořené, můžeme vytvořit superuživatele Django. Spuštěním následujícího příkazu spusťte kontejner s interaktivním shellem:
|
1 |
docker run -i -t --env-file env django-polls:v1 sh |
Příkaz spustí kontejner s příkazovým řádkem shellu, který můžete použít k interakci s shellem Pythonu. Vytvořme uživatele pomocí následujícího příkazu:
|
1 |
python manage.py createsuperuser |
Postupujte podle výzev a zadejte uživatelské jméno, e-mailovou adresu a heslo. Znovu zadejte heslo a stisknutím klávesy Enter uživatele vytvořte. Ukončete shell a ukončete kontejner stisknutím CTRL+D.
Dále musíme kontejner spustit znovu a přepsat výchozí příkaz příkazem Django collectstatic Django command. Tento příkaz vygeneruje statické soubory pro aplikaci a nahraje je do cloudového úložiště MinIO:
|
1 |
docker run --env-file env django-polls:v1 sh -c "python manage.py collectstatic --noinput" |
Příkaz vygeneruje a nahraje soubor do vaší nakonfigurované služby objektového úložiště. Zde je výstup:
Nyní můžete aplikaci spustit bez zadání jakéhokoli dalšího příkazu, který by přepsal výchozí příkaz CMD definovaný v Dockerfile:
|
1 |
docker run --env-file env -p 80:8000 django-polls:v1 |
Docker spustí výchozí příkaz definovaný v Dockerfile, spustí kontejner se serverem gunicorn, vystaví port kontejneru 8000 a namapuje jej na port Ubuntu 80. Nyní si můžete zobrazit rozhraní aplikace ve svém prohlížeči zadáním IP adresy prvního serveru do adresního řádku: http://FIRST_SERVER_IP.
Zobrazí se vám 404 Stránka nenalezena, protože jsme nedefinovali nic pro / cestu. Přejděte na http://FIRST_SERVER_IP/polls pro zobrazení rozhraní Polls:
Navštivte administrátorské rozhraní a vytvořte nějaké ankety: http://FIRST_SERVER_IP/admin:
Zadejte přihlašovací údaje, které jste nastavili pomocí příkazu createsuperuser výše, abyste získali přístup k administrátorskému rozhraní:
Pokud si zobrazíte zdrojový kód stránky, všimnete si, že statické soubory jsou stahovány z úložného bucketu, jak bylo definováno. Po ověření, že kontejner obsluhuje aplikaci podle očekávání, můžete kontejner ukončit stisknutím CTRL+C v terminálu.
Dále musíme nechat kontejner běžet v detached režimu, abychom mohli ukončit SSH relaci prvního serveru. Tím zůstane kontejner běžet na pozadí. Spusťte následující příkaz:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
Příznak -d spustí kontejner v detached režimu, takže může zůstat běžet na pozadí. Příznak --rm vyčistí souborový systém kontejneru po jeho ukončení. Kontejner pojmenujeme polls, abychom jej viděli při výpisu kontejnerů.
Ukončete SSH relaci vašeho prvního serveru a přejděte na http://FIRST_SERVER_IP/polls ve vašem prohlížeči, abyste potvrdili, že běží podle očekávání. Pokud vidíte rozhraní anket, pak byl váš první aplikační server úspěšně nastaven. V dalším kroku nastavíme druhý aplikační server.
Krok 2: Nastavení druhého aplikačního serveru
Budeme klonovat dockerizovanou větev aplikace, kterou jsme vytvořili v návodu Building a Django and Gunicorn Application with Docker on Ubuntu . Podrobnější informace o příkazech, které zde použijeme, naleznete v tomto návodu, nebo v shrnuté verzi v Kroku 1.
Měli byste mít spuštěný druhý server, přidaného uživatele sudo bez oprávnění root a nainstalovaný Docker, jak je vysvětleno v části Požadavky.
Dalším krokem je konfigurace tohoto serveru pro připojení k instanci serveru PostgreSQL. Jak je vysvětleno v kroku 1 návodu Building a Django and Gunicorn Application with Docker on Ubuntu, musíte povolit IP adresu druhého serveru v konfiguraci ufw a PostgreSQL.
Nejprve se přihlaste k instanci databázového serveru PostgreSQL pomocí svého uživatele sudo bez oprávnění root. Chcete-li přidat pravidlo ufw, spusťte následující příkaz:
|
1 |
sudo ufw allow from SECOND_SERVER_IP to any port 5432 |
Dále spusťte tento příkaz a přidejte IP adresu druhého serveru do souboru pro ověřování klientů PostgreSQL:
|
1 |
sudo nano /etc/postgresql/12/main/pg_hba.conf |
Přečtěte si komentáře, abyste lépe porozuměli konfiguraci. Dále přidejte tento řádek pod sekci hosts a uveďte svou IP adresu:
|
1 |
host all all SECOND_SERVER_IP/24 md5 |
Po dokončení úprav soubor uložte a zavřete.
Poté restartujte službu PostgreSQL, aby se změny projevily:
|
1 |
sudo service postgresql restart |
Odhlaste se z instance databázového serveru PostgreSQL a pokračujte v konfiguraci druhé instance aplikačního serveru.
Přihlaste se k druhému aplikačnímu serveru pomocí ssh. Poté naklonujte django-polls- větev repozitáře django-polls pomocí následujícího příkazu:
|
1 2 |
git clone --single-branch --branch django-polls-docker --depth 1 https://github.com/jaymoh/django-polls.git |
Přejdete do adresáře django-polls:
|
1 |
cd django-polls |
Poté sestavte obraz pomocí následujícího příkazu:
|
1 |
docker build -t django-polls:v1 . |
Jakmile je proces sestavení obrazu dokončen, upravte soubor env s konfiguračními hodnotami, jak je vysvětleno v Kroku 1. Otevřete soubor pomocí nano:
|
1 |
nano env |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
DJANGO_SECRET_KEY=váš_tajný_klíč DEBUG= DJANGO_LOGLEVEL=info DJANGO_ALLOWED_HOSTS=IP_adresa_vašeho_serveru DB_ENGINE=postgresql_psycopg2 DB_DATABASE=polls_db DB_USERNAME=hackins DB_PASSWORD=heslo_vaší_databáze DB_HOST=hostitel_vaší_databáze DB_PORT=port_vaší_databáze STATIC_DEFAULT_FILE_STORAGE=storages.backends.s3boto3.S3Boto3Storage STATIC_MINIO_BUCKET_NAME=test-bucket MINIO_ACCESS_KEY=váš_přístupový_klíč_minio MINIO_SECRET_KEY=váš_tajný_klíč_minio MINIO_URL=vaše_url_minio:váš_port_minio |
Nahraďte zástupné texty skutečnými hodnotami, které jste přidali v Kroku 1. Nezapomeňte odpovídajícím způsobem upravit proměnnou DJANGO_ALLOWED_HOSTS . Jakmile budete hotovi, soubor uložte a zavřete. Aktualizujte své přihlašovací údaje k MinIO v souboru env stejně jako v předchozím kroku.
Nyní můžete spustit kontejner aplikace v odpojeném režimu (detached mode) pomocí následujícího příkazu:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
Tento příkaz spustí kontejner a nechá ho běžet na pozadí. Ukončete relaci ssh druhého aplikačního serveru a přejděte na http://SECOND_SERVER_IP/polls ve vašem prohlížeči, abyste potvrdili, že běží podle očekávání. Pokud vše proběhlo v pořádku, měli byste vidět rozhraní anket (polls).
Nyní máte dva aplikační servery, na kterých běží stejná kopie vaší aplikace. V dalším kroku nakonfigurujete kontejner Nginx tak, aby sloužil jako reverzní proxy.
Krok 3: Nastavení Docker kontejneru Nginx
Nginx je jedním z nejpopulárnějších open-source webových serverů na světě. Je zodpovědný za zajištění dostupnosti a škálovatelnosti webů s nejvyšším provozem na internetu. Zaručuje bezpečnost a je velmi univerzální. Můžete jej použít pro reverzní proxy, ukládání do mezipaměti (caching) a vyvažování zátěže (load balancing). Naši aplikaci jsme nastavili tak, aby pro správu statických a mediálních souborů využívala samostatnou službu objektového úložiště. Proto nebudeme používat funkce Nginx pro ukládání do mezipaměti. Místo toho využijeme schopnosti Nginx pro reverzní proxy a vyvažování zátěže. Server Nginx na front-endu bude přijímat příchozí provoz a distribuovat jej na backendové aplikační servery. Poté zajistí bezpečnou komunikaci mezi klientem a serverem zabezpečením provozu pomocí SSL certifikátů získaných od Let’s Encrypt.
Existuje několik způsobů, jak implementovat reverzní proxy a vyvažování zátěže Nginx. Jedním ze způsobů je nastavení reverzní proxy Nginx odděleně od backendového aplikačního serveru, jak jsme to udělali v tomto návodu. Toto nastavení je flexibilní a umožňuje škálovat jak vrstvu proxy Nginx, tak aplikační vrstvu. Můžete přidat více proxy serverů Nginx nebo implementovat cloudový nástroj pro vyvažování zátěže. Dalším způsobem implementace reverzní proxy je použití jednoho z backendových aplikačních serverů jako proxy Nginx. Poté můžete proxy příchozí požadavky lokálně i na ostatní aplikační servery. Volitelně můžete nakonfigurovat kontejner Nginx na všech backendových aplikačních serverech a nastavit cloudový nástroj pro vyvažování zátěže na front-endu, který bude přijímat příchozí provoz a distribuovat jej na backendové aplikační servery.
Začněme konfigurovat proxy server. Přihlaste se ke čtvrtému serveru Ubuntu, který jste určili pro použití jako proxy Nginx, a vytvořte konfigurační adresář:
|
1 |
mkdir conf |
Otevřete konfiguraci pomocí nano uvnitř adresáře:
|
1 |
nano conf/nginx.conf |
Dále do souboru přidejte následující konfiguraci:
|
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 |
upstream django { server FIRST_SERVER_IP; server SECOND_SERVER_IP; } server { listen 80 default_server; return 444; } server { listen 80; listen [::]:80; server_name example_domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example_domain.com; # SSL ssl_certificate /etc/letsencrypt/live/example_domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example_domain.com/privkey.pem; ssl_session_cache shared:le_nginx_SSL:10m; ssl_session_timeout 1440m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; 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"; client_max_body_size 4G; keepalive_timeout 5; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://django; } location ^~ /.well-known/acme-challenge/ { root /var/www/html; } } |
V tomto konfiguračním souboru specifikujeme bloky server, upstream, a location které Nginx instruují k přesměrování HTTP požadavků na HTTPS a k distribuci požadavků mezi dva aplikační servery, které jsme nastavili v kroku 1 a kroku 2. Obecné informace o struktuře konfiguračního souboru Nginx naleznete v jejich oficiální dokumentaci.
Prostudovali jsme konfigurační soubory poskytované Docker Hubem v dokumentaci k obrazu Nginx, Certbot, a Gunicorn k vytvoření tohoto minimálního konfiguračního souboru Nginx. I když je to pouze pro demonstrační účely a zprovoznění našeho nastavení, můžete volně zkoumat a experimentovat s dalšími konfiguracemi podle návodů pro Nginx.
Blok upstream se používá k definování skupiny serverů, které budou zpracovávat příchozí požadavky. Skupině se přiřadí název a je volán direktivou proxy_pass. Blok jsme pojmenovali jako django a specifikovali IP adresy dvou aplikačních serverů v backendu:
|
1 2 3 4 |
upstream django { server FIRST_SERVER_IP; server SECOND_SERVER_IP; } |
Definovali jsme také 3 bloky server. První blok server zachycuje všechny požadavky, které neodpovídají vaší doméně, a vrací 444 kód (uzavře spojení bez odeslání odpovědi klientovi, čímž odmítne škodlivé nebo chybně formátované požadavky). Přímý HTTP požadavek na IP adresu vašeho serveru je zpracován tímto blokem, protože je definován jako default_server:
|
1 2 3 4 |
server { listen 80 default_server; return 444; } |
Ten druhý serverový blok zpracovává příchozí HTTP požadavky (port 80) a přesměrovává je na HTTPS (port 443) pomocí přesměrování HTTP 301:
|
1 2 3 4 5 6 |
server { listen 80; listen [::]:80; server_name example_domain.com; return 301 https://$server_name$request_uri; } |
Třetí serverový blok nyní zpracovává požadavky. Má několik direktiv a jejich význam si definujeme níže.
Máme dvě direktivy definující cesty k TLS certifikátu a klíči, jak je poskytuje Certbot. Certifikáty jsou připojeny do kontejneru Nginx při jeho spuštění:
|
1 2 3 |
# SSL ssl_certificate /etc/letsencrypt/live/example_domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example_domain.com/privkey.pem; |
Dále zde máme výchozí nastavení zabezpečení SSL doporučené nástrojem Certbot. Více se dozvíte v oficiální dokumentaci Nginx o modulu ngx_http_ssl_module. Mozilla také nabízí další informace o zabezpečení na straně serveru. Hodnota ssl_ciphers v souboru conf je převzata ze stránky společnosti Mozilla:
|
1 2 3 4 5 6 |
ssl_session_cache shared:le_nginx_SSL:10m; ssl_session_timeout 1440m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; 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"; |
V následujících dvou direktivách definujeme maximální povolenou velikost těla požadavku klienta a nastavíme časový limit pro keep-alive spojení s klientem. Nginx uzavře spojení s klientem po uplynutí počtu sekund, které nastavíte v direktivě keepalive_timeout. Více informací o konfiguracích Nginx pro nasazení Gunicornu naleznete v oficiální dokumentaci:
|
1 2 |
client_max_body_size 4G; keepalive_timeout 5; |
V konfiguračním souboru máme také definovány dva bloky location. První blok zpracovává proxy serverování požadavků, jak je definováno pomocí proxy direktiv. Příchozí požadavky jsou směrovány na upstream django servery definované dříve:
|
1 2 3 4 5 6 7 |
location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://django; } |
Více informací o proxy direktivách naleznete v Nginx Module ngx_http_proxy_module a v dokumentaci k nasazení serveru Gunicorn.
In the second location block, we define a path: /well-known/acme-challenge/. Obvykle ji používá Certbot k ověření vašeho doménového jména u Let’s Encrypt před vystavením nebo obnovením SSL certifikátu:
|
1 2 3 |
location ^~ /.well-known/acme-challenge/ { root /var/www/html; } |
To je pro konfigurační soubor Nginx vše. Po dokončení úprav můžete soubor uložit a zavřít.
Právě definovaný konfigurační soubor lze použít ke spuštění kontejneru Nginx. To však selže, protože jsme dosud nezajistili SSL certifikáty od Let’s Encrypt. V tomto návodu použijeme nginx:1.20.2 verzi Docker obrazu 1.20.2 z oficiálního registru obrazů Nginx na Docker Hubu.
Spuštěním níže uvedeného příkazu můžete stáhnout obraz a ověřit, zda vše funguje správně:
|
1 2 3 4 |
docker run --rm --name nginx -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
Tento příkaz vytvoří kontejner s názvem nginx a mapuje porty 80 a 443 mezi hostitelským systémem a kontejnerem. Příznak --rm odstraní po úspěšném sestavení všechny dočasné kontejnery. Používáme příznak -v k připojení konfiguračního souboru do kontejneru v /etc/nginx/conf.d/nginx.conf, což je výchozí adresář pro konfigurace Nginx. Je připojen v režimu pouze pro čtení pomocí příznaku ro, aby se zabránilo kontejneru Nginx v jeho úpravách. Nastavíme výchozí adresář webroot a připojíme jej jako /var/www/html. Na závěr instruujeme Docker, aby pro toto sestavení použil obraz nginx:1.20.2 pro toto sestavení. V dalším kroku získáme TLS/SSL certifikát a klíč od Let’s Encrypt.
Krok 4: Zajištění SSL/TLS certifikátu od Let’s Encrypt a konfigurace automatického obnovování Certbotu
Certbot pomáhá zajišťovat bezplatné TLS certifikáty od Let’s Encrypt a také spravovat jejich automatické obnovování před vypršením platnosti. To zvyšuje bezpečnost vašich webových stránek a zajišťuje jejich poskytování přes HTTPS. V souladu s udržováním naší architektury v kontejnerech budeme k získání SSL/TLS certifikátů a konfiguraci automatického obnovování používat Docker obraz Certbotu. Ujistěte se, že máte Docker nainstalovaný na vašem proxy serveru podle pokynů v části Požadavky.
Měli byste také mít DNS záznam typu A pro vaši registrovanou doménu ukazující na IP adresu vašeho proxy serveru. Ověření můžete provést spuštěním Docker obrazu certbot a předáním příznaku --staging:
|
1 2 3 4 |
docker run -it --rm -p 80:80 --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ certbot/certbot certonly --standalone --staging -d example_domain.com |
Tento příkaz stáhne obraz Certbotu a spustí jej v interaktivním režimu. To znamená, že bude spuštěn s shellem, což vám umožní zadat některé údaje. Mapuje port 80 hostitele na port 80 uvnitř kontejneru. Používáme příznak -v k připojení dvou adresářů hostitele do kontejneru: /etc/letsencrypt/ a /var/lib/letsencrypt/. Příznak --standalone příznak určuje, že chceme, aby obraz Certbot běžel bez použití Nginx. Nakonec zde máme --staging příznak, který spustí Certbot na staging serverech a ověří váš název domény.
Zadejte svou e-mailovou adresu a přijměte Podmínky služby po zobrazení výzvy. Následuje výstup úspěšného ověření:
|
1 2 3 4 5 6 7 |
Účet registrován. Žádost o a certifikát pro example_domain.com Úspěšně přijat certifikát. Certifikát je uložen v: /etc/letsencrypt/live/example_domain.com/fullchain.pem Klíč je uložen v: /etc/letsencrypt/live/example_domain.com/privkey.pem Tento certifikát vyprší dne 2022-04-29. Tyto soubory budou be aktualizovány, když se certifikát obnovírenews. |
DALŠÍ KROKY:
Certifikát bude nutné obnovit před vypršením jeho platnosti. Certbot může certifikát automaticky obnovovat na pozadí, ale možná budete muset provést kroky k povolení této funkce. Podívejte se na tento odkaz pro instrukce.
Certifikát si můžete prohlédnout pomocí příkazu cat:
|
1 |
sudo cat /etc/letsencrypt/live/example_domain.com/fullchain.pem |
Výše uvedený příkaz by měl zobrazit váš certifikát v terminálu. Jakmile potvrdíte, že Certbot váš certifikát zprovoznil, můžete nyní otestovat konfiguraci Nginx, kterou jste vytvořili v kroku 3. Spuštěním níže uvedeného příkazu Docker spusťte kontejner Nginx:
|
1 2 3 4 5 6 |
docker run --rm --name nginx -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /etc/letsencrypt:/etc/letsencrypt \ -v /var/lib/letsencrypt:/var/lib/letsencrypt \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
V tomto příkazu jsme použili příznak -v k připojení umístění adresářů s certifikáty SSL/TLS od Let’s Encrypt.
Jakmile je kontejner spuštěn a běží, otevřete webovou stránku v prohlížeči: http://example_domain.com. Pravděpodobně uvidíte varování, že web není zabezpečený:
Je to proto, že jsme zprovoznili pouze staging/testovací certifikáty a nikoli produkční certifikáty od Let’s Encrypt. Získejme produkční certifikáty spuštěním následujícího příkazu Certbot bez příznaku --staging příznaku:
|
1 2 3 4 |
docker run -it --rm -p 80:80 --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ certbot/certbot certonly --standalone -d example_domain.com |
Ve výzvě potvrďte, že chcete obnovit a nahradit stávající certifikát zadáním 2 a stiskněte ENTER. Tím by se měl zprovoznit certifikát připravený pro produkci. Nyní můžete spustit kontejner Nginx a vše by mělo fungovat správně:
|
1 2 3 4 5 6 |
docker run --rm --name nginx -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /etc/letsencrypt:/etc/letsencrypt \ -v /var/lib/letsencrypt:/var/lib/letsencrypt \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
Jakmile kontejner běží, otevřete znovu webovou stránku v prohlížeči: http://example_domain.com. Všimněte si, že váš prohlížeč je přesměrován na HTTPS, i když zadáte HTTP. To znamená, že náš server v konfiguraci Nginx i zprovozněné certifikáty SSL/TLS fungují správně. Přejděte na trasu polls route http://example_domain.com/polls, protože nemáme definovanou trasu pro domovskou cestu /. Měli byste vidět rozhraní anket:
Dosud jste úspěšně nakonfigurovali architekturu připravenou pro produkční nasazení. Implementovali jste dva backendové servery, které budou zpracovávat příchozí požadavky předávané z proxy serveru. Proxy server se postará o vyrovnávání zátěže a zabezpečení provozu pomocí poskytnutých certifikátů TLS.
Měli byste však mít na paměti, že platnost certifikátů Let’s Encrypt vyprší za 90 dní. Proto byste je měli obnovit před uplynutím této 90denní lhůty. Vzhledem k tomu, že kontejner Nginx bude spuštěn, měli byste při spuštění příkazu certbot pro obnovení certifikátu použít režim webroot namísto režimu standalone při spouštění příkazu certbot pro obnovu certifikátu. Nezapomeňte, že jste specifikovali adresář /var/www/html/.well-known/acme-challenge/ v konfiguračním souboru Nginx v Kroku 3. Certbot použije tuto cestu k uložení ověřovacích souborů. Klient Let’s Encrypt bude také volat tuto cestu s ověřovacími požadavky, když se pokusíte obnovit certifikáty. Jakmile se příkaz pro obnovení dokončí, můžete Nginx znovu načíst, aby se změny projevily.
Ukončete kontejner stisknutím CTRL+C ve vašem terminálu a spusťte jej znovu na pozadí (v odpojeném režimu) s příznakem -d flag:
|
1 2 3 4 5 6 |
docker run --rm --name nginx -d -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /etc/letsencrypt:/etc/letsencrypt \ -v /var/lib/letsencrypt:/var/lib/letsencrypt \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
Tím zůstane kontejner Nginx spuštěný na pozadí. Otestujme postup obnovení certifikátu s příznakem --dry-run spuštěním níže uvedeného příkazu:
|
1 2 3 4 5 |
docker run -it --rm --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ -v "/var/www/html:/var/www/html" \ certbot/certbot renew --webroot -w /var/www/html --dry-run |
V tomto příkazu jsme specifikovali plugin --webroot a také cestu, která se má použít pro ověřovací požadavky pomocí příznaku -w. Dále specifikujeme příznak --dry-run pro ověření postupu automatického obnovení, aniž by se certifikát skutečně vystavil.
Při úspěšné simulaci byste měli vidět podobný výstup:
|
1 2 3 4 5 6 7 8 |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/example_domain.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Simulating renewal of an existing certificate for example_domain.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/hackinroms.com/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
Kdykoli obnovíte certifikát pro spuštěnou aplikaci, musíte znovu načíst Nginx, aby kontejner začal používat nový certifikát. Následující příkaz Dockeru znovu načte kontejner nginx (nezapomeňte, že jsme kontejner pojmenovali jako nginx):
|
1 |
docker kill -s HUP nginx |
Příkaz odešle unixový signál HUP procesu Nginx běžícímu uvnitř Docker kontejneru nginx. To způsobí, že Nginx znovu načte své konfigurace a začne používat obnovené certifikáty.
Vzhledem k tomu, že máme na našem proxy serveru nainstalované TLS/SSL a náš web je poskytován přes HTTPS, musíme nyní zabezpečit naše backendové aplikační servery tak, aby povolovaly požadavky pouze z proxy serveru.
Krok 5: Zabezpečení backendových serverů Django před externím přístupem
Proxy server, který jste v tomto návodu implementovali, zajišťuje ukončení SSL (SSL termination), kdy dešifruje SSL připojení a předává nešifrované pakety aplikačním serverům v backendu. Vzhledem k tomu, že budeme backendové servery zabezpečovat proti jakémukoli externímu přístupu, měla by tato úroveň zabezpečení ve většině případů stačit. Pokud však nasazujete aplikace, které přenášejí citlivá data, jako jsou bankovní informace nebo zdravotní údaje, měli byste implementovat end-to-end šifrování.
V tomto návodu jsou servery Gunicorn v backendu chráněny Nginxem, protože nemají být přístupné přímo zvenčí. Proxy server Nginx funguje jako brána k backendovým serverům a brání externím klientům v přímém přístupu k backendovým aplikačním serverům. Měli byste zajistit, aby všechny požadavky procházely přes proxy server. Vzhledem k tomu má Docker problém, kdy obchází ufw pravidla firewallu a otevírá porty navenek, což může nechat vaši infrastrukturu nezabezpečenou. To je zřejmé z toho, že jsme naše aplikační servery nastavili v Kroku 1 a Kroku 2 bez povolení portu 80 v pravidlech ufw. Přesto však můžete k webovým stránkám přistupovat, když v prohlížeči navštívíte veřejnou IP adresu kteréhokoli ze serverů. Jedním ze způsobů, jak tento problém vyřešit, je použít iptables přímo, aniž byste procházeli přes ufw. Více informací naleznete v oficiální dokumentaci k Docker a iptables. Dalším doporučeným způsobem je použití cloudových firewallů.
Pojďme upravit konfiguraci UFW tak, abychom zablokovali externí přístup ke všem portům, které mohl Docker otevřít. Když jsme namapovali port hostitele 80 na port kontejneru Dockeru 8000 pomocí příznaku -p 80:8000 v příkazu Dockeru, neúmyslně jsme také otevřeli port 80 na hostitelském stroji. Tento přístup můžete zakázat úpravou konfigurace UFW, jak je popsáno v souboru README v repozitáři ufw-docker.
Pojďme to změnit pro první aplikační server Django. Přihlaste se k serveru a otevřete soubor v /etc/ufw/after.rules pomocí nano jako uživatel sudo:
|
1 |
sudo nano /etc/ufw/after.rules |
Soubor obsahuje následující pravidla ufw:
|
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 |
# # rules.input-after # # Pravidla, která by měla být spuštěna po pravidlech přidaných příkazovým řádkem ufw. Vlastní # pravidla by měla být přidána do jednoho z těchto řetězců: # ufw-after-input # ufw-after-output # ufw-after-forward # # Nemazejte tyto vyžadované řádky, jinak dojde k chybám *filter :ufw-after-input - [0:0] :ufw-after-output - [0:0] :ufw-after-forward - [0:0] # Konec vyžadovaných řádků # ve výchozím nastavení neprotokolovat hlučné služby -A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input -A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input -A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input -A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input -A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input -A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input # neprotokolovat hlučné broadcasty -A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input # nemazejte řádek 'COMMIT', jinak tato pravidla nebudou zpracována COMMIT |
Na konec souboru přidejte následující blok konfiguračních řádků UFW:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# BEGIN UFW AND DOCKER *filter :ufw-user-forward - [0:0] :DOCKER-USER - [0:0] -A DOCKER-USER -j RETURN -s 10.0.0.0/8 -A DOCKER-USER -j RETURN -s 172.16.0.0/12 -A DOCKER-USER -j RETURN -s 192.168.0.0/16 -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN -A DOCKER-USER -j ufw-user-forward -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12 -A DOCKER-USER -j RETURN COMMIT # END UFW AND DOCKER |
Pravidla, která jste přidali, zabraňují veřejnému přístupu k portům, které Docker otevírá. Dále umožňují přístup z 10.0.0.0/8, 172.16.0.0/12, a 192.168.0.0/16 soukromých rozsahů IP adres. Více podrobností o těchto pravidlech si můžete přečíst v ufw-docker README. Po dokončení úprav soubory uložte a zavřete. Toto nastavení by mělo fungovat, pokud byste měli nastavenou virtuální privátní síť (VPC) se všemi třemi servery ve VPC a poté byste specifikovali soukromé IP adresy Django serverů v direktivě upstream v Nginxu konfiguračním souboru.
My jsme však použili veřejné IP adresy a VPC možná nemáme. Proto musíte přidat pravidlo do ufw, které povolí provoz z proxy serveru Nginx přes port 80 obou aplikačních serverů Django. Do ufw můžete přidat povolující pravidlo určující výchozí IP adresu serveru pro port 80 pomocí následujícího příkazu:
|
1 |
sudo ufw allow from NGINX_PROXY_IP to any port 80 |
Jakmile dokončíte úpravy, restartujte aplikační server Django, aby se změny projevily, protože se zdá, že spuštění sudo ufw reload změny neaplikuje:
|
1 |
sudo reboot |
Po restartu serveru spusťte kontejner stejně jako v Kroku 1 nebo Kroku 2:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
Dále zkuste v prohlížeči navštívit IP adresu prvního serveru Django a ověřte, zda se zobrazí rozhraní Polls: http://FIRST_SERVER_IP/polls. To selže. Nyní se odhlaste z prvního serveru a opakujte kroky, které jste zde provedli, pro druhý server. Otevřete /etc/ufw/after.rules pomocí nano jako uživatel sudo:
|
1 |
sudo nano /etc/ufw/after.rules |
Stejně jako předtím přejděte dolů a přidejte konfigurační blok UFW:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# BEGIN UFW AND DOCKER *filter :ufw-user-forward - [0:0] :DOCKER-USER - [0:0] -A DOCKER-USER -j RETURN -s 10.0.0.0/8 -A DOCKER-USER -j RETURN -s 172.16.0.0/12 -A DOCKER-USER -j RETURN -s 192.168.0.0/16 -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN -A DOCKER-USER -j ufw-user-forward -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12 -A DOCKER-USER -j RETURN COMMIT # END UFW AND DOCKER |
Jakmile přidáte výše uvedený blok, soubor uložte a zavřete.
Dále přidejte povolující pravidlo do ufw specifikující zdrojovou IP adresu serveru k portu 80 pomocí následujícího příkazu:
|
1 |
sudo ufw allow from NGINX_PROXY_IP to any port 80 |
Restartujte server, aby se změny projevily:
|
1 |
sudo reboot |
Jakmile se server znovu spustí, spusťte kontejner znovu pomocí příkazu:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
Otestujte, zda můžete zobrazit rozhraní anket přechodem přímo na IP adresu druhého serveru: http://SECOND_SERVER_IP/polls. To by mělo také selhat.
Tato architektura je nyní připravena k otestování. Můžete navštívit https://example_domain_here/polls pro zobrazení výchozího rozhraní Polls ve vašem prohlížeči. To znamená, že proxy server Nginx má stále přístup k backendovým serverům Django.
Závěr
V této příručce jsme vám ukázali, jak implementovat škálovatelnou infrastrukturu pomocí kontejnerů Docker. Tato infrastruktura zahrnuje samostatný databázový server PostgreSQL, dva backendové aplikační servery a proxy server Nginx pro vyrovnávání zátěže a distribuci provozu mezi oba servery. Přestože jsme naši aplikaci založili na aplikaci Django Polls, můžete tuto architekturu přizpůsobit pro různé aplikace využívající jiné frameworky, jako je Node.js, Laravel, atd.
Toto je základní návod, který vám pomůže začít. Mezi vylepšení, která můžete přidat, patří hostování vašeho obrazu v repozitáři obrazů, jako je Docker Hub, což umožňuje snadnou distribuci obrazu na více serverů. Můžete také přidat kanály pro průběžnou integraci a nasazení (CI/CD) pro automatické sestavení, testování a nasazení obrazů na aplikační servery, kdykoli dojde k nějaké události. Událostí může být například odeslání nového kódu do určené větve v repozitáři Git. Možná budete chtít také automatizovat, co se stane, když kontejner narazí na chybu. Oficiální dokumentace Dockeru poskytuje dobrý návod na Automatické spouštění kontejnerů v případě chyb nebo restartu systému.
Ať se daří!








Komentáře
Zatím žádné komentáře. Buďte první.