AWS: сетап базової інфраструктури для WordPress
0 (0)

Автор |  11/03/2026
Click to rate this post!
[Total: 0 Average: 0]

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

Цього разу планую переїжджати з DigitalOcean, де RTFM хоститься з 2020 року. До самого DigitalOcean претензій нуль – всі ці роки системи працювали бєз єдіного разриву (с) Антон Уральский, але, раптом, з’ясував, що в мене на AWS накопичилась купа AWS Credits, які мені як AWS Hero видають кожного року. При цьому за хостинг і бекапи в DigitalOcean я плачу живими гривнями і, в принципі, немало – близько 40 баксів в місяць.

Ну і RTFM в AWS вже колись хостився – з 2015, здається, до 2020 року.

Отже, що треба захостити – це невеликий блог WordPress, тому сетап буде без fault tolerance і high availability.

Робити будемо методом “clickops” – без Terraform, просто руками.

Чому без Terraform – бо, по-перше, мені цікаво глянути, що ж нового з’явилось в AWS Console, бо насправді не так часто туди заглядаю і тим більш щось роблю руками. По-друге – просто нема сенсу робити якусь автоматизацію, бо скоріш за все щось буду перероблювати-міняти і потім більше часу витрачу на зміни в коді, ніж на сам сетап. Ну і сама інфраструктура відносно маленька.

А оскільки це чисто особистий проект для хостінгу одного сайту і без всяких Dev/Staging/Prod оточень – то і сенсу тягнути сюди Terraform мало.

Та і насправді коли робив все описане нижче – ловив дуже приємні флешбеки в роки 2015-2016, коли тільки ще знайомився з AWS і мало користувався Terraform.

І навіть є якийсь особливий вайб в тому, щоб самому все створити ручками, а не просто описати в коді Terraforrm resources.

Втім, новий пост напишу, і буде він більше для тих, хто тільки знайомиться з AWS і хоче побачити як можна побудувати базову інфрастуктуру для хостінг веб-сайта – або для тих, хто хочеться трохи ностальгії за тими часами, коли ми не все крутили в Кубернітісах 🙂

Намагався описати максимально стисло – але вийшло багато матеріалу.

Планування архітектури

Чисто базовий етап для майже будь-якого веб-сервісу – базова мережа, один EC2, один RDS, все в одній Availabilty Zone.

На EC2 буде Amazon Linux з NGINX та PHP-FPM, база даних блогу – AWS RDS MariaDB.

Спочатку планував Debian, бо система “поставив і забув”, але її використання в AWS потребує трохи геморою – а Amazon Linux працює просто з коробки.

По ходу діла знайшов непогане порівняння – Amazon Linux vs Debian: What are the differences?

EC2 буде в приватній мережі, без прямого доступу з інтернету, і спочатку для доступу до інстансу думав використати AWS SSM, який насправді ніколи толком не юзав, бо по роботі все в єтіх ваших Кубернетісах, але він прям overkill, потребує достатньо багато додаткових налаштувань – IAM Role та VPC Endpoints, що коштує додаткових грошей, тому все ж вирішив робити через AWS EC2 Instance Connect.

Для доступу до WordPress на EC2 в систему додамо AWS Load Balancer, до якого потім ще можна буде підключити AWS WAF.

І не буде робитись EC2 AutoScaling – бо це теж трошки overkill для маленького блогу. Правда, RDS, у якого мінімум 20 гіг диск при базі RTFM в 1.2 гіга теж таке собі, але нехай буде – подивимось на “традиційний” сетап подібної інфраструктури.

Отже, план такий

  • AWS Availability Zones:
    • всі ресурси (EC2 та RDS) будуть в одній AZ, але мережа має бути мінімум в двох
  • Network – VPC та Subnets:
    • створимо одну AWS VPC з чотирма Subnets у двох Availability Zones:
      • Public Subnets: тут будуть жити сервіси, яким треба мати Public IP – Load Balancer, NAT Gateway
      • Private Subnets: тут буде жити EC2 з WordPress та RDS з MariaDB
    • налаштуємо AWS Load Balancer для доступу до WordPress
  • EC2:
    • один сервер з Amazon Linux та NGINX та PHP-FPM
  • RDS:
    • мінімальний інстанс RDS з MariaDB – буде жити в приватному сабнеті в власною Secuity Group та автоматичними бекапами
  • Route 53:
    • для доступу до бази даних створимо окрему локальну DNS zone, яка буде доступна тільки в межах VPC
  • Security:
    • перший рівень захисту – це мережа: всі робочі ресурси будуть в приватних сабнетах
    • до них додамо Security Groups – для самого EC2, для AWS RDS і для Load Balancer,
    • пізніше можна буде глянути на AWS VPC NACL і потрогати AWS WAF – дуже давно з ним не працював
    • SSH та VPN і доступ до EC2:
      • EC2 Instance Connect для SSH
      • пізніше встановлю WireGuard і підключу до мого домашнього MikroTik та зможу підключатись по SSH вже напряму

І кілька слів по самому AWS.

Перше – вибір регіону: тут в першу чергу звертаємо увагу на локацію і клієнтів – якщо у нас основні клієнти в USA, то, логічно, вибираємо регіони там.

