Прийшов час для мажорного апгрейду серверу 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
- створимо одну AWS VPC з чотирма Subnets у двох Availability Zones:
- 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 році, якихось кардинальних змін в побудові нетворку не було:
- AWS: миграция RTFM, часть #1: ручное создание инфраструктуры — VPC, подсети, IGW, NAT GW, маршруты и EC2
- AWS: VPC – введение, примеры
- AWS: VPC – EC2 в public и private подсетях, NAT и Internet Gateway
Автогенерація імен ресурсів – теж прикольна штука, і генерує достатньо адекватні імена як раз в тому стилі, як я це завжди робив – з іменем 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/montht3.large: 2 vCPU, 8 GiB RAM, ~ 60 USD/monthm5.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 Lakea: процесори AMD – AMD EPYCn: окремий модифікатор, “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 гігабайт вистачить з запасом:
В
Запускаємо інстанс.
Поки робили це – 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: просто за те, що нам дозволили користуватись адресою IPv4AssociateAddressVPC: за прив’язку адреси до інстансу
Перевірити які адреси до чого підключені можна в VPC.
Тут дві адреси для ALB:
І одна для NAT Gateway:
І одна знайшлась unused – а я за неї плачу.
Ну і, власне, це основне по вартості AWS.
Платимо за “кожний чіх”. Втім, як в інших подібних провайдерах.
![]()












































