Другий момент, на який звертаємо увагу – ціна, бо в кожному регіоні ціни трохи відрізняються, хоч і не в рази.

Тому для RTFM візьму Ірландію (eu-west-1) – там і тихо (не літають шахеди, як в ОАЕ у 2026 році), і з європейських AWS Regions вона сама дешева.

Поїхали.

AWS Costs

Питання костів, коли працюєш з AWS, актуальне завжди.

Описаний нижче сетап вийшов в 5 USD/day, тобто 150 баксів на місяць – і це ще без урахування трафіку і додаткових сервісів типу бекапів, SSM та WAF.

В кінці буде детальний розбір по костам.

Ну і хоча пост називається “сетап базової інфраструктури” – але за великим рахунком для якогось персонального блогу він дуже overengineered: спокійно можна обійтися без приватних сабнетів, без AWS Apliaction Load Balancer, і навіть без AWS RDS. І якби я робив для RTFM і не мав вільних кредитів – то робив би набагато простіше.

Втім, якщо будувати щось більш “production grade”, то описана нижче інфраструктура як раз і є базовою, або, радше, “традиційною” – з розділенням мережевого доступу, з винесенням баз даних на окремий інстанс, з Load Balancer.

Створення VPC

Починаємо з основи всього – VPC.

VPC дасть нам ізоляцію, дасть можливість отримати доступ до ресурсів в приватних сабнетах, дасть можливість зекономити на трафіку – бо зможемо ходити до ресурсів AWS S3 через внутрішні ендпоінти, а не через інтернет.

Нам треба зробити:

  • Private Subnets: для EC2 та RDS
    • ще можна і бази даних винести в окремі subnets – але це вже точно поза “сетап базової інфраструктури для WordPress”, тому не робимо
  • Public Subnets: для Load Balancer та NAT Gateway

AWS ALB потребує мінімум 2 subnets, тому робити будемо у двох Availability Zones, хоча всі ресурси будуть жити тільки в одній.

Основні налаштування

В панелі створення VPC багато чого змінилось з того часу, як я тут щось робив руками – додалась можливість через “VPC and more” створити відразу все – спробуємо, як це працює.

Єдиний, як на мене, недолік в цій можливості “все і одразу” – не так добре розумієш що і для чого створюється, а створення якихось ресурсів взагалі проходить повз уваги: я, наприклад, тільки через декілька днів згадав, що в AWS VPC для Public Subnets створюється ще і Internet gateway.

Тому якщо вперше знайомишся з AWS і VPC, то в старому підході “робити все руками” все ж є сенс.

Якщо хочеться зробити “по-старому” – то описував цей процес ще у 2016 році, якихось кардинальних змін в побудові нетворку не було:

Автогенерація імен ресурсів – теж прикольна штука, і генерує достатньо адекватні імена як раз в тому стилі, як я це завжди робив – з іменем subnet type та Availability Zone:

Вибір CIDR важливий, особливо, якщо планується мати кілька VPC і між ними будувати “мости” у вигляді VPC Peering – треба розрахувати так, щоб адреси не перетинались.

Крім того, конкретно в моєму кейсі, треба враховувати майбутній VPN, у якого власна мережа для клієнтів – 10.100.0.0/24.

AWS по дефолту пропонує 10.0.0.0/16 – можна так і залишити, хоча, звісно, для такого проекту адрес буде забагато.

Але, головне, щоб ця мережа не перетинається з 10.100.0.0/24, бо в 10.0.0.0/16 входять адреси від 10.0.0.0 до 10.0.255.255.

тут я вже почав писати про розрахунок адрес, але вийшло таке полотенце тексту, що вирішив його винести окремим матеріалом

Отже, залишаємо дефолтний блок 10.0.0.0/16:

IPv6 нам не треба, пропускаємо.

Tenancy – щось на дуже дорогому: можливість запускати всі свої EC2 на виділеному для AWS Account hardware серверах, зараз це точно не треба, див. Amazon EC2 Dedicated Instances.

VPC encryption control – щось нове, дозволяє включити контроль використання plaintext трафіку в мережі, нам не треба, пропускаємо.

Number of Availability Zones задаємо у 2, це мінімум для ALB:

Створення VPC Subnets

Далі треба налаштувати сабнети двох типів і створити по одному сабнету кожного типу в кожній Availability Zone.

Перший блок /24 я залишаю “резервним”, да і виглядає так красивіше:

  • дві публічні:
    • 10.0.1.0/24
    • 10.0.2.0/24
  • дві приватні:
    • 10.0.3.0/24
    • 10.0.4.0/24

Створення NAT Gateway

Regional NAT Gateways – нова плюшка від AWS, не так давно  з’явилась – дозволяє повністю автоматизувати створення NAT Gateways в нових Availablity Zones, не потребує Public Subnets, автоматично апдейтить Route Tables.

Але коштує, звісно, дорожче, та і взагалі в цьому сетапі не потрібна.

Створюємо класичний Zonal NAT Gateway і тільки в одній Availability Zone:

Завершення налаштувань VPC

VPC Endponts – залишаємо дефолтний S3, бо в мене, наприклад, до S3 пишуться бекапи блогу. Пізніше ще додамо новий, для EC2 Instance Connect.

Про VPC Endpoints трохи детальніше писав в пості Terraform: створення EKS, частина 1 – VPC, Subnets та Endpoints.

DNS Options залишаємо включеними – штука корисна і грошей не просить:

  • DNS hostnames: чи створювати “локальні” імена, наприклад – ip-10-0-3-226.eu-west-1.compute.internal – потрібно, аби коректно працювали RDS, EFS та інші мережеві ресурси
  • DNS resolution: чи зможуть сервіси всередині VPC використовувати його внутрішній DNS – теж штука корисна і зручна, хоча має свої обмеження (див, наприклад, Kubernetes: нагрузочное тестирование и high-load тюнинг – проблемы и решения)

І в результаті маємо таку картину (раніше теж не було, дуже зручно, і, здається, навіть route tables і маршрути створювались руками):

Все – поїхали створювати, займе трохи часу – міжна поки зробити чай.

За кілька хвилин – все готово:

Створення Security Groups

Зробимо три окремі Security Groups – для EC2, для RDS, та для Load Balancer:

В Security Group для EC2 дозволяємо SSH в межах VPC, дозволяємо HTTP від Public Subnets – там будуть інстанси Load Balancer (які, по суті, під капотом являють собою звичайні AWS EC2 – як і для AWS RDS):

Для SSH можна зробити більш суворі правила – дозволити тільки з VPN CIDR та VPC Private Subnet в eu-west-1a, де пізніше буде створюватись EC2 Instnace Connect Ednpoint – але це вже можна буде підтюнити пізніше, коли все буде працювати.

Аналогічно створюємо Security Group для Load Balancer – тут дозволяємо весь In на порти 80 та 443:

І для RDS – відкриваємо порт 3306 з Private Subnets, бо окрім EC2 сюди ніхто ходити не має:

Створення EC2 Instance Connect Endpoint

Коли планував робити з сервер на Debian, то думав доступ робити через AWS SSM – але для SSM потребує аж трьох VPC Endpoints, і за кожен треба платити гроші.

Тому зробив простіше – через EC2 Instance Connect Endpoint.

Переходимо в Endpoints, створюємо новий:

Задаємо ім’я і тип:

Вибираємо створену вище VPC. Опція “Preserve Client IP” – штука прикольна, передає клієнтський IP замість адреси самого Endpoint – можна буде спробувати пізніше, поки залишаємо в дефолтному “off”:

Створюється довго, хвилин 5 – як раз встигнемо зробити ще чаю та запустити EC2.

Створення EC2

Вибираємо Amazon Linux, задаємо ім’я інстансу:

Вибір типу інстансу та підрахунок потрібної пам’яті

Дуже коротко про типи, бо матеріал і так виходить великий, детальніше див. Amazon EC2 instance types.

Всі AWS EC2 діляться на кілька основних типів інстансів:

  • general purpose: збалансовані по CPU/RAM та вартості типи
    • сюди входять і burstable type, такі як t3/t4 – доступ до CPU більш обмежений, але на короткий час може, власне, burst – видаватись 100% процесорного часу, див. CPU Credsts, див. Key concepts for burstable performance instances
    • приклад general purpose:
      • t3.medium – 2 vCPU, 4 GiB RAM, ~ 30 USD/month
      • t3.large: 2 vCPU, 8 GiB RAM, ~ 60 USD/month
      • m5.large: 2 vCPU, 8 GiB RAM, ~ 69 USD/month
  • compute optimized: “заточені” під CPU – більше CPU, менше RAM:
    • приклад: c5.large – 2 vCPU, 4 GiB RAM, ~61 USD/month
  • memory optimized: і навпаки – більше RAM і менше CPU
    • приклад: r5.large – 2 vCPU, 16 GiB RAM, ~90 USD/month
  • storage optimized: мають NVMe диски з високим IOPS
    • приклад: i3.large – 2 vCPU, 15.25 GiB RAM, ~112 USD/month

Цифри 3/4/5/6 etc – покоління інстансів, чи вище цифра – тим новіше залізо “під капотом”, плюс можливості самого AWS (наприклад, в старих t2 нема підтримки підключення з serial console).

Плюс кожен тип має “підтипи”:

  • g: процесори Graviton – процесори від самого AWS на архітектурі – можуть бути не з усім сумісні, але використання ~20-30% дешевше, ніж у звичайних типів інстансів ARM при вищій швидкості виконання задач
  • i: процесори Intel – Intel Xeon, Intel Ice Lake
  • a: процесори AMD – AMD EPYC
  • n: окремий модифікатор, “network” – вищий network bandwidth, наприклад, інстанси R6in – Intel Network

Для вибору користуємось сервісами типу https://instances.vantage.sh або https://calculator.holori.com/aws.

Зараз сервер для RTFM в DigitalOcean має 2 vCPU і 4 GB RAM:

При цьому навантаження на процесор в середньому в районі 5%, а пам’ять зайнята на 60%:

Але зараз на цьому ж сервері живе MariaDB Server:

Тобто, якщо винести базу даних в AWS RDS, то на новому інстансі основним “споживачем” пам’яті буде PHP-FPM.

Можна подивитись скільки пам’яті процеси php-fpm використовують зараз:

root@setevoy-do-2023-09-02:~# ps aux --sort=rss | grep php-fpm | awk '{print $6}' | awk '{sum+=$1} END {print sum/1024 " MB total"}'
410.477 MB total

І кількість процесів:

root@setevoy-do-2023-09-02:~# ps aux --sort=rss | grep php-fpm | grep 'master\|rtfm.co.ua' | grep -v grep
root     1157320  0.0  0.3 264156 13092 ?        Ss   Mar03   0:55 php-fpm: master process (/etc/php/8.2/fpm/php-fpm.conf)
rtfm     1238997  1.3  3.1 362608 126980 ?       S    15:23   0:07 php-fpm: pool rtfm.co.ua
rtfm     1237462  1.7  3.2 360912 129788 ?       S    12:16   3:26 php-fpm: pool rtfm.co.ua
rtfm     1237598  1.7  3.3 364440 132484 ?       S    12:34   3:04 php-fpm: pool rtfm.co.ua

Прикинемо скільки йому треба.

Параметри PHP-FPM pool для RTFM зараз:

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

Див. PHP-FPM: Process Manager – dynamic vs ondemand vs static (2018 рік) та NGINX: настройка сервера и PHP-FPM (2014 рік).

Тобто максимум може бути 5 FPM workers – і з цією кількістю воркерів блог спокійно переживав TCP/IP: SYN flood атака на сервер RTFM, та “Hacker News hug of death”, бо більшість запитів оброблюються на CloudFlare:

Аби прикинути споживання пам’яті кожним можемо глянути в RSS (Resident Set Size), реальна фізична пам’ять процесу – але сюди включається пам’ять на shared бібліотеки, тобто якщо кілька PHP-FPM workers використовують одну і ту ж libc – RSS кожного включає її повністю і сумарний RSS буде завищений.

Втім – нехай буде завищена, бо ми прикидуємо “найгірший” варіант.

Дивимось скільки пам’яті на кожен воркер зараз:

root@setevoy-do-2023-09-02:~# ps aux | grep php-fpm | grep 'pool rtfm.co.ua' | awk '{print $6/1024 " MB - " $13}'
126.773 MB - rtfm.co.ua
133.355 MB - rtfm.co.ua
126.168 MB - rtfm.co.ua

І якщо маємо pm.max_children = 5 – то максимум пам’яті буде ~150 MB * 5 == 750 MB.

Можна для початку взяти t3.medium – хоча це прям запас з головою:

Решта налаштувань

Створюємо ключ для доступу по SSH:

Зберігаємо собі на робочу машину, відразу задаємо права:

$ chmod 600 ~/.ssh/rtfm-al-2026-03.pem

Вибираємо VPC, сабнет eu-west-1a та Securty Group, яку створили вище:

Сторейдж – одного диску на 50 гігабайт вистачить з запасом:

В Advanced details включаємо Termination protection – дуже корисна для production ресурсів опція.

І додатково можна поки що додати Detailed CloudWatch monitoring – але за нього доведеться платити додаткові гроші, тому потім краще відключити:

Запускаємо інстанс.

Поки робили це – EC2 Instance Connect Endpoint вже готовий:

Сам EC2 інстанс стартує дуже швидко – перевіряємо підключення:

Вибираємо EC2 Instance Connect, вибираємо “Connect using Private IP”:

І ми в системі:

Аби підключитись з ноутбука – використовуємо AWS CLI:

$ aws --region eu-west-1 --profile setevoy ec2-instance-connect ssh --instance-id i-026523e8f29147e3e --connection-type eice

А пізніше вже буде пряме підключення через VPN.

Поки робимо апгрейд і встановлюємо для тесту NGINX:

# dnf update -y
# dnf install -y nginx
# systemctl enable nginx
# systemctl start nginx

Перевіряємо:

# curl localhost:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Тут все готове – можна переходити до SSL/TLS та Load Balancer, а потім встановити PHP і вже перевірити роботу WordPress.

Отримання SSL/TLS сертифікату з AWS Certificate Manager

Дуже зручна штука – бо один раз отримати, підключити до Load Balancer, і забути – далі AWS сам менеджить всі renew.

Переходимо в ACM, клікаємо Request a certificate:

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

Залишаємо дефолту опцію DNS validation:

Клікаємо Create records in Route 53 – це, звісно, тільки для тих доменів, які обслуговуються на Route 53, див. далі приклад з Cloudflare Name Servers:

Перевіряємо чи додався новий запис для домену в Route 53:

AWS ACM Certificate та DNS validation для домену на Cloudflare Nameservers

Домен rtfm.co.ua обслуговується серверами Cloudflare:

$ whois rtfm.co.ua | grep "Name Server"
Name Server:ETHAN.NS.CLOUDFLARE.COM
Name Server:NOVALEE.NS.CLOUDFLARE.COM

Тому в панелі керування DNS Cloudflare додаємо новий запис з типом CNAME:

Але…

Фікс помилки AWS ACM Certificate DNS validation failed

Але валідація сфейлилась:

Читаємо документацію Certification Authority Authorization (CAA) problems, перевіряємо записи з типом CAA (Certification Authority Authorization) для домену – хто може видавати сертифікати для цього домену:

$ dig rtfm.co.ua CAA +short
0 issuewild "digicert.com; cansignhttpexchanges=yes"
0 issuewild "letsencrypt.org"
0 issuewild "pki.goog; cansignhttpexchanges=yes"
0 issuewild "ssl.com"
0 issue "comodoca.com"
0 issue "digicert.com; cansignhttpexchanges=yes"
0 issue "letsencrypt.org"
0 issue "pki.goog; cansignhttpexchanges=yes"
0 issue "ssl.com"
0 issuewild "comodoca.com"

І дійсно – AWS тут нема.

Додаємо два CAA записи – один для сертифікатів для корневого домену:

І один для wildcard сертифікатів:

Ще раз перевіряємо:

$ dig @ethan.ns.cloudflare.com rtfm.co.ua CAA +short
0 issue "amazon.com"
...
0 issuewild "amazon.com"
...

Видаляємо перший сертифікат, повторюємо процес створення та валідації – і тепер все готово:

Створення AWS Load Balancer

Використання Load Balancer дасть змогу, власне, балансувати навантаження – якщо планується мати кілька інстансів, то зручно мати один і той статичний URL, який можна використати як CNAME для домену, буде можливість додавати або замінювати інстанси без необхідності внесення змін в DNS домену, і дасть можливість використання AWS Web Application Firewall.

Плюс, ALB спрощує менеджмент SSL/TLS – один раз створюємо сертифікат, підключаємо його до ALB, і SSL termination буде на Load Balancer: клієнти до Load Balancer ходять з HTTPS, а Load Balancer до EC2 по HTTP – простіший конфіг NGINX, нема потреби в налаштуванні Let’s Encrypt.

Хоча за великим рахунком, якщо планується дійсно невеликий персональний сайт – то ALB теж overkill. Втім, якщо є зайві кредити, то його використання дійсно спрощує життя.

Створення Target Group

Load Balancer працює через Target Groups (TG), де кожна TG включає в себе один або декілька EC2 на які ALB буде слати трафік.

Створюємо нову Target Group:

В Type вказуємо Instance, задаємо ім’я групи та протокол, за яким буде відбуватись комунікація ALB з сервісами на EC2 в цій групі.

На EC2 у нас NGINX який приймає підключення на порт 80 – тому в Protocol та Port залишаємо дефолтні параметри:

Якщо на ALB плануємо використовувати і HTTP і HTTPS – то вказуємо HTTP/1, якщо тільки HTTPS – то можна HTTP/2.

Хоча зазвичай для HTTP просто налаштовується redirect на HTTPS і можна було б тут відразу вказати HTTP2 – але деякі клієнти все ще можуть використовувати HTTP v1 – тому залишимо їм право вибору і залишаємо дефолтну опцію HTTP1:

В Health checks можна залишити все як є – Health check path на NGINX буде “/”, Traffic port буде 80:

Вибираємо інстанс(и) для цієї Target Group:

Підтверджуємо створення:

Типи Load Balancers: ALB vs NLB vs GLB vs CLB

Amazon дозволяє створити кілька типів Load Balancer:

  • Application Load Balancer:
    • модно-молодьожно
    • працює на L7 (HTTP/HTTPS), може читати зміст HTTP-запиту і мати окремі налаштування по, наприклад, URI (/api/ – слати на одну Target Group, /users/ – слати на Auth0 тощо)
    • підтримує роботу з WebSocket, gRPC
  • Network Load Balancer:
    • працює на L4 (TCP/UDP) – дуже швидкий, чудовий вибір для high load applications, має можливість використання Static IP
  • Gateway Load Balancer:
    • доволі специфічна штука, для роутингу трафіку через сторонні network appliances (firewall, IDS/IPS), я ніколи не користувався

Окремо загадаємо про Classic Load Balancer – легасі, deprecated. Підтримує і L4 і L7 але гірше ніж ALB/NLB окремо.

Детальніше див. What’s the Difference Between Application, Network, and Gateway Load Balancing?

Налаштування AWS Load Balancer

Переходимо до створення Load Balancer:

 

Вибираємо тип Application Load Balancer:

 

Задаємо ім’я, тип Internet-facing (тип Internal – корисна опція, коли треба мати ALB, який доступний тільки всередині VPC):

Вибираємо VPC, Subnets та Secuiruty Group, яку робили на початку:

Для HTTP Listener налаштовуємо редірект на HTTPS:

А в HTTPS Listener підключаємо створену вище Target Group:

Підключаємо SSL сертифікат із ACM:

Перевіряємо, що все ОК і створюємо:

Створення займе кілька хвилин – робимо ще один чай.

Налаштування DNS для ALB

Поки створюється ALB – додамо новий запис в Route 53, який буде прив’язаний до створеного Load Balancer.

Тут приклад на іншому домені, але він в AWS ACM був доданий, тому буде працювати без помилок TLS.

Створюємо новий DNS Record, вибираємо тип Alias, знаходимо наш ALB, Routing policy залишаємо Simple (див. Choosing a routing policy):

Перевіряємо, чи все працює (може зайняти 5-10 хвилин на апдейт DNS):

Створення AWS Relational Database Service

Останній крок перед запуском WordPress – створити сервер даних.

З AWS RDS працюю дуже давно, сервіс класний, хоча, звісно, не безкоштовний. Але “перекласти відповідальність” за стабільність і бекапи на плечі AWS – чудове рішення для якогось production.

Плюс інтеграція з AWS IAM, CloudWatch Logs та Metrics, автоматичні бекапи, автоскейлінг – можливостей багато.

Створюємо новий сервер (хоча меню називається “Create database” – але створюється саме окремий інстанс):

В Credentails management можна залишити дефолтний AWS Secrets Manager – він вміє автоматично ротейтити пароль root для сервера, див. Set up automatic rotation for Amazon RDS:

Залишаємо опцію Password and IAM database authentication – хоча інтеграція IAM обмежує доступ тільки до самого серверу, а не баз даних, і все одно треба буде створювати юзера з власними правами доступу і паролем, див. AWS: RDS з IAM database authentication, EKS Pod Identities та Terraform.

Тип інстансу вибираємо мінімально доступний, db.t3.micro – хоча для RTFM і цього вистачить з великим запасом:

Розмір диску – мінімум 20 гігабайт, що при розмірі БД у RTFM в 1.2 гігабайти теж з головою.

Корисна штука для production – storage autoscaling: працює повністю непомітно для сервера і клієнтів:

В Connectivity можна автоматично налаштувати підключення до EC2 – створить всі необхідні параметри в VPC та Subnets, але давайте хоч тут зробимо вручну.

DB Subnet Group – створюємо нову, RDS сам вибере потрібні private subnets, бо далі, в Public access, ми задаємо “No” – сервер баз даних має жити тільки в приватних мережах, без доступу у світ.

Згадав про цікаву історію – MySQL/MariaDB: like Petya ransomware для баз данных и ‘root’@’%’: клієнт створив штук 10 серверів БД з публічним доступом, доступом root з інтернету, і… Без пароля.

Результат прєдсказуємий 🙂

У VPC Security Group вибираємо групу, яку створювали на початку:

В Additional monitoring settigns – інтересу раді можна включити Enhanced monitoring, це коштує додаткових грошей – але в production може дуже знадобитись, бо додає метрики по роботі операційної системи (CPU per process, RAM, disk I/O, network, file system), див. довгий пост по PostgreSQL: AWS RDS Performance and monitoring – був цікавий випадок, коли Enhanced monitoring знадобився:

Additional configuration – тут відразу можемо створити базу даних і налаштувати автоматичні бекапи.

Автоматичні бекапи (Periodic snapshots) – дуже рекомендована штука, рятувала не один раз: створює повний snapshot інстансу, і потім з цього снапшоту можна в будь-який момент створити новий інстанс з усіма даними.

До того для RDS є можливість налаштувати Continuous backups – для відновлення стану баз(и) на якийсь конкретний момент часу, див. Amazon Relational Database Service backups.

Базу створимо пізніше вручну, залишаємо бекапи:

І в кінці відразу бачимо приблизну вартість – раніше не було цього, зручно зробили:

DNS та Private hosted zone

Корисна з точки зору безпеки штука – приватні доменні зони, які доступні тільки всередині VPC, див. Working with private hosted zones.

Тому створимо окрему зону з DNS Records, які потрібні тільки в VPC, в нашому випадку – Database URL як раз чудовий приклад:

Знаходимо URL в самому RDS:

Додаємо його як value в CNAME нового запису:

Підключення до RDS

Підключаємось по SSH до EC2, шукаємо пакет mariadb:

[ec2-user@ip-10-0-3-146 ~]$ dnf search mariadb
...
mariadb114.x86_64 : A very fast and robust SQL database server
...

Аби встановити тільки клієнт – вибираємо mariadb без -server:

[ec2-user@ip-10-0-3-146 ~]$ sudo dnf install -y mariadb114

В AWS Secrets Manager знаходимо пароль RDS root:

І підключаємось, використовуючи local DNS record, який створили вище:

[ec2-user@ip-10-0-3-146 ~]$ mysql -h db.rtfm.local -P 3306 -u rtfm_root -p
Enter password:
...
MariaDB [(none)]>

Або можна це трохи автоматизувати з AWS CLI – в RDS є приклад команди:

Запуск WordPress

Ну і нарешті – у нас все готово для запуску WordPress.

Що нам залишилось – це створити базу даних, юзера, і на EC2 встановити PHP.

Створення бази даних в RDS

Створюємо базу даних і юзера – для WordPress рекомендовано utf8mb4_unicode_ci (підтримка всяких емодзі):

MariaDB [(none)]> CREATE DATABASE test_wp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Query OK, 1 row affected (0.064 sec)

MariaDB [(none)]> CREATE USER 'test_wp_user'@'%' IDENTIFIED BY 'test_wp_pass';
Query OK, 0 rows affected (0.058 sec)

MariaDB [(none)]> GRANT ALL PRIVILEGES ON test_wp_db.* TO 'test_wp_user'@'%';
Query OK, 0 rows affected (0.034 sec)

MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.027 sec)

Установка PHP та модулів

Встановлюємо PHP і модулі – хоча тут не всі модулі, не пам’ятаю, що треба ще для роботи RTFM, але це базовий набір для WordPress та, в принципі, будь-якого вебсайту:

[root@ip-10-0-3-146 ~]# dnf install -y php-fpm php-mysqlnd php-json php-mbstring php-xml php-gd

Додаємо в автостарт та запускаємо сервіс PHP-FPM:

[root@ip-10-0-3-146 ~]# systemctl enable php-fpm
Created symlink /etc/systemd/system/multi-user.target.wants/php-fpm.service → /usr/lib/systemd/system/php-fpm.service.
[root@ip-10-0-3-146 ~]# systemctl start php-fpm

Дефолтний конфіг – /etc/php-fpm.d/www.conf, для RTFM буде окремий, але це не зараз.

Перевіряємо файл сокету, щоб впевнитись, що FPM готовий приймати підключення:

[root@ip-10-0-3-146 ~]# ll /run/php-fpm/www.sock
srw-rw----+ 1 root root 0 Mar  8 11:34 /run/php-fpm/www.sock

Створення NGINX virtualhost

Додаємо файл налаштувань тестового сайту – /etc/nginx/conf.d/test.conf.

Каталог /etc/nginx/conf.d/ включається в конфіг через основний файл налаштувань /etc/nginx/nginx.conf:

...
 include /etc/nginx/conf.d/*.conf;
...

У файлі /etc/nginx/conf.d/test.conf описуємо HTTP сервер на порту 80 з fastcgi_pass на PHP-FPM socket:

server {
    listen 80;
    server_name test-alb.setevoy.org.ua;

    root /var/www/html;
    index index.php;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Для перевірки PHP створюємо тестовий файл /var/www/html/index.php:

<?php phpinfo(); ?>

Виконуємо nginx check config && reload:

[root@ip-10-0-3-146 ~]# nginx -t && systemctl reload nginx
nginx: [warn] conflicting server name "_" on 0.0.0.0:80, ignored
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

І перевіряємо файл index.php в браузері:

Установка WordPress

Завантажуємо архів, розпаковуємо, міняємо власника та групу на nginx:nginx:

[root@ip-10-0-3-146 ~]# cd /var/www/html
[root@ip-10-0-3-146 html]# wget https://wordpress.org/latest.tar.gz
...
[root@ip-10-0-3-146 html]# tar -xzf latest.tar.gz
[root@ip-10-0-3-146 html]# mv wordpress/* .
mv: overwrite './index.php'? y
[root@ip-10-0-3-146 html]# rm -rf wordpress latest.tar.gz
[root@ip-10-0-3-146 html]# chown -R nginx:nginx /var/www/html

Відкриваємо в браузері – не завантажуються CSS та картинки.

Але це ОК, далі поправимо, не критично.

Критично буде далі з RDS – тому спочатку дочитайте цю частину:

Клікаємо Let’s go, задаємо параметри підключення до RDS:

І ловимо помилку “Error establishing a database connection“:

Ну… 🙂

WordPress users know that feeling 🙂

AWS RDS та WordPress “Error establishing a database connection”

Перше, що можна спробувати – це створити файл wp-config.php вручну і задати параметри явно:

...
// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'test_wp_db' );

/** Database username */
define( 'DB_USER', 'test_wp_user' );

/** Database password */
define( 'DB_PASSWORD', 'test_wp_pass' );

/** Database hostname */
define( 'DB_HOST', 'db.rtfm.local' );

/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8mb4' );
...

Але в цьому конкретному випадку це не допоможе.

Тому встановлюємо php-cli:

[root@ip-10-0-3-146 html]# dnf install -y php-cli

І спершу перевіряємо, чи працює DNS Resolver на нашу приватну DNS zone:

[root@ip-10-0-3-146 html]# php -r "echo gethostbyname('db.rtfm.local');"
10.0.3.53

Да, все чудово.

Тепер пробуємо MySQL connect – і от тут вже ловимо  саму помилку – “Connections using insecure transport are prohibited“:

[root@ip-10-0-3-146 html]# php -r "mysqli_connect('db.rtfm.local', 'test_wp_user', 'test_wp_pass', 'test_wp_db') or die(mysqli_connect_error());"
PHP Fatal error:  Uncaught mysqli_sql_exception: Connections using insecure transport are prohibited while --require_secure_transport=ON. in Command line code:1
Stack trace:
#0 Command line code(1): mysqli_connect()
#1 {main}
  thrown in Command line code on line 1

Тут два варіанти вирішення – або в wp-config.php примусово включити SSL для підключення (рекомендується):

define('MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL);

Або змінити параметр require_secure_transport в RDS (не рекомендується):

Після додавання MYSQLI_CLIENT_SSL установка вже пішла нормально – картинки зараз поправимо:

Фікс CSS та картинок

Проблема виникає через змішаний трафік – до ALB ми ходимо по HTTPS, а між ALB та EC2 маємо простий HTTP.

Для фіксу в wp-config.php додаємо:

$_SERVER['HTTPS'] = 'on';
define('WP_HOME', 'https://test-alb.setevoy.org.ua');
define('WP_SITEURL', 'https://test-alb.setevoy.org.ua');

І блог працює без проблем:

AWS Costs Breakdown: а шо по грошам?

Болюча тема для будь-якого IaaS/PaaS провайдеру – будь-то Google Cloud Engine, Microslop Microsoft Azure чи AWS.

Коротко пройдемось по описаному вище сетапу – що і скільки в результаті коштує по грошам.

В Cost Explorer бачимо таку картину:

5 доларів на день, за 30 днів буде 150 доларів.

Ну – дуже не слабо, як для приватного блогу. Але і описана вище інфраструктура трохи завелика для такого проекту.

Подивимось що саме нам так дорого обходиться.

EC2-Other costs

Часте питання – “що за EC2 Other в Cost Explorer“, бо не дуже зрозуміла назва.

Фактично, сюди входять Public IP адреси, трафік, EBS volumes.

Подивитись що конкретно нам обходиться в $1.29 можна в тому ж Cost Explorer – у Filters > Service вибираємо EC2 Other, а в Group by > Dimension вибираємо Usage type або API Operation:

Власне, бачимо вартість NAT Gateway.

Деталі заходимо в документації Amazon VPC pricing, і рахуємо: $0.045 на годину, множимо на 24 і маємо 1.080 долари на добу, або 30+ доларів на місяць.

І це ще без трафіку через NAT Gateway, який рахується окремо – $0.048 за кожен гігабайт в cross-region або cross AvailabilityZone трафіку.

Вартість трафіку в AWS це взагалі окрема тема, тут вже розбирати не буду, але колись писав пост AWS: Cost optimization – обзор расходов на сервисы и стоимость трафика в AWS.

Власне, саме з цієї причини варто мати VPC Endpoint для S3: тип gateway безкоштовний, зато трафік буде йти не через NAT Gateway – а всередині VPC.

EBS Volumes costs

На скріншоті вище бачимо API Operation CreateVolume-Gp3 – це вартість EBS, який підключений до EC2, див. Amazon EBS pricing: 50 гігабайт диск дає нам $4.4 на місяць, або $0.147/день.

Диск для RDS рахується окремо:

EC2-Instances costs

Тут все просто – маємо один t3.medium, який коштує $0.0416 – маємо $0.99 в день.

Але до того ж і тут рахується трафік – він $0.04 до $0.09 за гігабайт outgoing в залежності від об’єму.

Вхідний трафік не оплачується.

Втім, є свої нюанси з трафіком:

  • ALB: трафік, який віддаємо клієнтам через Load Balancer – оплачується
  • NAT Gateway: тут взагалі платимо двічі:
    • NAT GW processing fee: це з costs самого NAT Gateway, аза кожен переданий через нього назовні гігабайт
    • EC2 Data Transfer Out: і додатково платимо за кожен гігабайт “у світ” з самого EC2
  • RDS: дані в межах одної Availability Zone не оплачуються, але якщо є cross-AZ або cross-region сетап – то платимо $0.01/GB за вхідний і вихідний трафік

А, і це ще не згадував On-Demand, Reserved, Spot інстанси. Але це теж окрема тема, див. Amazon EC2 billing and purchasing options.

А ще окремо оплачуються CPU Credits, $0.05 per vCPU-Hour 🙂 (для RDS теж)

Elastic Load Balancing costs

Див. Elastic Load Balancing pricing.

  • платимо за кону годину роботи ALB
  • платимо за LCU (Load Balancer Capacity Units) – навантаження на ALB, загальна вартість буде залежати від того, скільки ALB опрацював запитів від клієнтів (або під час DDoS :trollface: )
  • платимо за outgoing трафік – але тільки за трафік з ALB, бо трафік між EC2 та ALB в межах одної Availability Zone безкоштовний

Relational Database Service costs

Див. Amazon RDS for MariaDB pricing.

Вже бачили на скріншоті вище – EU-InstanceUsage:db.t3.micro, EU-RDS:GP3-Storage, EU-DataTransfer-In-Bytes, EU-DataTransfer-Out-Bytes.

  • за db.t3.micro в одній Availability Zone платимо $0.018, або $0.43 на добу, або ~13 в місяць
  • CPU Credits для t3 – $0.075 per vCPU-hour
  • Storage: $0.115/GB-month
  • Backup snapshots: безкоштовний в розміні 100% від розміру диска для RDS instance

Route 53 costs

Див. Amazon Route 53 pricing.

Тут платимо за:

  • $0.50 за кожну домену зону
  • і окремо за запити до DNS, але там дуже багато безкоштовних запитів, ще і різні типи – кожну вже окремо описувати не буду

VPC costs

Див. Amazon VPC pricing.

Тут багато свої особливостей – VPC Peering, IPAM, Encryption.

Конкретно в нашому випадку платиться тільки за Public IP (бо у Load Balancer та NAT Gateway власні Elastic IP addresses):

При чому за IP платимо два рази:

  • AllocateAddressVPC: просто за те, що нам дозволили користуватись адресою IPv4
  • AssociateAddressVPC: за прив’язку адреси до інстансу

Перевірити які адреси до чого підключені можна в VPC.

Тут дві адреси для ALB:

І одна для NAT Gateway:

І одна знайшлась unused – а я за неї плачу.

Ну і, власне, це основне по вартості AWS.

Платимо за “кожний чіх”. Втім, як в інших подібних провайдерах.

Loading