AWS

Amazon EC2 — Elastic Compute Cloud

Повне керівництво з Amazon EC2 для .NET розробників. Instance Types, AMI, Pricing Models, Security Groups, EBS, запуск .NET 10 на Linux та ASP.NET на Windows Server з IIS від А до Я.

Amazon EC2 — Elastic Compute Cloud

Що таке EC2 і навіщо він вам потрібен

Уявіть, що вам потрібен комп'ютер — але не фізичний, який треба купувати, встановлювати в стійку, підключати кабелі та обслуговувати. Ви хочете просто замовити комп'ютер через інтернет, отримати до нього доступ через кілька хвилин і платити лише поки він працює. Саме це і є Amazon EC2 (Elastic Compute Cloud).

EC2 надає віртуальні сервери (офіційна назва — instances) у хмарі AWS. Кожен instance — це повноцінний комп'ютер з процесором, оперативною пам'яттю, дисковим простором та мережевим інтерфейсом. Ви можете запустити від одного до тисяч instances за лічені хвилини — і зупинити їх так само швидко.

Чому це важливо для .NET розробника?

Класичний шлях деплою .NET додатку: купуємо або орендуємо фізичний сервер → встановлюємо Windows Server або Linux → налаштовуємо IIS або nginx → деплоїмо застосунок. Цей процес займає дні або тижні і коштує фіксовану суму незалежно від навантаження.

З EC2: запускаємо instance з готовим образом операційної системи → встановлюємо .NET та IIS → деплоїмо → і сервер вже у production. Весь процес — кілька годин. Якщо навантаження зросло — запускаємо ще instances. Якщо впало — зупиняємо зайві. Платимо лише за фактичний час роботи.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

actor "Розробник" as Dev
cloud "AWS Cloud" as AWS {
    node "EC2 Instance" as EC2 #dbeafe {
        component ".NET 10 Runtime" as dotnet
        component "ASP.NET Core API" as api
        component "Ubuntu 26.04 LTS" as os
    }
}

rectangle "Ваш комп\'ютер" as Local {
    component "Код + .csproj" as Code
}

database "EBS Volume" as EBS #d1fae5

Dev -right-> AWS : ssh ubuntu@IP
Code -right-> EC2 : dotnet publish → scp
EC2 -down-> EBS : /var/app/
@enduml

EC2 Instance Types — вибір правильного сервера

Instance Type — це конфігурація hardware вашого віртуального сервера: скільки ядер CPU, скільки RAM, який тип мережевого з'єднання. AWS пропонує 600+ типів instances, згрупованих у сімейства за призначенням.

Формат назви: [сімейство][покоління].[розмір]

Наприклад: t3.micro — сімейство T (General Purpose), 3-тє покоління, розмір micro.

General Purpose (Загального призначення)

Найпопулярніша категорія — збалансоване співвідношення CPU та RAM. Ідеально для більшості .NET Web API, веб-сайтів, мікросервісів.

Сімейство T (Burstable Performance): t3.nano, t3.micro, t3.small, t3.medium, t3.large

Особливість T-instances: CPU Credits. В тихі моменти instance накопичує «кредити» CPU. При піковому навантаженні витрачає їх для короткочасного використання більш ніж базового % CPU. Це дозволяє мати дешевий instance, який справляється з рідкісними піками.

  • t3.micro — 2 vCPU, 1 GB RAM ← входить у Free Tier на 12 місяців
  • t3.small — 2 vCPU, 2 GB RAM
  • t3.medium — 2 vCPU, 4 GB RAM ← мінімум для .NET 10 API з реальним навантаженням
  • t3.large — 2 vCPU, 8 GB RAM

Сімейство M (Fixed Performance): m6i.large, m6i.xlarge, m6i.2xlarge

Фіксований (не burstable) рівень CPU. Для production .NET застосунків зі стабільним навантаженням.

Compute Optimized (Оптимізовані під CPU)

Сімейство C: c6i.large, c6i.xlarge. Вище співвідношення CPU до RAM. Для CPU-інтенсивних задач: обробка зображень, відеотранскодування, ML inference, складні обчислення.

Memory Optimized (Оптимізовані під RAM)

Сімейство R: r6i.large, r6i.xlarge. Більше RAM відносно CPU. Для in-memory кешування (Redis/Memcached на тому ж instance), великих баз даних в пам'яті, .NET застосунків з великими Dataset.

Storage Optimized

Сімейство I та D: надвисока швидкість роботи з диском (NVMe SSD). Для баз даних з інтенсивним I/O, Elasticsearch, аналітичних задач.

Для початківців: якщо ви не впевнені — починайте з t3.micro (Free Tier) для навчання, t3.medium для перших production застосунків. Тип завжди можна змінити — зупинити instance, змінити тип, запустити знову.

T — Burstable (Free Tier)

CPU Credits для рідкісних піків. t3.micro — Free Tier. Ідеально для розробки, навчання, низьконавантажених API.

M — General Purpose

Фіксований CPU, збалансоване CPU/RAM. m6i.large — production .NET API зі стабільним навантаженням.

C — Compute Optimized

Вище CPU до RAM. c6i.large — CPU-інтенсивні задачі: обробка зображень, ML inference, відеотранскодування.

R — Memory Optimized

Більше RAM до CPU. r6i.large — in-memory кешування, великі Dataset, аналітика в пам'яті.

EC2 Instance Lifecycle — стани та переходи

Кожен EC2 instance проходить через певні стани (states) протягом свого існування. Розуміння цих станів критично важливе: від них залежить тарифікація, збереження даних та IP-адреса після рестарту.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

rectangle "pending\n(ініціалізується)" as PEND #fef3c7
rectangle "running\n(працює · тарифікується)" as RUN #bbf7d0
rectangle "stopping\n(зупиняється)" as STOP #fde68a
rectangle "stopped\n(зупинений · EBS збережений)" as STOPD #e5e7eb
rectangle "shutting-down\n(видаляється)" as SHUT #fecaca
rectangle "terminated\n(видалений назавжди)" as TERM #fecaca
rectangle "rebooting\n(перезавантажується)" as REBOOT #dbeafe

PEND -right-> RUN : ~1-3 хв
RUN -down-> STOP : Stop
RUN -right-> SHUT : Terminate
RUN -up-> REBOOT : Reboot
STOP -down-> STOPD
STOPD -left-> PEND : Start
STOPD -right-> TERM : Terminate
SHUT -right-> TERM
REBOOT -left-> RUN : ~30 сек

note bottom of STOPD
  Обчислення не тарифікуються.
  EBS томи — тарифікуються далі.
  Публічний IP змінюється при Start
  (якщо немає Elastic IP).
end note
@enduml

Стан stopped — зупинений, але не видалений

Stop — аналог «вимкнути комп'ютер». Instance зупинений, але існує: EBS томи зберігають усі дані, Security Groups залишаються, Instance ID не змінюється. Обчислення не тарифікуються, проте EBS-диски тарифікуються далі (~$0.08/GB/місяць для gp3).

Що відбувається при Stop → Start:

  • AWS може перемістити instance на інший фізичний хост у тій самій AZ
  • Публічна IP-адреса змінюється — якщо не прикріплений Elastic IP, DNS запис на старий IP перестане працювати
  • Приватна IP (всередині VPC) — залишається незмінною
  • Дані на всіх EBS томах — збережені

Коли використовувати Stop: dev-сервери, які не потрібні вночі або у вихідні — зупиняєте ввечері, запускаєте вранці. Платите лише за EBS (~$1.6/місяць за 20 GB gp3), не за EC2.

Стан terminated — незворотнє видалення

Terminate — «видалити комп'ютер назавжди». Після переходу в shutting-downterminated instance неможливо відновити. За замовчуванням root EBS том також видаляється (параметр «Delete on termination»). Додаткові EBS томи можна налаштувати на збереження.

Terminate — незворотня операція. AWS не може відновити видалений instance. Перед видаленням переконайтесь, що важливі дані збережені у S3 або на окремому EBS томі з вимкненим «Delete on termination».

Стан rebooting — перезавантаження без зміни хосту

На відміну від Stop → Start, Reboot залишає instance на тому самому фізичному хості. Публічна IP-адреса не змінюється. Тарифікація не переривається. Рекомендований для застосування оновлень ОС, що вимагають перезапуску.

Hibernate — збереження RAM на диск

Hibernate — особливий режим зупинки: вміст оперативної пам'яті зберігається на root EBS томі. При наступному запуску instance відновлюється з точного стану: всі процеси продовжуються, з'єднання відновлюються — схоже на режим сну ноутбука.

Вимоги: instance type підтримує Hibernate (T3, M5, C5, R5 та ін.), root EBS обов'язково зашифрований, RAM ≤ 150 GB, тривалість Hibernate ≤ 60 днів.

ДіяCPU тарифікаціяПублічний IPДані на EBSВідновлення
StopНіЗмінюєтьсяЗбереженіStart (~1-2 хв)
HibernateНіЗмінюєтьсяЗбережені + RAMStart (~30 сек)
RebootТак (без перерви)Не змінюєтьсяЗбереженіАвто (~30 сек)
TerminateНіЗвільняєтьсяВидаляються (root)Неможливо
Free Tier та стани:t3.micro у Free Tier — 750 годин на місяць у стані running. У стані stopped Free Tier-ліміт не витрачається, але EBS у Free Tier обмежений — 30 GB загалом.

AMI — Amazon Machine Image

AMI (Amazon Machine Image) — це готовий «шаблон» операційної системи з попередньо встановленим програмним забезпеченням. Коли ви запускаєте EC2 instance — ви вказуєте AMI, і instance «клонується» з цього шаблону.

Думайте про AMI як про знімок жорсткого диска (snapshot): включає ОС, всі встановлені програми, конфігурацію. Запуск instance з AMI — це як розгортання готового диска на новому комп'ютері.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

rectangle "AMI: Ubuntu 26.04 LTS\n(або Custom AMI + .NET 10)" as AMI #dbeafe {
    rectangle "ОС + пакети" as OS #bbf7d0
    rectangle "Конфігурація" as CFG #d1fae5
}

node "EC2 Instance A\n(eu-central-1a)" as I1 #e0e7ff
node "EC2 Instance B\n(eu-central-1b)" as I2 #e0e7ff
node "EC2 Instance C\n(Auto Scaling)" as I3 #e0e7ff

AMI -down-> I1 : клонування
AMI -down-> I2 : клонування
AMI -down-> I3 : клонування

note right of AMI
  Один AMI — необмежена
  кількість instances.
  Кожен — незалежна копія.
end note
@enduml

Типи AMI

AWS Managed AMI — офіційні образи від Amazon та партнерів:

  • Amazon Linux 2023 — власний дистрибутив AWS, оптимізований для EC2. Рекомендований для більшості Linux-задач.
  • Ubuntu Server 26.04 LTS — популярний серед розробників. Відмінна підтримка .NET.
  • Windows Server 2022 — для .NET Framework та IIS. Включає ліцензію Windows у вартість EC2.
  • Windows Server 2022 with SQL Server — для застосунків з MS SQL Server.
  • Red Hat Enterprise Linux (RHEL) — для корпоративних середовищ.

AWS Marketplace AMI — образи від третіх сторін з попередньо встановленим ПЗ (платні або безкоштовні): Bitnami WordPress, GitLab CE, Nginx Plus тощо.

Community AMI — публічні образи від спільноти. Використовуйте з обережністю — перевіряйте джерело.

Custom AMI (ваші власні) — ви можете створити AMI зі свого налаштованого instance. Зручно для auto scaling: замість того, щоб кожен новий сервер встановлював .NET та конфігурував застосунок — він одразу стартує з готовим середовищем.

AMI та регіони

Важливий нюанс: AMI прив'язані до регіону. AMI, доступна у eu-central-1, недоступна у us-east-1 — потрібно скопіювати. ID однієї й тієї ж ОС різниться між регіонами.

При написанні скриптів автоматизації — ніколи не хардкодьте AMI ID! Замість цього запитуйте актуальний ID через SSM Parameter Store або AWS CLI фільтрами.

Знайти актуальний AMI ID для Ubuntu 26.04 у вашому регіоні:

aws ec2 describe-images \
    --owners 099720109477 \
    --filters "Name=name,Values=ubuntu/images/hvm-ssd-gp3/ubuntu-resolute-26.04-amd64-server-*" \
    --query "sort_by(Images, &CreationDate)[-1].ImageId" \
    --output text --region eu-central-1
# ami-0a1b2c3d4e5f67890

Тут 099720109477 — це офіційний AWS Account ID компанії Canonical (розробника Ubuntu). Це гарантує, що ви отримаєте справжній офіційний образ.


Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

rectangle "On-Demand\n$0.0116/год (t3.micro)" as OD #dbeafe
rectangle "Savings Plans\n(1-3 роки, -30-75%)" as SP #d1fae5
rectangle "Spot Instances\n(-60-90%, переривувані)" as SI #fef3c7

rectangle "Гнучкість" as FLEX #e5e7eb
rectangle "Економія" as SAVE #e5e7eb

OD -down-> FLEX : максимальна
OD -down-> SAVE : мінімальна
SP -down-> FLEX : середня
SP -down-> SAVE : висока
SI -down-> FLEX : низька
SI -down-> SAVE : максимальна
@enduml

EC2 Pricing Models — моделі оплати

Ціна EC2 залежить не лише від типу instance, але і від моделі оплати. Правильний вибір може заощадити 50–90% витрат.

On-Demand — оплата по факту

Принцип: платите за кожну секунду роботи instance. Немає зобов'язань, немає передоплати. Запустили — платите. Зупинили — не платите.

Коли використовувати: навчання, розробка, тестування, нерегулярні задачі, коли ви ще не знаєте, скільки ресурсів потрібно.

Ціна (eu-central-1): t3.micro ≈ $0.0116/год ≈ $8.4/місяць при 24/7 роботі.

Reserved Instances та Savings Plans

Принцип: ви зобов'язуєтесь використовувати певну кількість ресурсів протягом 1 або 3 років і отримуєте знижку 30–75% відносно On-Demand.

Reserved Instances (RI): резервуєте конкретний тип instance (t3.medium) у конкретному регіоні. Якщо вам раптом потрібен більший instance — RI не застосовується.

Savings Plans: гнучкіша альтернатива. Ви зобов'язуєтесь витрачати N доларів на годину (наприклад $0.05/год), а знижка застосовується автоматично до будь-яких EC2 instances та Lambda, навіть якщо ви змінили тип.

Коли використовувати: для production серверів, які працюють 24/7 протягом тривалого часу.

Spot Instances — найдешевші, але переривувані

Принцип: AWS продає невикористані EC2 потужності зі знижкою 60–90% відносно On-Demand. Але якщо ці потужності знадобляться AWS — ваш instance може бути примусово зупинений з попередженням за 2 хвилини.

Коли використовувати: batch-обробка даних, ML-тренування, відеорендеринг, CI/CD pipeline — задачі, які можна перервати і продовжити.

Коли НЕ використовувати: для production web-сервера, де переривання неприпустиме.

On-Demand

Оплата посекундно. Без зобов'язань. Ідеально: навчання, розробка, тестування.

Savings Plans

Зобов'язання на 1-3 роки. Знижка 30-75%. Ідеально: стабільні production сервери.

Spot Instances

Знижка 60-90%. Можуть бути перервані. Ідеально: batch-задачі, CI/CD, ML.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

node "EC2 Instance" as EC2 #dbeafe {
    rectangle ".NET API" as API #bbf7d0
}

rectangle "EBS Volumes" as EBS #e9ecef {
    rectangle "Root Volume\ngp3 20GB (ОС)" as ROOT #d1fae5
    rectangle "Data Volume\nio2 100GB (БД)" as DATA #fef3c7
    rectangle "Log Volume\nst1 50GB (Логи)" as LOG #f3f4f6
}

database "EBS Snapshots\n(in S3)" as SNAP #e0e7ff

EC2 -right-> EBS : підключені
ROOT -down-> SNAP : backup щодня
DATA -down-> SNAP : backup щогодини
@enduml

EBS — Elastic Block Store (диски для EC2)

EBS (Elastic Block Store) — це мережеві диски (аналог жорстких дисків), які підключаються до EC2 instances. Кожен EC2 instance має щонайменше один EBS том — root volume з операційною системою.

Ключова особливість EBS: дані зберігаються незалежно від EC2 instance. Якщо зупинити або навіть видалити instance — EBS том залишається і дані не втрачаються (якщо не увімкнено «Delete on termination»).

Типи EBS томів

gp3 (General Purpose SSD) — рекомендований для більшості задач. 3000 IOPS та 125 MB/s за замовчуванням (можна збільшити незалежно від розміру). Ціна: ~$0.08/GB/місяць.

gp2 (застарілий General Purpose SSD) — попереднє покоління. IOPS прив'язані до розміру (3 IOPS/GB). Уникайте для нових проєктів.

io2 (Provisioned IOPS SSD) — для баз даних з інтенсивним I/O. До 64,000 IOPS. Дорожче.

st1 (Throughput Optimized HDD) — для великих об'ємів послідовного читання/запису. Дешевший за SSD, але повільніший.

EBS Snapshots — резервні копії

Snapshot — це точкова копія EBS тому, збережена в S3. Використовується для:

  • Резервного копіювання даних
  • Переміщення даних між регіонами
  • Створення AMI з поточного стану instance
  • Відновлення instance до попереднього стану
aws ec2 describe-volumes + create-snapshot
$ aws ec2 describe-volumes \
> --filters "Name=attachment.instance-id,Values=i-1234567890abcdef0" \
> --query "Volumes[*].VolumeId" \
> --output text --region eu-central-1
vol-0a1b2c3d4e5f67890
$ aws ec2 create-snapshot \
> --volume-id vol-0a1b2c3d4e5f67890 \
> --description "Backup before update" \
> --region eu-central-1
{
"SnapshotId": "snap-0123456789abcdef0",
"VolumeId": "vol-0a1b2c3d4e5f67890",
"State": "pending",
"Progress": "0%"
}

Instance Store — надшвидке тимчасове сховище

Поряд з EBS існує інший тип сховища — Instance Store (ephemeral storage). Це фізично вбудовані NVMe SSD диски на тому самому хості, де запущений instance. Доступний лише для певних типів instances: сімейства i4i, i3, d3, m5d, c5d, r5d та ін.

Характеристики Instance Store:

  • Надзвичайна швидкість: без мережевого hop до окремого сховища — затримка менше 0.1 мс, IOPS до мільйонів. i4i.xlarge дає до 625,000 IOPS проти ~16,000 у gp3 EBS
  • Тимчасовий (ephemeral): дані повністю знищуються при Stop, Terminate або апаратному збої хоста. При Reboot — зберігаються
  • Включений у вартість: Instance Store не тарифікується окремо — ціна входить у cost instance type
Ніколи не зберігайте на Instance Store дані без реплікації. База даних, конфіги, артефакти деплою — зберігайте на EBS або S3. Instance Store підходить лише для тимчасових даних: кеш, temp-директорії, буфери обробки — де втрата некритична або є кластерна реплікація.
ХарактеристикаEBS gp3Instance Store
PersistenceЗберігається при StopЗнищується при Stop
IOPSдо 16,000до 3.3 млн
Latency0.5–4 мс< 0.1 мс
Ціна~$0.08/GB/місяцьВключена в instance
EBS SnapshotТакНі
Зміна розміруТак (без зупинки)Ні (фіксований)
ПризначенняОсновне сховищеКеш, буфери, scratch

Типові сценарії: кеш Elasticsearch (дані є в основному сховищі), буфер черги повідомлень із реплікацією, проміжні файли при обробці відео або ML-пайплайні, scratch space для великих обчислень.


Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

node "EC2 Instance" as EC2 #dbeafe {
    rectangle "Security Group\n(dotnet-api-sg)" as SG #bbf7d0 {
        rectangle "Inbound Rules" as IN #d1fae5
        rectangle "Outbound Rules" as OUT #d1fae5
    }
}

actor "User" as User
actor "Developer" as Dev

User -right-> IN : HTTP(80) from 0.0.0.0/0
Dev -right-> IN : SSH(22) from My IP
IN -right-> "ASP.NET Core API" : allowed
OUT --> "Internet" : all traffic allowed (default)
@enduml

Security Groups — файрвол для EC2

Security Group — це віртуальний файрвол, який контролює вхідний (inbound) і вихідний (outbound) мережевий трафік для EC2 instance. Думайте про нього як про список правил: «дозволити трафік з порту 80» або «заборонити все, крім SSH з нашого IP».

Ключові властивості Security Groups:

  • Stateful (з відстеженням стану): якщо дозволено вхідне з'єднання — відповідний вихідний трафік дозволяється автоматично, і навпаки. Вам не потрібно додавати окреме правило для «відповідь на запит».
  • Дозвільні, не забороняючі: у Security Groups можна лише дозволяти трафік. Не можна написати «заборонити з IP 1.2.3.4». Для заборони — використовуйте NACL.
  • Можна призначити кілька Security Groups одному instance.
  • За замовчуванням: весь вхідний трафік заборонений, весь вихідний — дозволений.

Правила Security Group

Кожне правило описує:

ПолеПрикладОпис
TypeHTTP, SSH, Custom TCPТип трафіку (HTTP автоматично ставить порт 80)
ProtocolTCP, UDP, ICMPПротокол
Port Range80, 443, 8080, 22Порт або діапазон портів
Source0.0.0.0/0, 203.0.113.5/32, sg-xxxЗвідки дозволений трафік

Source 0.0.0.0/0 — означає «з будь-якого IP у світі». Використовуйте лише для публічних портів (80, 443).

Source 203.0.113.5/32 — лише з конкретного IP (наприклад, ваш домашній IP). /32 означає один конкретний IP. Ідеально для SSH — не відкривайте 22 порт для всього світу!

Source sg-0a1b2c3d — лише від інших EC2 instances з цим Security Group. Зручно для мікросервісної архітектури: API може звертатись до БД лише якщо знаходиться в тій самій Security Group.

Security Groups vs NACLs

Студенти часто плутають ці два механізми. Ось принципова різниця:

ХарактеристикаSecurity GroupNetwork ACL (NACL)
Рівень застосуванняEC2 InstanceSubnet
Тип правилЛише AllowAllow та Deny
Stateful/StatelessStatefulStateless
Порядок правилУсі правила перевіряютьсяПравила нумеровані, першe спрацьоване
Типове використанняЩоденне управління доступомБлокування діапазонів IP

Для більшості задач достатньо Security Groups. NACLs — додатковий шар захисту для специфічних сценаріїв (наприклад, заблокувати країну або діапазон IP після DDoS атаки).


VPC — Virtual Private Cloud та мережева топологія EC2

Кожен EC2 instance запускається всередині VPC (Virtual Private Cloud) — ізольованої приватної мережі у хмарі AWS. Розуміння VPC необхідне для проєктування безпечної архітектури: саме VPC визначає, хто може звертатись до вашого instance і куди сам instance може ходити в мережі.

AWS автоматично створює Default VPC у кожному регіоні з готовими публічними підмережами та Internet Gateway. Для навчання та простих проєктів Default VPC цілком підходить — саме його ми використовуємо у практичних прикладах цього розділу.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

cloud "Internet" as INET

rectangle "VPC: 10.0.0.0/16" #dbeafe {
    rectangle "Public Subnet\n10.0.1.0/24" as PUB #d1fae5 {
        node "Load Balancer (ALB)\nабо EC2 (публічний)" as ALB #bbf7d0
    }
    rectangle "Private Subnet\n10.0.2.0/24" as PRIV #fef3c7 {
        node "EC2\n(App Server)" as APP #fde68a
        database "RDS\n(Database)" as DB #fecaca
    }
    rectangle "Internet Gateway" as IGW #e0e7ff
    rectangle "NAT Gateway\n(виходити — так,\nвходити — ні)" as NAT #e0e7ff
}

INET -down-> IGW
IGW -down-> ALB
ALB -down-> APP : internal HTTP
APP -down-> DB : :5432 (private)
APP -up-> NAT : outbound (apt-get, AWS API)
NAT -up-> IGW
IGW -up-> INET

note right of PRIV
  Немає маршруту до IGW —
  недоступна з інтернету напряму.
  Ідеально для БД та backend.
end note
@enduml

Public Subnet vs Private Subnet

Public Subnet (публічна підмережа) — підмережа, таблиця маршрутизації якої містить маршрут 0.0.0.0/0 → igw-xxx до Internet Gateway. Instances тут мають публічний IP і доступні з інтернету (якщо дозволяє Security Group). Сюди розміщують: Application Load Balancer, Bastion Host, публічні API, Elastic IP.

Private Subnet (приватна підмережа) — підмережа без маршруту до IGW. Instances недоступні з інтернету напряму — навіть якщо у них є публічний IP (він є, але маршрут відсутній). Ідеальне місце для: RDS, ElastiCache, backend-сервісів, черг повідомлень.

Правило безпечної архітектури: база даних — завжди у private subnet. Навіть якщо Security Group обмежує доступ — публічний IP на RDS залишається вектором атаки. Глибокий захист (defense in depth) = Security Group плюс приватна підмережа.

Internet Gateway та NAT Gateway

Internet Gateway (IGW) забезпечує двонаправлений трафік між instances у public subnet та інтернетом. Один на VPC, горизонтально масштабований, завжди доступний. Безкоштовний.

NAT Gateway дозволяє instances у private subnet ініціювати вихідні з'єднання (завантажувати оновлення, пакети, звертатись до AWS API), але не дає зовнішнім клієнтам ініціювати вхідні з'єднання. Тобто App Server у private subnet може apt-get update, проте підключитись до нього ззовні неможливо. NAT Gateway платний: $0.045/год ($32/місяць) + трафік ~$0.045/GB.

CIDR — запис діапазонів IP-адрес

CIDR (Classless Inter-Domain Routing) — формат IP/prefix, де prefix — кількість фіксованих бітів:

  • 10.0.0.0/16 → 65,536 адрес (10.0.0.0 – 10.0.255.255) — типово для VPC
  • 10.0.1.0/24 → 256 адрес (10.0.1.0 – 10.0.1.255) — типово для subnet
  • 203.0.113.5/32 → одна конкретна адреса — для SSH правила у Security Group
  • 0.0.0.0/0 → будь-яка адреса у світі — для публічних HTTP/HTTPS правил

Зони доступності та відмовостійкість

Підмережа прив'язана до однієї Availability Zone. Best practice для production: мінімум дві підмережі (public + private) у різних AZ. Тоді збій однієї фізичної зони AWS не вимикає весь застосунок — Application Load Balancer автоматично перенаправляє трафік до instances в іншій AZ.


Elastic IP — стала публічна IP-адреса

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

package "Без Elastic IP" #fef3c7 {
    node "Restart 1\nIP: 3.64.100.10" as N1 #fde68a
    node "Restart 2\nIP: 18.184.55.32 ← змінився!" as N2 #fecaca
}

package "З Elastic IP" #d1fae5 {
    rectangle "Elastic IP: 3.64.185.42\n(стала адреса)" as EIP #bbf7d0
    node "Restart 1" as N3 #e0e7ff
    node "Restart 2" as N4 #e0e7ff
}

EIP -down-> N3
EIP -down-> N4

note right of EIP
  DNS: myapi.pp.ua → 3.64.185.42
  Завжди коректний після рестарту
end note
@enduml

За замовчуванням кожен раз, коли ви зупиняєте і запускаєте EC2 instance — він отримує нову публічну IP-адресу. Це проблема для production: DNS запис вказує на стару IP, і після рестарту сервер «зникає» з інтернету.

Elastic IP (EIP) — це стала публічна IP-адреса, яку ви можете прикріпити до будь-якого EC2 instance. При зупинці/запуску instance — EIP залишається незмінним.

Важливо про ціну: Elastic IP безкоштовний, поки він прикріплений до запущеного instance. Але якщо EIP виділений але не прикріплений (або прикріплений до зупиненого instance) — він коштує $0.005/год ($3.6/місяць). AWS стягує плату за невикористані IP, щоб не допустити вичерпання пулу публічних адрес.

Завжди звільняйте Elastic IP, якщо більше не використовуєте instance. EIP, що висить без instance — це «порожня» витрата. Після видалення instance — EIP лишається у вашому акаунті і тарифікується!

CloudWatch та моніторинг EC2

Запустити застосунок — лише половина роботи. Щоб впевнено керувати production-сервером, потрібно спостерігати за ним: знати завантаження CPU та пам'яті, стежити за здоров'ям instance, отримувати сповіщення до того, як про проблему повідомить користувач. Amazon CloudWatch — сервіс моніторингу AWS, який автоматично збирає метрики з EC2, зберігає логи та надсилає сповіщення.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

node "EC2 Instance" #dbeafe {
    rectangle "Hypervisor\n(CPU, Network, Disk I/O)" as HYP #d1fae5
    rectangle "CloudWatch Agent\n(RAM, Disk %, App Logs)" as CWA #bbf7d0
}

rectangle "Amazon CloudWatch" #e0e7ff {
    rectangle "Metrics\n(5-хв або 1-хв)" as MET #fef3c7
    rectangle "Alarms" as ALR #fde68a
    rectangle "Log Groups\n(/ec2/dotnet-api)" as LOG #f3f4f6
}

rectangle "SNS Topic\n(email / Slack)" as SNS #fecaca
rectangle "Auto Scaling Group\n(scale out/in)" as ASG #d1fae5

HYP -right-> MET : автоматично (безкоштовно)
CWA -right-> MET : кожну хвилину
CWA -right-> LOG : streaming
MET -down-> ALR : threshold
ALR -right-> SNS : сповіщення
ALR -down-> ASG : масштабування
@enduml

Стандартні метрики EC2 (без налаштувань, безкоштовно)

AWS автоматично збирає метрики на рівні гіпервізора кожні 5 хвилин. При Detailed Monitoring (~$0.01/метрика/місяць) — кожну хвилину:

МетрикаЩо вимірюєРекомендований поріг
CPUUtilization% використання CPUAlarm при > 80%
NetworkIn / NetworkOutБайти мережевого трафікуАналіз тренду
DiskReadOps / DiskWriteOpsIOPS операцій до EBS< 3000 для gp3 baseline
StatusCheckFailed_InstanceЗбій перевірки всередині ОСAlarm при > 0
StatusCheckFailed_SystemЗбій апаратного хостуAlarm при > 0

Критична відсутність: стандартні метрики не включають MemoryUtilization та заповненість диска — ці дані недоступні на рівні гіпервізора. Для них потрібен CloudWatch Agent.

CloudWatch Agent — RAM, диск та логи застосунку

CloudWatch Agent — легкий демон, що збирає метрики ОС та надсилає логи застосунку у CloudWatch. Встановлення на Ubuntu:

# Встановлення агента
sudo apt-get install -y amazon-cloudwatch-agent

# Мінімальна конфігурація: RAM + диск + логи застосунку
sudo tee /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json > /dev/null <<'EOF'
{
  "metrics": {
    "append_dimensions": { "InstanceId": "${aws:InstanceId}" },
    "metrics_collected": {
      "mem": { "measurement": ["mem_used_percent"], "metrics_collection_interval": 60 },
      "disk": { "measurement": ["disk_used_percent"], "resources": ["/"], "metrics_collection_interval": 60 }
    }
  },
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [{
          "file_path": "/var/app/logs/app.log",
          "log_group_name": "/ec2/dotnet-api",
          "log_stream_name": "{instance_id}"
        }]
      }
    }
  }
}
EOF

# Запуск агента
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
    -a fetch-config -m ec2 \
    -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s
CloudWatch Agent потребує IAM Role з CloudWatchAgentServerPolicy — додайте її до ролі, прикріпленої до EC2 instance. Без цього дозволу агент не зможе надсилати метрики та логи.

CloudWatch Alarms — автоматичні сповіщення

Alarm спрацьовує коли метрика порушує поріг і надсилає сповіщення через SNS (email, Slack, PagerDuty) або запускає Auto Scaling:

# Alarm: CPU > 80% протягом 5 хвилин
aws cloudwatch put-metric-alarm \
    --alarm-name "high-cpu-dotnet-api" \
    --metric-name CPUUtilization \
    --namespace AWS/EC2 \
    --dimensions Name=InstanceId,Value=$INSTANCE_ID \
    --statistic Average \
    --period 300 \
    --threshold 80 \
    --comparison-operator GreaterThanThreshold \
    --evaluation-periods 1 \
    --alarm-actions arn:aws:sns:eu-central-1:123456789012:alert-topic \
    --region eu-central-1

# Alarm: instance health check failed → негайне сповіщення
aws cloudwatch put-metric-alarm \
    --alarm-name "instance-status-failed" \
    --metric-name StatusCheckFailed_Instance \
    --namespace AWS/EC2 \
    --dimensions Name=InstanceId,Value=$INSTANCE_ID \
    --statistic Maximum \
    --period 60 \
    --threshold 0 \
    --comparison-operator GreaterThanThreshold \
    --evaluation-periods 1 \
    --alarm-actions arn:aws:sns:eu-central-1:123456789012:alert-topic \
    --region eu-central-1
Мінімальний production checklist alarms:CPUUtilization > 80% (перевантаження), StatusCheckFailed_Instance > 0 (instance недоступний), disk_used_percent > 85% (через CloudWatch Agent — диск майже повний). Ці три алерми покривають найпоширеніші аварійні ситуації.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

actor "Developer" as Dev
node "EC2 Instance Launch" as LAUNCH #dbeafe {
    rectangle "User Data Script" as UD #bbf7d0
    queue "First Boot Only" as BOOT #f3f4f6
    rectangle "Install .NET 10" as DOTNET #d1fae5
    rectangle "Setup App Dir" as DIR #d1fae5
}

Dev -right-> LAUNCH : запускає instance
LAUNCH -down-> UD : виконує скрипт
UD -down-> BOOT : один раз
BOOT -down-> DOTNET : apt-get install
BOOT -down-> DIR : mkdir /var/app
@enduml

EC2 User Data — автоматизація при запуску

User Data — це скрипт (bash або PowerShell), який виконується один раз при першому запуску EC2 instance. Це механізм автоматичного налаштування сервера без ручного SSH-підключення.

Приклад User Data для Ubuntu з автоматичним встановленням .NET 10:

#!/bin/bash
# Цей скрипт виконається автоматично при першому запуску instance

# Оновлюємо список пакетів (аналог "перевірити оновлення" у Windows)
apt-get update -y

# .NET 10 входить до стандартних репозиторіїв Ubuntu 26.04 —
# додатковий репозиторій Microsoft не потрібен
# Встановлюємо .NET 10 Runtime (лише для запуску, не розробки)
apt-get install -y dotnet-runtime-10.0

# Створюємо директорію для застосунку
mkdir -p /var/app

# Записуємо лог — щоб переконатись, що скрипт виконався
echo "Setup completed at $(date)" >> /var/app/setup.log

User Data для Windows Server (PowerShell):

<powershell>
# Встановити .NET 10 Hosting Bundle (включає Runtime і IIS модуль)
$url = "https://download.microsoft.com/download/dotnet/10.0/aspnetcore-runtime-10.0.0-win-x64.exe"
$installer = "C:\aspnetcore-installer.exe"
Invoke-WebRequest -Uri $url -OutFile $installer
Start-Process -FilePath $installer -ArgumentList "/quiet /norestart" -Wait

# Встановити IIS та необхідні компоненти
Install-WindowsFeature -Name Web-Server, Web-Asp-Net45, Web-Mgmt-Console -IncludeManagementTools

# Записати лог
"Setup completed at $(Get-Date)" | Out-File C:\setup.log
</powershell>

EC2 Instance Metadata Service (IMDS)

Instance Metadata Service (IMDS) — це внутрішній HTTP-сервіс, доступний з будь-якого EC2 instance за адресою http://169.254.169.254. Він надає інформацію про сам instance: ID, тип, регіон, IAM Role, публічний IP тощо.

Ця адреса (169.254.169.254) — спеціальна «link-local» адреса, доступна лише зсередини instance. Жоден зовнішній комп'ютер не може звернутись до IMDS ззовні — це внутрішній сервіс EC2.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

node "EC2 Instance" as EC2 #dbeafe {
    rectangle ".NET Application\n(AWS SDK)" as APP #bbf7d0
    rectangle "IMDS Client\n(IMDSv2 token)" as CLIENT #d1fae5
}

rectangle "IMDS\n169.254.169.254\n(link-local, лише зсередини)" as IMDS #e0e7ff

database "IAM Role\nCredentials" as IAM #fef3c7
database "Instance Metadata\n(ID, IP, Region...)" as META #fde68a

APP -right-> CLIENT : запит
CLIENT -right-> IMDS : HTTP GET + IMDSv2 token
IMDS -down-> IAM : /iam/security-credentials/
IMDS -down-> META : /instance-id, /public-ipv4...

note bottom of IMDS
  Доступний лише зсередини instance.
  Зовнішній трафік не досягає IMDS.
end note
@enduml

Навіщо IMDS .NET розробнику:

  • AWS SDK for .NET автоматично звертається до IMDS для отримання IAM Role credentials — саме завдяки цьому ваш код на EC2 не потребує Access Keys
  • Можна отримати публічний IP instance прямо зсередині коду — без зовнішніх запитів
  • Визначити, в якому регіоні запущений instance
curl http://169.254.169.254/latest/meta-data/instance-id
# i-1234567890abcdef0

curl http://169.254.169.254/latest/meta-data/public-ipv4
# 3.64.185.42

curl http://169.254.169.254/latest/meta-data/instance-type
# t3.medium

curl -s http://169.254.169.254/latest/meta-data/placement/region
# eu-central-1

IMDSv2 — безпечніша версія: AWS рекомендує використовувати IMDSv2, яка вимагає спочатку отримати токен, а потім використовувати його для запитів. Це захищає від Server-Side Request Forgery (SSRF) атак.

TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
    -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
    http://169.254.169.254/latest/meta-data/instance-id
# i-1234567890abcdef0

IAM Role для EC2 — безпечний доступ без хардкоду ключів

Коли .NET код на EC2 звертається до AWS-сервісів (S3, DynamoDB, SQS, Secrets Manager), йому потрібні облікові дані (credentials) — щоб AWS міг верифікувати, від чийого імені виконуються запити і чи є на це дозвіл. Початківці часто допускають критичну помилку безпеки: хардкодять AWS_ACCESS_KEY_ID та AWS_SECRET_ACCESS_KEY прямо у appsettings.json або .env файл на сервері. Якщо такий файл потрапить у публічний Git-репозиторій — автоматичні сканери знайдуть і використають ці ключі за лічені хвилини.

Правильне рішення — IAM Role, прикріплена до EC2 instance. AWS SDK отримує тимчасові автоматично-ротовані credentials через IMDS. Жодного хардкоду. Жодного ризику витоку ключів.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

actor "DevOps" as DEV #dbeafe

rectangle "AWS IAM" #bbf7d0 {
    rectangle "IAM Role\nec2-api-role" as ROLE #d1fae5
    rectangle "Trust Policy\n(ec2.amazonaws.com)" as TRUST #d1fae5
    rectangle "Permission Policy\n(s3:GetObject, sqs:SendMessage)" as PERM #d1fae5
}

node "EC2 Instance" #fef3c7 {
    rectangle "Instance Profile\n(обгортка для Role)" as PROFILE #fde68a
    rectangle "AWS SDK for .NET\nDefaultAWSCredentials" as SDK #e0e7ff
}

database "IMDS 169.254.169.254\n/iam/security-credentials/" as IMDS #f3f4f6
storage "S3 / SQS / DynamoDB..." as SVC #e0e7ff

DEV -right-> ROLE : створює
ROLE -down-> TRUST
ROLE -down-> PERM
DEV -right-> PROFILE : attach до instance
SDK -down-> IMDS : запит credentials
IMDS -up-> SDK : temp credentials\n(1 год TTL · авто-ротація)
SDK -right-> SVC : підписаний AWS request
@enduml

Як це працює технічно

  1. Адміністратор створює IAM Role з двома складовими:
    • Trust Policy — хто може «взяти» (assume) цю роль. Для EC2: "Service": "ec2.amazonaws.com"
    • Permission Policy — що ця роль може робити (наприклад s3:GetObject на конкретний bucket)
  2. Роль прикріплюється до EC2 через Instance Profile (контейнер для ролі — створюється автоматично через Console)
  3. AWS STS (Security Token Service) видає тимчасові credentials (AccessKeyId + SecretAccessKey + SessionToken) з TTL ~1 година, розміщуючи їх у IMDS за адресою /latest/meta-data/iam/security-credentials/{role-name}
  4. AWS SDK for .NET автоматично запитує ці credentials з IMDS і оновлює їх до закінчення TTL — без жодного коду з вашого боку

Приклад: надати EC2 доступ до S3

  1. IAM → Roles → Create role
  2. Trusted entity: AWS service → EC2 → Next
  3. Permissions: знайдіть і оберіть AmazonS3ReadOnlyAccess → Next
  4. Role name: ec2-s3-readonly-roleCreate role
  5. EC2 → Instances → ваш instance → Actions → Security → Modify IAM role
  6. Оберіть ec2-s3-readonly-roleUpdate IAM role

Використання у .NET коді

З прикріпленою IAM Role AWS SDK for .NET автоматично знаходить credentials — жодного конфігурування не потрібно:

// Нічого конфігурувати! SDK сам знайде credentials через ланцюжок провайдерів:
// 1. Змінні середовища → 2. ~/.aws/credentials → 3. IAM Role через IMDS ← спрацює на EC2
var s3Client = new AmazonS3Client(RegionEndpoint.EUCentral1);

// Читання конфігу зі S3 замість хардкоду у репозиторії
var response = await s3Client.GetObjectAsync("my-config-bucket", "appsettings.Production.json");
using var reader = new StreamReader(response.ResponseStream);
var configJson = await reader.ReadToEndAsync();

SDK автоматично оновлює тимчасові credentials до закінчення TTL — ваш код не зупиняється при ротації.

Принцип мінімальних привілеїв (Least Privilege): надавайте IAM Role лише необхідні дозволи. Якщо API лише читає один S3 bucket — не давайте AmazonS3FullAccess. Натомість — власна policy з s3:GetObject та s3:ListBucket на arn:aws:s3:::my-bucket/*. Це мінімізує збитки у разі компрометації instance.

EC2 Instance Connect — підключення без SSH ключів

EC2 Instance Connect — це сервіс AWS, який дозволяє підключитись до EC2 через браузер або CLI без збереження SSH приватних ключів локально. AWS тимчасово завантажує ваш публічний ключ у instance на 60 секунд.

Через Console: EC2 → Instances → оберіть instance → Connect → вкладка EC2 Instance ConnectConnect. Відкриється термінал прямо у браузері.

Через CLI:

EC2 Instance Connect CLI — підключення без .pem ключа
$ pip install ec2instanceconnectcli
Collecting ec2instanceconnectcli
Successfully installed ec2instanceconnectcli-1.0.3
$ mssh ec2-user@i-1234567890abcdef0 --region eu-central-1
ec2-user@ip-172-31-10-25:~$
EC2 Instance Connect не вимагає відкритого порту 22 для всього світу. Достатньо дозволити трафік із IP-діапазону AWS для EC2 Instance Connect. AWS публікує ці діапазони.

SSH Key Pair та PEM — аутентифікація без пароля

Коли ви підключаєтесь до EC2 через SSH — AWS не просить ввести пароль. Замість цього використовується асиметрична криптографія: пара математично пов'язаних ключів. Для студентів, які звикли до логін/пароль — це принципово інший підхід, і важливо зрозуміти, як він працює.

Асиметрична криптографія: дві половини одного ключа

Звичайна (симетрична) криптографія: один ключ, яким і шифрують, і розшифровують. Якщо цей ключ вкрасти — система компрометована.

Асиметрична криптографія (Public-Key Cryptography) використовує дві математично пов'язані частини:

Публічний ключ (Public Key)

Можна роздавати будь-кому — розміщувати на серверах, публікувати у профілі GitHub. Компрометація публічного ключа нічого не дає зловмиснику. Сервер зберігає публічний ключ та перевіряє за ним підпис клієнта.

Приватний ключ (Private Key)

Зберігається лише у вас — ніколи не передається нікуди. Це ваш цифровий підпис. Клієнт (SSH) підписує запит приватним ключем. Якщо підпис верифікується публічним ключем — автентифікація пройшла.

Математична властивість: пара генерується так, що з публічного ключа практично неможливо відновити приватний — навіть за необмеженого часу та потужності комп'ютерів сучасної ери. Алгоритм RSA базується на задачі факторизації великих чисел (1024–4096 біт), Ed25519 — на математиці еліптичних кривих.

Loading diagram...
@startuml
skinparam backgroundColor #1e1e2e
skinparam defaultFontColor #cdd6f4
skinparam ArrowColor #89b4fa
skinparam SequenceLifeLineBorderColor #585b70
skinparam SequenceParticipantBorderColor #585b70
skinparam SequenceParticipantBackgroundColor #313244
skinparam SequenceParticipantFontColor #cdd6f4
skinparam NoteBackgroundColor #45475a
skinparam NoteBorderColor #585b70
skinparam NoteFontColor #cdd6f4

participant "SSH Client\n(ваш комп'ютер)" as C
participant "EC2 Instance\n(сервер)" as S

note over C: Має приватний ключ\n(ec2-lab-key.pem)
note over S: Має публічний ключ\n(у ~/.ssh/authorized_keys)

C -> S: TCP з'єднання на порт 22
S -> C: Challenge (випадковий рядок)
note over C: Підписує challenge\nприватним ключем
C -> S: Підпис (Signature)
note over S: Верифікує підпис\nпублічним ключем
S -> C: Authentication OK
note over C,S: Зашифрований\nSSH-сеанс встановлено
@enduml

Що таке .pem файл?

PEM (Privacy Enhanced Mail) — текстовий формат для зберігання криптографічних об'єктів: ключів, сертифікатів. Назва застаріла (формат виник в email-стандартах 1990-х), але використовується досі.

PEM-файл — це Base64-закодований двійковий об'єкт між текстовими маркерами:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0Z3VS5JJcds3xHn/ygWep4AyHgMZJmANwNdGv4VWBWCA
...кілька сотень символів Base64...
tQJ5LMzKvOkzjJHJHAIDAQABAoIBAC5RgZ+hBx7xHNaMpPgwGALBSa9BFKQR
-----END RSA PRIVATE KEY-----

Файл .pem — це лише контейнер. Він може містити:

  • RSA приватний ключ (що й відбувається при створенні EC2 Key Pair)
  • Сертифікат X.509 (HTTPS-сертифікат вашого сайту — теж у PEM-форматі)
  • Ланцюжок сертифікатів (Certificate Authority chain)
.pem, .key, .crt — часто лише різні розширення для одного й того ж Base64-формату. Відрізнити можна лише за -----BEGIN ...----- маркером всередині.

Як EC2 Key Pair працює: від генерації до входу

При натисканні Create key pair у AWS Console відбувається наступне:

1. AWS генерує пару RSA-2048 або Ed25519 ключів на своїх серверах
2. Публічний ключ → зберігається в AWS, записується в EC2 instance при запуску
   (у файл /home/ubuntu/.ssh/authorized_keys)
3. Приватний ключ → завантажується вам у вигляді .pem файлу і ВИДАЛЯЄТЬСЯ з AWS
4. AWS більше не має вашого приватного ключа — лише ви

Тому якщо ви втратили .pem файл — підключитись до цього instance вже неможливо (лише через EC2 Instance Connect або монтування EBS-тому до іншого instance).

Порівняння: пароль vs ключ

КритерійПарольSSH Key Pair
Передача по мережіПароль/хеш передаєтьсяПриватний ключ ніколи не передається
Brute-forceВразливий (можна перебрати)Неможливий (2048-бітний ключ)
ФішингМожна обманом отриматиНе допоможе без файлу
ЗручністьПотрібно пам'ятатиПотрібен доступ до файлу
Зберігання на серверіХеш паролю у /etc/shadowПублічний ключ у authorized_keys
ВідкликанняЗмінити парольВидалити рядок з authorized_keys

Формати ключів: .pem vs .ppk

Mac / Linux — SSH вбудований у систему і підтримує .pem напряму:

ssh -i ~/.ssh/ec2-lab-key.pem ubuntu@1.2.3.4

Windows — старіші версії використовують PuTTY, який має власний формат .ppk. При створенні ключа в AWS Console для Windows потрібно обирати .ppk. Якщо у вас .pem, конвертуйте через PuTTYgen.

Windows 10/11 мають вбудований OpenSSH (з 2018 року). У PowerShell або CMD команда ssh -i працює так само, як на Mac/Linux. .ppk потрібен лише якщо ви використовуєте PuTTY свідомо.

Безпека: chmod 400 та зберігання ключа

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

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'ec2-lab-key.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.

Команда chmod 400 ~/.ssh/ec2-lab-key.pem встановлює права -r--------: читання дозволено лише власнику, нікому більше.

400 = 4 (read) + 0 (---) + 0 (---) = власник може читати, група і всі інші — нічого

Де зберігати .pem файл:

  • ~/.ssh/ — стандартна директорія для SSH-ключів
  • ✅ Менеджер паролів з можливістю зберігання файлів (1Password, Bitwarden)
  • ✅ Зашифрований архів у хмарному сховищі
  • ❌ Робочий стіл, папка Downloads, будь-яке синхронізоване сховище без шифрування
  • ❌ Git-репозиторій (навіть приватний)
Якщо ви випадково закомітили .pem файл у Git — негайно видаліть Key Pair в AWS Console і створіть новий. Навіть якщо репозиторій приватний — він міг бути вже прочитаний до видалення.

Практичний приклад: .NET 10 API на Linux (Ubuntu) від А до Я

У цьому прикладі ми запустимо EC2 instance з Ubuntu, встановимо .NET 10, задеплоїмо ASP.NET Core API і налаштуємо його як системний сервіс, що автоматично стартує при перезапуску сервера.

Крок 1: Запуск EC2 instance

  1. Відкрийте EC2 у AWS Console → InstancesLaunch instances
  2. Name: dotnet-api-server
  3. Application and OS Images (AMI):
    • Натисніть Ubuntu у Quick Start
    • Оберіть Ubuntu Server 26.04 LTS (HVM), SSD Volume Type
    • Architecture: 64-bit (x86)
  4. Instance type: t3.micro (Free Tier) або t3.medium для реального навантаження
  5. Key pair (login):
    • Натисніть Create new key pair
    • Key pair name: ec2-lab-key
    • Key pair type: RSA
    • Private key file format: .pem (для Mac/Linux) або .ppk (для Windows з PuTTY)
    • Натисніть Create key pair — файл .pem автоматично завантажиться
    • Збережіть цей файл! Його неможливо завантажити повторно.
  6. Network settings:
    • Натисніть Edit
    • VPC: залиште default
    • Subnet: залиште default
    • Auto-assign public IP: Enable
    • Firewall (security groups): Create security group
      • Security group name: dotnet-api-sg
      • ✅ Allow SSH traffic from: My IP (AWS автоматично визначить ваш поточний IP)
      • ✅ Allow HTTP traffic from the internet (порт 80)
  7. Configure storage: 20 GB gp3 (достатньо)
  8. Натисніть Launch instance
aws ec2 describe-instances — очікування статусу running
$ aws ec2 describe-instances --instance-ids $INSTANCE_ID \
> --query "Reservations[0].Instances[0].State.Name" \
> --output text --region eu-central-1
pending
# Через 2 хвилини...
$ aws ec2 describe-instances --instance-ids $INSTANCE_ID \
> --query "Reservations[0].Instances[0].State.Name" \
> --output text --region eu-central-1
running

Зачекайте ~2 хвилини поки instance запуститься. Стан зміниться з pending на running.


Крок 2: Підключення до сервера через SSH

SSH (Secure Shell) — це протокол для безпечного підключення до віддаленого комп'ютера через термінал. Це як «Remote Desktop» але текстовий. Для Linux серверів SSH — стандартний спосіб роботи.

Спочатку знайдіть публічну IP-адресу вашого instance:

  1. EC2 → Instances → оберіть dotnet-api-server
  2. У вкладці Details знайдіть Public IPv4 address
  3. Скопіюйте цей IP (наприклад: 3.64.185.42)

Тепер підключіться через SSH:

SSH підключення до EC2
$ ssh -i ~/.ssh/ec2-lab-key.pem ubuntu@3.64.185.42
The authenticity of host '3.64.185.42' can't be established.
ED25519 key fingerprint is SHA256:abc123...
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '3.64.185.42' (ED25519) to the list of known hosts.
ubuntu@ip-172-31-10-25:~$

Пояснення команди SSH:

  • ssh — команда для підключення
  • -i ~/.ssh/ec2-lab-key.pem — прапорець -i вказує файл ключа автентифікації. ~ означає вашу домашню директорію (/Users/yourname на Mac, C:\Users\yourname на Windows)
  • ubuntu@3.64.185.42 — ім'я користувача (ubuntu для Ubuntu AMI) та IP-адреса сервера

Після підключення ви побачите запрошення командного рядка (ubuntu@ip-172-31-10-25:~$) — це означає, що ви знаходитесь всередині сервера. Всі наступні команди виконуються на сервері, не на вашому комп'ютері.

Якщо отримуєте помилку Permission denied (publickey) — переконайтесь, що файл ключа має права 400: chmod 400 ~/.ssh/ec2-lab-key.pem

Крок 3: Встановлення .NET 10 на Ubuntu

Ви вже підключені до сервера через SSH. Виконайте наступні команди одну за одною.

Що таке apt-get? Це менеджер пакетів Ubuntu — аналог Chocolatey для Windows або Homebrew для Mac. Командою apt-get install ви встановлюєте програми, командою apt-get update — оновлюєте список доступних пакетів.

# Оновлюємо список доступних пакетів у менеджері
# -y означає "погоджуватись автоматично" без підтвердження
sudo apt-get update -y

Що таке sudo? На Linux більшість системних команд вимагають прав адміністратора. sudo (Super User DO) дозволяє виконати команду з правами адміністратора. Без sudo ви отримаєте помилку Permission denied.

Ubuntu 26.04 LTS і .NET 10: починаючи з Ubuntu 26.04, пакет dotnet-sdk-10.0 доступний безпосередньо у стандартних репозиторіях (resolute-updates). Додаткові репозиторії Microsoft (packages-microsoft-prod) більше не потрібні.
Встановлення .NET SDK
ubuntu@ip-172-31-10-25:~$ sudo apt-get install -y dotnet-sdk-10.0
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
  dotnet-apphost-pack-10.0 dotnet-host-10.0 dotnet-hostfxr-10.0 dotnet-runtime-10.0 dotnet-sdk-10.0
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
Setting up dotnet-sdk-10.0 (10.0.107-0ubuntu1~26.04.1)... Done
# Перевірте встановлення
dotnet --version
Перевірка версії .NET
ubuntu@ip-172-31-10-25:~$ dotnet --version
10.0.107

Крок 4: Створення та публікація .NET Web API

На вашому локальному комп'ютері (відкрийте новий термінал, не SSH-сесію) створіть проєкт:

# Створюємо новий Web API проєкт
dotnet new webapi -n Ec2LabApi --no-openapi
cd Ec2LabApi

Замінимо вміст Program.cs на наступний:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Відображаємо інформацію про сервер — корисно для перевірки
app.MapGet("/", () => new
{
    message = "Hello from EC2!",
    server = Environment.MachineName,
    dotnetVersion = Environment.Version.ToString(),
    timestamp = DateTime.UtcNow
});

app.MapGet("/health", () => Results.Ok("Healthy"));

app.Run();

Опублікуємо додаток для Linux (навіть якщо ви на Mac або Windows):

# Публікуємо для Linux x64
# -r linux-x64 вказує цільову платформу
# --self-contained false означає що на сервері має бути .NET Runtime (ми його вже встановили)
# -o ./publish вказує куди зберегти результат
dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish
dotnet publish
$ dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish
Determining projects to restore...
Restored Ec2LabApi.csproj (2.1s)
Ec2LabApi -> ./bin/Release/net10.0/linux-x64/Ec2LabApi.dll
Ec2LabApi -> ./publish/
Build succeeded.

Крок 5: Копіювання файлів на сервер через SCP

SCP (Secure Copy Protocol) — утиліта для копіювання файлів між комп'ютерами через SSH. Синтаксис: scp [файл-звідки] [куди].

На вашому локальному комп'ютері (в директорії Ec2LabApi):

# Копіюємо папку publish на сервер
# -r означає рекурсивно (разом зі всіма підпапками)
# -i вказує SSH ключ (той самий, що для ssh)
# ЗАМІНІТЬ 3.64.185.42 на ваш реальний Public IP
scp -r -i ~/.ssh/ec2-lab-key.pem ./publish ubuntu@3.64.185.42:/tmp/ec2lab-publish
scp копіювання
$ scp -r -i ~/.ssh/ec2-lab-key.pem ./publish ubuntu@3.64.185.42:/tmp/ec2lab-publish
Ec2LabApi.dll 100% 182KB 1.2MB/s 00:00
Ec2LabApi.pdb 100% 268KB 1.3MB/s 00:00
Ec2LabApi.runtimeconfig.json 100% 151B 12.1KB/s 00:00
appsettings.json 100% 141B 11.3KB/s 00:00

Поверніться до SSH-сесії (термінал де ви підключені до сервера). Перемістіть файли:

# Переміщуємо з тимчасової директорії у постійну
sudo mv /tmp/ec2lab-publish /var/app

# Перевіримо, що файли є
ls -la /var/app/
Список файлів на сервері
ubuntu@ip-172-31-10-25:~$ ls -la /var/app/
total 204
drwxr-xr-x 2 root root 4096 Jan 15 10:30 .
drwxr-xr-x 8 root root 4096 Jan 15 10:30 ..
-rwxr-xr-x 1 root root 182456 Jan 15 10:30 Ec2LabApi.dll
-rw-r--r-- 1 root root 141 Jan 15 10:30 appsettings.json
-rw-r--r-- 1 root root 151 Jan 15 10:30 Ec2LabApi.runtimeconfig.json

Крок 6: Запуск і тестування

# Переходимо в директорію додатку
cd /var/app

# Запускаємо .NET API
# ASPNETCORE_URLS вказує на якому порту слухати
# & в кінці запускає процес у фоновому режимі
ASPNETCORE_URLS="http://+:5000" dotnet Ec2LabApi.dll &
Запуск API
ubuntu@ip-172-31-10-25:/var/app$ ASPNETCORE_URLS="http://+:5000" dotnet Ec2LabApi.dll &
[1] 12345
info: Microsoft.Hosting.Lifetime[14]
     Now listening on: http://[::]:5000
info: Microsoft.Hosting.Lifetime[0]
     Application started. Press Ctrl+C to shut down.

Перевіримо прямо на сервері:

# curl — консольний HTTP клієнт
# Виконуємо HTTP запит до нашого API (який слухає на localhost:5000)
curl http://localhost:5000/
curl тест на сервері
ubuntu@ip-172-31-10-25:/var/app$ curl http://localhost:5000/
{"message":"Hello from EC2!","server":"ip-172-31-10-25","dotnetVersion":"10.0.7","timestamp":"2026-01-15T10:35:00Z"}

Тепер перевіримо з вашого локального комп'ютера (замініть IP на ваш):

# ЗАМІНІТЬ 3.64.185.42 на ваш реальний Public IP
curl http://3.64.185.42:5000/
# {"message":"Hello from EC2!","server":"ip-172-31-10-25","dotnetVersion":"10.0.7","timestamp":"2026-01-15T10:35:05Z"}

API працює у хмарі! Можна відкрити у браузері: http://3.64.185.42:5000


Крок 7: Налаштування Systemd Service — автозапуск після рестарту

Запуск через & — тимчасовий. Якщо сервер перезавантажиться — API не запуститься автоматично. Systemd — це система управління сервісами на Linux. Вона запускає сервіси при старті ОС, перезапускає їх при збоях, збирає логи.

Зупиніть поточний процес:

# Знайдіть PID (Process ID) запущеного dotnet процесу
# ps aux — показати всі запущені процеси
# grep dotnet — відфільтрувати лише рядки зі словом "dotnet"
ps aux | grep dotnet
# Виведе: ubuntu  12345  ... dotnet Ec2LabApi.dll

# Завершіть процес за PID
kill 12345

Створіть файл unit для systemd:

# nano — простий текстовий редактор у терміналі
# Ctrl+O — зберегти, Ctrl+X — вийти
sudo nano /etc/systemd/system/ec2lab-api.service

У відкритому редакторі вставте наступний вміст:

[Unit]
Description=EC2 Lab .NET API
# Запускати після того, як мережа буде готова
After=network.target

[Service]
# Користувач під яким запускається процес
User=ubuntu
# Робоча директорія
WorkingDirectory=/var/app
# Команда запуску
ExecStart=/usr/bin/dotnet /var/app/Ec2LabApi.dll
# Автоматично перезапускати якщо процес впав
Restart=always
# Чекати 10 секунд перед перезапуском
RestartSec=10
# Змінні середовища
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://+:5000

[Install]
# Запускати у стандартному multi-user режимі
WantedBy=multi-user.target

Збережіть: натисніть Ctrl+O (підтвердіть Enter) → Ctrl+X для виходу.

# Перечитати конфігурацію systemd (потрібно після кожної зміни .service файлу)
sudo systemctl daemon-reload

# Увімкнути автозапуск при старті системи
sudo systemctl enable ec2lab-api

# Запустити сервіс прямо зараз
sudo systemctl start ec2lab-api

# Перевірити статус
sudo systemctl status ec2lab-api
systemctl status
$ sudo systemctl status ec2lab-api
● ec2lab-api.service - EC2 Lab .NET API
Loaded: loaded (/etc/systemd/system/ec2lab-api.service; enabled)
Active: active (running) since Mon 2024-01-15 10:40:00 UTC
Main PID: 13456 (dotnet)
Tasks: 19 (limit: 1057)
Memory: 52.1M
Jan 15 10:40:01 ip-172-31 dotnet[13456]: info: Lifetime Now listening on: http://[::]:5000
# Переглянути логи сервісу
sudo journalctl -u ec2lab-api -n 50 --no-pager

# Слідкувати за логами в реальному часі (Ctrl+C щоб вийти)
sudo journalctl -u ec2lab-api -f

Перезавантажте сервер і переконайтесь, що API стартує автоматично:

sudo reboot
# Підключення закриється. Зачекайте 1-2 хвилини і підключіться знову через SSH.
# Потім перевірте: curl http://localhost:5000/

Крок 8 (бонус): Підключення безкоштовного домену pp.ua до EC2

Зараз ваш API доступний за IP-адресою (http://3.64.185.42:5000). Але IP-адреса — це незручно: вона може змінитись, її важко запам'ятати. Підключимо безкоштовний домен pp.ua.

Загальна схема:

myapi.pp.ua
    ↓ A record (DNS)
3.64.185.42 (Elastic IP вашого EC2)
    ↓
EC2 instance → .NET API (порт 80 через nginx)
Чому Elastic IP? Публічна IP-адреса EC2 instance змінюється при кожній зупинці/запуску. Якщо прив'язати домен до змінної IP — він «зламається» після рестарту. Elastic IP — стала адреса, що не змінюється. Переконайтесь що ви виділили Elastic IP та прив'язали до instance (Крок «Elastic IP» у теоретичній частині цього модуля).

8a: Виділення та прив'язка Elastic IP

  1. EC2 → Elastic IPsAllocate Elastic IP addressAllocate
  2. Оберіть щойно виділений IP → ActionsAssociate Elastic IP address
  3. Instance: оберіть ваш dotnet-api-serverAssociate
  4. Запишіть Elastic IP (наприклад 3.64.185.42) — саме цю адресу вкажете у DNS

8b: Встановлення nginx як reverse proxy (порт 80)

.NET API слухає на порті 5000. Відкривати 5000 публічно — незручно (користувачам доведеться вводити myapi.pp.ua:5000). Краще поставити nginx — lightweight веб-сервер, який приймає запити на порту 80 і проксіює їх на 5000.

На сервері (через SSH):

# Встановити nginx
sudo apt-get install -y nginx

# Створити конфігурацію для нашого API
sudo nano /etc/nginx/sites-available/ec2lab-api

Вставте в редакторі (Ctrl+O зберегти, Ctrl+X вийти):

server {
    listen 80;
    # ЗАМІНІТЬ myapi.pp.ua на ваш реальний субдомен
    server_name myapi.pp.ua;

    location / {
        # Проксіювати запити до .NET API на порту 5000
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        # Передати реальний IP клієнта до .NET API
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_cache_bypass $http_upgrade;
    }
}
# Увімкнути конфігурацію (створити symbolic link)
sudo ln -s /etc/nginx/sites-available/ec2lab-api /etc/nginx/sites-enabled/

# Видалити дефолтну конфігурацію nginx
sudo rm /etc/nginx/sites-enabled/default

# Перевірити синтаксис конфігурації
sudo nginx -t

# Перезапустити nginx
sudo systemctl restart nginx
sudo systemctl enable nginx
nginx -t перевірка конфігурації
ubuntu@ip-172-31-10-25:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Також переконайтесь, що Security Group дозволяє HTTP (порт 80) — він вже мав бути відкритий при створенні instance.

8c: Реєстрація субдомену на pp.ua через nic.ua

Що таке pp.ua? Це безкоштовна зона субдоменів в українському домені .ua. Зона .pp.ua (Personal Page) дозволяє кожному отримати безкоштовний домен виду yourname.pp.ua без оплати і без прив'язки до хостинг-провайдера. Реєстрація та DNS-управління здійснюється через nic.ua — офіційний реєстр доменних імен України.

Крок 1: Створення акаунту на nic.ua

  1. Перейдіть на https://nic.ua
  2. Натисніть «Реєстрація» (або «Увійти»«Зареєструватися»)
  3. Заповніть форму реєстрації:
    • Email — ваша поштова адреса (важлива, підтвердження прийде сюди)
    • Пароль — мінімум 8 символів
    • Ім'я та прізвище — ваші реальні дані (потрібні для домену)
    • Телефон — може знадобитись для верифікації
  4. Натисніть «Зареєструватись»
  5. Перейдіть у свою пошту → відкрийте лист від nic.ua → натисніть посилання підтвердження
Акаунт nic.ua безкоштовний. Вам не потрібно вводити дані картки для реєстрації безкоштовних .pp.ua доменів.

Крок 2: Пошук та реєстрація домену

  1. Після входу в акаунт — у пошуковому рядку на головній сторінці введіть бажаний субдомен: myapi.pp.ua
  2. Натисніть «Перевірити» або клавішу Enter
  3. Якщо домен вільний — ви побачите статус «Доступний» та кнопку «Замовити»
  4. Натисніть «Замовити» → додасться в корзину
  5. Ціна: 0 грнpp.ua домени безкоштовні
  6. Натисніть «Оформити замовлення»
  7. Підтвердіть дані реєстранта (власника домену) — вони підтягнуться з вашого профілю
  8. Натисніть «Підтвердити» або «Зареєструвати»
Вибирайте коротке запам'ятовуване ім'я. Субдомен може містити лише латинські букви, цифри та дефіс. Не може починатись або закінчуватись дефісом. Наприклад: myapi, dotnet-lab, ec2-demo.

Крок 3: Перехід до DNS Management

Після реєстрації домен з'явиться у вашому особистому кабінеті:

  1. Натисніть «Мої домени» або перейдіть до розділу «Домени» в боковому меню
  2. Знайдіть ваш myapi.pp.ua у списку
  3. Натисніть на назву домену → відкриється сторінка управління
  4. Перейдіть на вкладку «DNS» або «Управління DNS»
За замовчуванням nic.ua виступає DNS-хостом для вашого домену. Вам не потрібно змінювати NS-сервери — ви вже можете додавати записи безпосередньо в їхній панелі.

8d: Додавання A record у DNS панелі nic.ua

У розділі «DNS» вашого домену натисніть «Додати запис» або «Add Record» і заповніть:

ПолеЗначення
Тип / TypeA
Ім'я / Name@ (або залиште порожнім — для кореневого myapi.pp.ua)
Значення / IP3.64.185.42 (ваш Elastic IP)
TTL300

Натисніть «Зберегти» або «Додати».

Що таке A record? DNS запис типу A (Address) вказує браузеру яку IP-адресу використовувати для домену. Коли браузер відкриває myapi.pp.ua — він спочатку запитує DNS: «яка IP-адреса у myapi.pp.ua?» → отримує 3.64.185.42 → підключається до цього IP.

Поле «Ім'я» у DNS панелі nic.ua вже містить суфікс домену. Якщо ваш домен myapi.pp.ua, а ви хочете налаштувати саме цей домен (не підсубдомен виду www.myapi.pp.ua) — вводьте @ або залишайте порожнім. Якщо хочете www.myapi.pp.ua — введіть www.

Зачекайте 1–10 хвилин поки DNS оновиться. Перевірка:

DNS перевірка через nslookup
$ nslookup myapi.pp.ua
Server: 1.1.1.1
Non-authoritative answer:
Name: myapi.pp.ua
Address: 3.64.185.42
$ curl http://myapi.pp.ua/
{"message":"Hello from EC2!","server":"ip-172-31-10-25",...}

Відкрийте http://myapi.pp.ua у браузері — ваш .NET API доступний через зручний домен!

8e: HTTPS через Let's Encrypt та Certbot

Чому не ACM? AWS Certificate Manager (ACM) — безкоштовний SSL-сервіс AWS, але його сертифікати не можна встановити безпосередньо на EC2. ACM працює лише через ALB (Application Load Balancer) та CloudFront. Для HTTPS безпосередньо на EC2 — використовується Let's Encrypt.

Що таке Let's Encrypt? Це безкоштовний, автоматизований та відкритий центр сертифікації (CA), що видає TLS/SSL сертифікати. Сертифікати дійсні 90 днів і оновлюються автоматично. Certbot — офіційний клієнт Let's Encrypt, який автоматично отримує сертифікат і налаштовує nginx.

Як Let's Encrypt перевіряє що ви власник домену? Через HTTP-01 challenge: Certbot створює тимчасовий файл на сервері за адресою http://myapi.pp.ua/.well-known/acme-challenge/..., сервери Let's Encrypt перевіряють його доступність через HTTP (порт 80) → якщо файл доступний — ви підтверджуєте права на домен → видається сертифікат.

Передумови перед запуском Certbot:
  • Ваш домен myapi.pp.ua вже вказує на Elastic IP вашого EC2 (A record налаштований, DNS розповсюдився)
  • nginx запущений і відповідає на HTTP запити (curl http://myapi.pp.ua)
  • У Security Group відкриті порти 80 (HTTP) та 443 (HTTPS) — перевірте зараз

Крок 1: Відкрити порт 443 у Security Group

Перейдіть: EC2 → Instances → ваш instance → Security → Security groups → Edit inbound rules → Add rule:

TypeProtocolPortSource
HTTPSTCP4430.0.0.0/0

Або через AWS CLI:

# Отримати ID вашої Security Group
aws ec2 describe-instances \
  --instance-ids i-0abc123def456789 \
  --query "Reservations[0].Instances[0].SecurityGroups[0].GroupId" \
  --output text

# Додати правило для HTTPS
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 443 \
  --cidr 0.0.0.0/0

Крок 2: Встановити Certbot з плагіном nginx

sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
Встановлення Certbot
ubuntu@ip-172-31-10-25:~$ sudo apt-get install -y certbot python3-certbot-nginx
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
certbot python3-certbot python3-certbot-nginx python3-acme
Setting up certbot (2.12.0-1) ...
Setting up python3-certbot-nginx (2.12.0-1) ...

Крок 3: Отримати SSL сертифікат

# ЗАМІНІТЬ myapi.pp.ua на ваш реальний домен
sudo certbot --nginx -d myapi.pp.ua

Certbot задасть вам кілька запитань в інтерактивному режимі:

Запити Certbot під час отримання сертифікату
ubuntu@ip-172-31-10-25:~$ sudo certbot --nginx -d myapi.pp.ua
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (for urgent renewal/security notices):
# Введіть ваш email → натисніть Enter
your@email.com
Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf
(A)gree/(C)ancel:
# Введіть: A
A
Would you be willing to share your email address with EFF? (Y)es/(N)o:
# Введіть: N (або Y — на ваш вибір)
N
# Certbot автоматично верифікує домен через HTTP-01 challenge...
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for myapi.pp.ua
Waiting for verification...
Cleaning up challenges
Deploying certificate to VirtualHost /etc/nginx/sites-enabled/ec2lab-api
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/ec2lab-api
Congratulations! You have successfully enabled https://myapi.pp.ua
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/myapi.pp.ua/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/myapi.pp.ua/privkey.pem
Your certificate will expire on 2026-08-19. Make sure to renew
it before then.

Що Certbot зробив автоматично? Він модифікував ваш файл /etc/nginx/sites-available/ec2lab-api — додав SSL директиви та редирект з HTTP на HTTPS:

server {
    # Certbot додав: слухати порт 443 з SSL
    listen 443 ssl;
    server_name myapi.pp.ua;

    # Certbot додав: шляхи до сертифікатів
    ssl_certificate /etc/letsencrypt/live/myapi.pp.ua/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapi.pp.ua/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_cache_bypass $http_upgrade;
    }
}

# Certbot додав: редирект HTTP → HTTPS
server {
    listen 80;
    server_name myapi.pp.ua;
    return 301 https://$host$request_uri;
}

Крок 4: Перевірити що HTTPS працює

# Перевірити що nginx запущений після змін Certbot
sudo systemctl status nginx

# Перевірити відповідь через HTTPS
curl -I https://myapi.pp.ua/

# Перевірити деталі сертифікату
curl -v https://myapi.pp.ua/ 2>&1 | grep -A5 "SSL connection"
Перевірка HTTPS через curl
ubuntu@ip-172-31-10-25:~$ curl -I https://myapi.pp.ua/
HTTP/2 200
server: nginx/1.26.0 (Ubuntu)
content-type: application/json; charset=utf-8
date: Wed, 21 May 2026 14:30:00 GMT
ubuntu@ip-172-31-10-25:~$ curl -I http://myapi.pp.ua/
HTTP/1.1 301 Moved Permanently
Location: https://myapi.pp.ua/
# HTTP автоматично перенаправляє на HTTPS ✓
Відкрийте https://myapi.pp.ua у браузері — ви побачите замочок у адресному рядку. Клацніть на замочок → «Connection is secure»«Certificate is valid» → там буде вказано «Let's Encrypt» як видавця і дата закінчення дії.

Крок 5: Перевірити автоматичне оновлення сертифікатів

Let's Encrypt сертифікати дійсні 90 днів. Certbot при встановленні автоматично налаштовує systemd timer для щоденної перевірки і оновлення (якщо залишилось менше 30 днів).

# Перевірити що timer активний
sudo systemctl status certbot.timer

# Перевірити коли наступне оновлення
sudo systemctl list-timers certbot.timer

# Протестувати оновлення без реальних змін (dry run)
sudo certbot renew --dry-run
Перевірка certbot.timer та dry-run тест
ubuntu@ip-172-31-10-25:~$ sudo systemctl status certbot.timer
● certbot.timer - Run certbot twice daily
Loaded: loaded (/lib/systemd/system/certbot.timer; enabled)
Active: active (waiting)
Trigger: Thu 2026-05-22 00:00:00 UTC; 9h left
ubuntu@ip-172-31-10-25:~$ sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/myapi.pp.ua.conf
- - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/myapi.pp.ua/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry **

Де зберігаються сертифікати?

ls -la /etc/letsencrypt/live/myapi.pp.ua/
ФайлПризначення
fullchain.pemВаш сертифікат + проміжний ланцюжок CA (для nginx/Apache)
privkey.pemПриватний ключ (тримайте в секреті, не передавайте)
cert.pemЛише ваш сертифікат (без ланцюжка)
chain.pemЛише проміжний ланцюжок Let's Encrypt
Всі файли в /etc/letsencrypt/live/ — це симлінки на актуальні версії. Ніколи не копіюйте і не переміщуйте їх вручну. При оновленні сертифікату Certbot автоматично оновлює файли, на які вказують ці симлінки.

Типові помилки та рішення:

ПомилкаПричинаРішення
Connection refused / Timeout during connectПорт 80 закритий у Security GroupДодайте inbound rule HTTP (80) для 0.0.0.0/0
DNS problem: NXDOMAIN looking up A for myapi.pp.uaDNS ще не розповсюдився або A record неправильнийПеревірте nslookup myapi.pp.ua, зачекайте 5-10 хвилин
too many certificates already issued for pp.uaRate limit Let's Encrypt (5 сертифікатів/тиждень на домен)Зачекайте тиждень або використайте --staging для тестів
Error: The requested nginx plugin does not appear to be installedНе встановлено python3-certbot-nginxsudo apt-get install python3-certbot-nginx

Практичний приклад: ASP.NET Core з IIS на Windows Server

Windows Server EC2 підходить для випадків, коли потрібен IIS (Internet Information Services) — веб-сервер Microsoft, або для legacy .NET Framework додатків.

Крок 1: Запуск Windows Server EC2

  1. EC2 → Launch instances
  2. Name: windows-iis-server
  3. AMI: У пошуку введіть Windows Server 2022 → оберіть Microsoft Windows Server 2022 Base
    • Увага: Windows AMI позначені як Paid — вартість Windows-ліцензії включена в ціну instance (~$0.05/год для t3.medium)
  4. Instance type: t3.medium (мінімум для Windows Server + IIS + .NET)
  5. Key pair: оберіть існуючий ec2-lab-key або створіть новий у форматі .pem
  6. Network settings → Create security group:
    • ✅ Allow RDP traffic from: My IP (порт 3389 — Remote Desktop Protocol)
    • ✅ Allow HTTP traffic from the internet (порт 80)
  7. Configure storage: 50 GB gp3 (Windows займає більше місця)
  8. Launch instance

Крок 2: Отримання пароля адміністратора та підключення через RDP

Windows EC2 використовує RDP (Remote Desktop Protocol) замість SSH. Пароль адміністратора генерується автоматично і шифрується вашим SSH ключем.

Важливо: пароль стає доступним лише через 4–15 хвилин після запуску instance (Windows завершує ініціалізацію).

  1. EC2 → Instanceswindows-iis-serverActionsSecurityGet Windows password
  2. Натисніть Upload private key file → завантажте ваш ec2-lab-key.pem
  3. Натисніть Decrypt password → скопіюйте пароль
  4. Connect → вкладка RDP clientDownload remote desktop file (завантажить .rdp файл)
  5. Відкрийте .rdp файл → введіть пароль (логін: Administrator)

Підключення через RDP (замініть IP та пароль):

  • Windows: відкрийте «Remote Desktop Connection» → введіть IP
  • Mac: встановіть «Microsoft Remote Desktop» з App Store → New Connection

::

::


Крок 3: Встановлення IIS та .NET 10 Hosting Bundle на Windows Server

Після підключення через RDP ви бачите звичайний робочий стіл Windows Server. Відкрийте PowerShell від імені адміністратора:

# Встановити IIS (Internet Information Services) — веб-сервер Microsoft
# Web-Server — базовий IIS
# Web-Asp-Net45 — підтримка ASP.NET
# Web-Mgmt-Console — графічний менеджер IIS
Install-WindowsFeature -Name Web-Server, Web-Asp-Net45, Web-Mgmt-Console `
    -IncludeManagementTools -Restart:$false
Встановлення IIS
Success Restart Needed Exit Code Feature Result
------- -------------- --------- --------------
True No Success {Common HTTP Features, Default Document...}
# Завантажити та встановити .NET 10 ASP.NET Core Hosting Bundle
# Hosting Bundle включає: .NET Runtime + ASP.NET Core Runtime + IIS Module
$url = "https://download.microsoft.com/download/dotnet/10.0/dotnet-hosting-10.0.0-win.exe"
$installer = "$env:TEMP\dotnet-hosting-10.0.0-win.exe"

Write-Host "Завантажуємо .NET 10 Hosting Bundle..."
Invoke-WebRequest -Uri $url -OutFile $installer -UseBasicParsing

Write-Host "Встановлюємо..."
# /quiet — без GUI, /norestart — без автоматичного рестарту
Start-Process -FilePath $installer -ArgumentList "/quiet /norestart" -Wait

Write-Host "Перезапускаємо IIS..."
net stop was /y
net start w3svc

Write-Host "Перевірка встановлення:"
dotnet --version

Крок 4: Публікація та деплой на IIS

На вашому локальному комп'ютері (створіть або використайте той самий проєкт):

# Публікуємо для Windows
dotnet publish -c Release -r win-x64 --self-contained false -o ./publish-win

Скопіюйте папку publish-win на Windows Server. Варіанти:

  • Через RDP — просто перетягніть папку у вікно RDP (якщо включено clipboard)
  • Через S3: завантажте в S3 bucket і завантажте через PowerShell на сервері

Через S3 (рекомендовано):

# На локальному комп'ютері — завантажте в S3
# ЗАМІНІТЬ your-bucket-name на ваш S3 bucket (або створіть через Console)
aws s3 sync ./publish-win s3://your-bucket-name/ec2lab-api/

На Windows Server (PowerShell):

# Завантажити з S3 (AWS SDK вбудований у PowerShell на Amazon AMI)
# ЗАМІНІТЬ your-bucket-name
$destPath = "C:\inetpub\ec2lab-api"
New-Item -ItemType Directory -Path $destPath -Force

# Якщо немає AWS CLI — встановіть
# Invoke-WebRequest "https://awscli.amazonaws.com/AWSCLIV2.msi" -OutFile "$env:TEMP\AWSCLIV2.msi"
# Start-Process msiexec.exe -Wait -ArgumentList "/I $env:TEMP\AWSCLIV2.msi /quiet"

aws s3 sync s3://your-bucket-name/ec2lab-api/ $destPath

Налаштуйте сайт у IIS через PowerShell:

Import-Module WebAdministration

# Створіть Application Pool для .NET 10
New-WebAppPool -Name "Ec2LabApiPool"
$pool = Get-Item "IIS:\AppPools\Ec2LabApiPool"
$pool.managedRuntimeVersion = ""  # No Managed Code (для .NET Core/10)
$pool | Set-Item

# Видаліть Default Web Site (якщо є)
Remove-Website -Name "Default Web Site" -ErrorAction SilentlyContinue

# Створіть новий сайт
New-Website -Name "Ec2LabApi" `
    -Port 80 `
    -PhysicalPath "C:\inetpub\ec2lab-api" `
    -ApplicationPool "Ec2LabApiPool"

# Запустіть сайт
Start-Website -Name "Ec2LabApi"

Write-Host "Сайт запущено! Перейдіть: http://localhost"

Перевірте у браузері на вашому комп'ютері: http://WINDOWS_IP/


Крок 5: Створення Custom AMI

Тепер, коли сервер налаштований (встановлений .NET, IIS, задеплоєний додаток) — створимо AMI. Це дозволить у майбутньому запускати новий сервер з тим самим станом без повторного встановлення.

  1. EC2 → Instances → оберіть instance → ActionsImage and templatesCreate image
  2. Image name: dotnet-iis-ready-v1
  3. Image description: Windows Server 2022 + IIS + .NET 10 Hosting Bundle
  4. No reboot: можна залишити unchecked (перезавантаження гарантує консистентність)
  5. Create image
  6. Прогрес: EC2 → AMIs → ваша AMI буде у стані pending → за 5–15 хвилин стане available

Крок 6: ОБОВ'ЯЗКОВО — Очищення ресурсів

Windows Server EC2 (t3.medium) коштує ~$0.065/год без зупинки — це ~$47/місяць. Зупиніть або видаліть instance після завершення роботи!
  1. EC2 → Instances → оберіть instance → Instance stateTerminate instance
    • Або Stop instance якщо плануєте продовжити пізніше (за зупинений instance EBS ще тарифікується, але менше)
  2. Якщо створювали Elastic IP — EC2 → Elastic IPsRelease Elastic IP address
  3. AMI: EC2 → AMIs → ваша AMI → ActionsDeregister AMI (опціонально)
  4. EBS Snapshots від AMI: EC2 → Snapshots → видаліть пов'язані

Практичний приклад: Dokploy — власна PaaS-платформа на EC2

Попередні приклади показали «класичний» підхід деплою: SSH-підключення, ручне копіювання файлів через SCP, налаштування systemd-сервісів. Цей підхід прозорий і надійний, але вимагає значної ручної роботи при кожному оновленні застосунку — ви повинні перебудувати проєкт, скопіювати файли на сервер, перезапустити сервіс. З ростом кількості застосунків це перетворюється на рутинну операцію, яка займає час і відволікає від розробки.

Dokploy — це безкоштовна open-source PaaS-платформа (Platform as a Service), яку ви встановлюєте на власний EC2 instance. Після встановлення вона надає зручний веб-інтерфейс для деплою застосунків — схожий за концепцією на Heroku або Railway, але повністю під вашим контролем, без абонентської плати за платформу і без залежності від стороннього сервісу.

Ключові можливості Dokploy:

  • Автоматичний деплой з GitHub — після кожного git push застосунок автоматично перебудовується і перезапускається без вашої участі
  • SSL-сертифікати через Let's Encrypt — Traefik автоматично отримує та оновлює сертифікати, жодних скриптів не потрібно
  • Веб-інтерфейс для логів — переглядайте логи контейнерів прямо у браузері в реальному часі
  • Docker та Docker Compose — підтримка будь-якого стека: .NET, Node.js, Python, Go, PHP
  • Нульовий downtime — новий контейнер запускається до зупинки старого (rolling deploy)
  • Управління змінними середовища — зберігайте секрети та конфігурацію окремо від коду
  • Rollback одним кліком — повернення до будь-якої попередньої версії з повною історією деплоїв

У цьому прикладі ми встановимо Dokploy на Ubuntu EC2, задеплоїмо ASP.NET Core API через GitHub repository та налаштуємо автоматичний CI/CD.

Loading diagram...
@startuml
skinparam style plain
skinparam backgroundColor #ffffff

actor "Розробник" as DEV

rectangle "EC2 Instance (t3.medium)\nUbuntu 26.04" as EC2 #dbeafe {
    rectangle "Dokploy\n:3000 (UI + API)" as DOKPLOY #bbf7d0 {
        rectangle "Docker Engine" as DOCKER #d1fae5
        rectangle "Traefik\n:80 / :443" as TRAEFIK #fef3c7
    }
    rectangle ".NET API Container\n:8080 (внутрішній)" as APP #e0e7ff
}

cloud "GitHub\nRepository" as GH #f3f4f6
rectangle "Let's Encrypt\n(SSL)" as LE #fde68a
actor "Користувач" as USER

DEV -right-> GH : git push
GH -down-> DOKPLOY : webhook → auto deploy
DOKPLOY -down-> DOCKER : docker build
DOCKER -right-> APP : docker run
TRAEFIK -down-> APP : proxy :443 → :8080
LE -up-> TRAEFIK : TLS-сертифікат
USER -right-> TRAEFIK : https://myapi.pp.ua

note right of DOKPLOY
  Встановлення — одна команда.
  Dokploy UI доступний на :3000.
  Traefik — вбудований reverse proxy
  з автоматичним Let's Encrypt.
end note
@enduml

Крок 1: Запуск EC2 instance для Dokploy

Dokploy запускає кілька Docker-контейнерів власної інфраструктури (застосунок, Traefik, PostgreSQL, Redis) ще до того, як ви задеплоїте перший сервіс. Тому мінімальні вимоги вищі, ніж для звичайного застосунку.

t3.micro (1 GB RAM) недостатньо для Dokploy. Під час встановлення або після нього система почне використовувати swap, контейнери зависатимуть. Мінімум — t3.small (2 GB RAM) для одного-двох застосунків. Для комфортної роботи з кількома сервісами використовуйте t3.medium (4 GB RAM).
  1. EC2 → Launch instances
  2. Name: dokploy-server
  3. Application and OS Images (AMI):
    • Натисніть Ubuntu у Quick Start
    • Оберіть Ubuntu Server 26.04 LTS (HVM), SSD Volume Type
    • Architecture: 64-bit (x86)
  4. Instance type: t3.medium (рекомендовано; мінімум t3.small для тестування)
  5. Key pair: оберіть існуючий ec2-lab-key або створіть новий у форматі .pem
  6. Network settings → Edit → Create security group:
    • Security group name: dokploy-sg
    • Правило 1 — SSH:
      • Type: SSH, Port: 22, Source: My IP (лише ви)
    • Правило 2 — HTTP:
      • Type: HTTP, Port: 80, Source: Anywhere (потрібно для Let's Encrypt верифікації та HTTP→HTTPS редиректу)
    • Правило 3 — HTTPS:
      • Type: HTTPS, Port: 443, Source: Anywhere (основний трафік застосунків)
    • Правило 4 — Dokploy UI:
      • Type: Custom TCP, Port: 3000, Source: My IP (веб-інтерфейс лише для адміністратора)
  7. Configure storage: 30 GB gp3 (Docker образи .NET займають ~2–3 GB кожен; 20 GB мінімум, 30 GB рекомендовано)
  8. Натисніть Launch instance

::

::


Крок 2: Виділення Elastic IP та налаштування DNS

Dokploy використовує Traefik для автоматичного отримання SSL-сертифікатів від Let's Encrypt. Let's Encrypt верифікує, що ви дійсно контролюєте домен, звернувшись до вашого сервера через порт 80. Для цього потрібна стала IP-адреса, яка не змінюється після рестартів, — саме таку надає Elastic IP.

  1. EC2 → Elastic IPsAllocate Elastic IP address
    • Network Border Group: eu-central-1
    • Натисніть Allocate
  2. Оберіть щойно виділений IP → ActionsAssociate Elastic IP address
    • Resource type: Instance
    • Instance: оберіть dokploy-server
    • Натисніть Associate
  3. Запишіть виділений IP (далі у прикладах — 3.64.185.42)
aws ec2 describe-addresses — перевірка Elastic IP
$ echo "Elastic IP: $EIP"
Elastic IP: 3.64.185.42

Налаштування DNS: зайдіть у панель вашого реєстратора доменів та додайте A-запис:

ПолеЗначенняПояснення
Ім'яdokployСубдомен для самого Dokploy UI
ТипAПрямий запис IP-адреси
Значення3.64.185.42Ваш Elastic IP
TTL300Час кешування (5 хв)

Якщо ваш домен — pp.ua, а ім'я — dokploy, то Dokploy UI буде доступний за https://dokploy.pp.ua. Для застосунків додайте окремий A-запис або wildcard-запис * → 3.64.185.42, щоб субдомени на кшталт myapi.pp.ua теж вказували на сервер.

Перевірте поширення DNS командою nslookup dokploy.pp.ua або сервісом whatsmydns.net. DNS-зміни набирають чинності від кількох хвилин до 48 годин, але зазвичай — менш ніж 5–15 хвилин.

Крок 3: Встановлення Dokploy

Підключіться до сервера через SSH та виконайте встановлення:

SSH підключення до dokploy-server
$ ssh -i ~/.ssh/ec2-lab-key.pem ubuntu@3.64.185.42
Warning: Permanently added '3.64.185.42' (ED25519) to the list of known hosts.
ubuntu@ip-172-31-10-25:~$

Dokploy встановлюється однією командою. Інсталятор автоматично завантажить Docker, Docker Compose та всі необхідні образи. Жодних попередніх налаштувань не потрібно:

curl -sSL https://dokploy.com/install.sh | sh — встановлення Dokploy
ubuntu@ip-172-31-10-25:~$ sudo curl -sSL https://dokploy.com/install.sh | sudo sh
Dokploy Installer v0.4.x
Detecting OS... Ubuntu 26.04 LTS
Installing Docker Engine...
✓ Docker installed (v26.x)
Installing Docker Compose Plugin...
✓ Docker Compose installed
Pulling Dokploy service images...
dokploy/dokploy:latest ... done
traefik:v3.x ... done
postgres:16 ... done
redis:7-alpine ... done
Starting Dokploy services...
✓ All services started successfully
🚀 Dokploy is running at: http://3.64.185.42:3000
Open this URL in your browser to complete the initial setup.
Інсталяція займає 3–7 хвилин залежно від швидкості з'єднання (потрібно завантажити ~1.5 GB Docker образів). Команда блокує термінал до завершення — не закривайте SSH-сесію.
Проблема: скрипт падає з помилкою docker: not found або E: Unable to locate package docker-ceЦе трапляється, коли ваша Ubuntu має нестандартну кодову назву (наприклад, resolute) — офіційний репозиторій Docker не містить пакетів для такої назви, тому встановлення Docker збоїть, і наступний крок ініціалізації Docker Swarm видає docker: not found.Діагностика: якщо у логах ви бачите рядок на кшталт:
https://download.docker.com/linux/ubuntu resolute stable
і потім помилки E: Unable to locate package — ви зіткнулись саме з цим.Крок 1: Встановіть Docker вручну
# 1. Видаліть зіпсований файл репозиторію, який створив скрипт
sudo rm -f /etc/apt/sources.list.d/docker.list

# 2. Додайте офіційний репозиторій Docker зі стабільною кодовою назвою jammy
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu jammy stable" | sudo tee /etc/apt/sources.list.d/docker.list

# 3. Оновіть списки пакетів
sudo apt-get update

# 4. Встановіть Docker
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Крок 2: Перевірте, що Docker працює
sudo docker version
Має вивести інформацію про клієнт та сервер без помилок not found.Крок 3: Ініціалізуйте Docker Swarm вручнуDokploy базується на Docker Swarm. Скрипт впав саме на цьому кроці, тому ініціалізуємо Swarm самостійно. Підставте IP-адресу вашого instance (вона видна у логах скрипта або у AWS Console):
# ЗАМІНІТЬ на вашу реальну приватну IP-адресу instance
sudo docker swarm init --advertise-addr 172.31.41.246
Крок 4: Запустіть скрипт Dokploy зновуТепер, коли Docker встановлений і Swarm активований, скрипт знайде все необхідне і просто розгорне контейнери без повторного встановлення Docker:
curl -sSL https://dokploy.com/install.sh | sudo sh

Перевірте, що всі чотири контейнери Dokploy запущені та здорові:

docker ps — перевірка контейнерів Dokploy
ubuntu@ip-172-31-10-25:~$ docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES STATUS PORTS
dokploy-app Up 3 minutes 0.0.0.0:3000->3000/tcp
dokploy-traefik Up 3 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
dokploy-postgres Up 3 minutes 5432/tcp
dokploy-redis Up 3 minutes 6379/tcp

Що робить кожен контейнер:

  • dokploy-app — основний застосунок: веб-інтерфейс, REST API, управління деплоями
  • dokploy-traefik — reverse proxy: приймає HTTP/HTTPS трафік, термінує TLS, маршрутизує до контейнерів застосунків
  • dokploy-postgres — реляційна база даних Dokploy (конфігурація, проєкти, деплої)
  • dokploy-redis — кеш та черга задач (асинхронні деплої, WebSocket для логів)

Крок 4: Перше налаштування у веб-інтерфейсі

Відкрийте у браузері: http://3.64.185.42:3000

Ви побачите сторінку реєстрації першого адміністратора. Ця форма відображається лише один раз — при першому відкритті після чистого встановлення. Якщо цю сторінку побачить сторонній — він зможе стати адміністратором. Тому важливо виконати цей крок одразу після встановлення.

Заповніть форму реєстрації:

  • Email: ваша email-адреса (буде логіном для входу)
  • Password: надійний пароль (мінімум 8 символів; рекомендується 16+)
  • Confirm Password: повторіть пароль

Натисніть Create Account. Ви автоматично увійдете в Dokploy і побачите порожній Dashboard.

Огляд головних розділів інтерфейсу:

РозділПризначення
DashboardЗагальна статистика: кількість проєктів, сервісів, стан сервера (CPU, RAM, диск)
ProjectsСписок проєктів — кожен проєкт містить один або кілька сервісів
Settings → ServerДомен Dokploy UI, Let's Encrypt email, SSH-ключі для GitHub
Settings → CertificatesПерегляд виданих SSL-сертифікатів та їх статусу
Settings → UsersУправління командою: додавання користувачів з різними правами
Settings → NotificationsНалаштування сповіщень (Slack, Discord, Email, Telegram)

Налаштування серверного домену та SSL:

  1. Перейдіть у SettingsServer
  2. У полі Domain введіть ваш домен для Dokploy UI: dokploy.pp.ua
  3. У полі Let's Encrypt Email введіть вашу email-адресу (на неї Let's Encrypt надсилає сповіщення про закінчення сертифікату)
  4. Натисніть Save

Після збереження Dokploy передає налаштування Traefik, який автоматично звертається до Let's Encrypt, проходить HTTP-01 верифікацію та отримує сертифікат. Зачекайте 1–2 хвилини — потім Dokploy UI буде доступний за адресою https://dokploy.pp.ua.

Якщо після збереження HTTPS не запрацював — перевірте два моменти: 1) DNS-запис для dokploy.pp.ua вже вказує на ваш Elastic IP (nslookup dokploy.pp.ua); 2) порт 80 відкритий у Security Group (Let's Encrypt потребує його для верифікації).

Крок 5: Підготовка .NET застосунку до деплою через Docker

Dokploy будує ваш застосунок з Dockerfile у корені репозиторію. Якщо Dockerfile відсутній — Dokploy може спробувати автовизначення стека через Nixpacks, але для .NET рекомендується явний Dockerfile.

Ключовий момент при написанні Dockerfile для Dokploy: застосунок повинен слухати на порту, який ви потім вкажете в налаштуваннях домену. Традиційно для контейнерів використовують порт 8080.

# Файл: Dockerfile (у корені репозиторію)

# Стадія 1: збірка
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Копіюємо файл проєкту окремо для кешування шару відновлення пакетів
COPY MyApi.csproj .
RUN dotnet restore

# Копіюємо решту коду та публікуємо
COPY . .
RUN dotnet publish -c Release -o /app/publish --no-restore

# Стадія 2: runtime образ (менший розмір — лише runtime, без SDK)
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .

# Налаштовуємо порт: 8080 — стандарт для контейнерів (не 5000/5001)
ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production
EXPOSE 8080

ENTRYPOINT ["dotnet", "MyApi.dll"]
Двостадійна збірка (build + runtime) критично важлива для зменшення розміру образу: образ SDK важить ~800 MB, образ лише runtime — ~200 MB. Продакшн-образ буде вчетверо меншим, що прискорює деплой.

Переконайтесь, що репозиторій містить .dockerignore для виключення зайвих файлів:

# .dockerignore
bin/
obj/
*.user
.git/
.vs/
**/*.md

Крок 6: Деплой .NET застосунку через Dokploy

Крок 6a: Створення проєкту

  1. Перейдіть у ProjectsCreate Project
  2. Name: my-dotnet-api
  3. Description: опціонально
  4. Натисніть Create

Крок 6b: Додавання сервісу

  1. Всередині проєкту натисніть + Create Service → оберіть Application
  2. Name: api
  3. Натисніть Create

Крок 6c: Налаштування джерела коду

  1. У налаштуваннях сервісу знайдіть секцію Source
  2. Оберіть GitHub
  3. Натисніть Connect with GitHub → авторизуйте Dokploy у вашому GitHub акаунті (Dokploy отримає право читати репозиторії)
  4. Оберіть Repository: username/my-dotnet-api
  5. Оберіть Branch: main
  6. Build Type: оберіть Dockerfile
  7. Натисніть Save

Крок 6d: Налаштування домену

  1. Перейдіть на вкладку Domains у налаштуваннях сервісу
  2. Натисніть Add Domain
  3. Заповніть форму:
    • Host: myapi.pp.ua
    • Port: 8080 (порт, на якому слухає ваш застосунок всередині контейнера)
    • HTTPS: увімкніть — Traefik автоматично отримає сертифікат
    • HTTP to HTTPS Redirect: увімкніть
  4. Натисніть Save

Крок 6e: Перший деплой

  1. Перейдіть на вкладку Deployments
  2. Натисніть Deploy
  3. З'явиться вікно з живими логами збірки
Логи збірки .NET застосунку в Dokploy (вкладка Deployments)
[Dokploy] Starting deployment #1 for service: api
[Dokploy] Cloning repository: github.com/username/my-dotnet-api (branch: main)
[Dokploy] Building Docker image...
Step 1/10 : FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
---> a1b2c3d4e5f6
Step 4/10 : RUN dotnet restore
Determining projects to restore...
Restored /src/MyApi.csproj (1.4s)
Step 6/10 : RUN dotnet publish -c Release -o /app/publish --no-restore
Build succeeded. 0 Warning(s). 0 Error(s).
MyApi -> /app/publish/MyApi.dll
Step 10/10 : ENTRYPOINT ["dotnet", "MyApi.dll"]
Successfully built f7a8b9c0d1e2
[Dokploy] Starting container...
[Dokploy] ✓ Deployment #1 successful! Service is running.
[Dokploy] Available at: https://myapi.pp.ua

Після успішного деплою перевірте застосунок:

curl — перевірка доступності задеплоєного API
$ curl https://myapi.pp.ua/
{"message":"Hello from Dokploy!","environment":"Production","version":"1.0.0"}
$ curl -I https://myapi.pp.ua/
HTTP/2 200
content-type: application/json; charset=utf-8
strict-transport-security: max-age=31536000

Крок 7: Автоматичний CI/CD через GitHub Webhook

Webhook — це HTTP-запит, який GitHub надсилає на вашу адресу при кожному git push. Dokploy приймає цей запит та автоматично запускає новий деплой. Після налаштування процес розгортання повністю автоматичний: ви пишете код, виконуєте git push — сервер оновлюється сам.

Отримання Webhook URL у Dokploy:

  1. Перейдіть у налаштування вашого сервісу (вкладка General)
  2. Знайдіть секцію Webhook — там відображається унікальний URL вигляду https://dokploy.pp.ua/api/deploy/webhook/abc123xyz456
  3. Скопіюйте цей URL

Додавання Webhook у GitHub:

  1. Відкрийте GitHub repository → SettingsWebhooksAdd webhook
  2. Payload URL: вставте скопійований Webhook URL
  3. Content type: application/json
  4. Which events would you like to trigger this webhook?Just the push event
  5. Переконайтесь, що Active увімкнено
  6. Натисніть Add webhook

GitHub відразу надішле тестовий ping запит. У Dokploy у вкладці Deployments ви побачите, що запит отримано (але деплой не запустився — ping це не push).

git push — автоматичний тригер деплою через webhook
$ git add . && git commit -m "feat: додати health check endpoint"
[main 3f4a5b6] feat: додати health check endpoint
1 file changed, 8 insertions(+)
$ git push origin main
Enumerating objects: 5, done.
Writing objects: 100% (3/3), done.
To github.com:username/my-dotnet-api.git
abc1234..3f4a5b6 main -> main
# Через ~15 секунд у Dokploy Deployments з'явиться новий деплой:
[Dokploy] Webhook received → Deployment #2 started automatically
У вкладці Deployments зберігається повна історія деплоїв з логами кожного. Якщо новий деплой зламав щось — натисніть Rollback поряд з будь-яким попереднім успішним деплоєм. Dokploy зупинить поточний контейнер і запустить образ попередньої версії. Rollback займає 10–30 секунд.

Крок 8: Управління змінними середовища

Environment Variables — стандартний спосіб зберігання конфігурацій, чутливих до оточення: рядки підключення до баз даних, API-ключі, секретні ключі JWT тощо. Зберігати їх у коді або у файлах конфігурації у Git-репозиторії — небезпечно.

  1. У налаштуваннях сервісу перейдіть на вкладку Environment
  2. Натисніть Add Variable для кожної змінної:
ASPNETCORE_ENVIRONMENT=Production
ConnectionStrings__DefaultConnection=Host=...;Database=...;Username=...;Password=...
JwtSettings__Secret=your-very-long-secret-key-here
AllowedOrigins=https://myapp.pp.ua,https://www.myapp.pp.ua
SENTRY_DSN=https://...@sentry.io/123456
  1. Натисніть Save — Dokploy автоматично перезапустить контейнер з новими змінними
Ніколи не зберігайте паролі, API-ключі та connection strings у appsettings.json або в Git-репозиторії. Якщо такі дані потрапили у публічний репозиторій — негайно скасуйте всі ключі та змініть паролі. Для production використовуйте Environment Variables у Dokploy або AWS Secrets Manager.

Перегляд логів у реальному часі через вкладку Logs вашого сервісу. Це еквівалент docker logs -f — ви бачите все, що застосунок виводить у stdout/stderr. Логи оновлюються у реальному часі через WebSocket.


Крок 9: ОБОВ'ЯЗКОВО — Очищення ресурсів

t3.medium коштує ~$0.052/год без зупинки — це ~$37/місяць. Зупиніть або видаліть instance після завершення роботи з лабораторною роботою!
  1. EC2 → Instancesdokploy-serverInstance stateTerminate instance
  2. EC2 → Elastic IPs → оберіть виділений IP → ActionsRelease Elastic IP address
    • Увага: спочатку instance має бути видалений (або від'єднайте IP перед видаленням через Disassociate Elastic IP address)
aws ec2 terminate-instances — видалення dokploy-server
$ aws ec2 terminate-instances --instance-ids $INSTANCE_ID --region eu-central-1
{
"TerminatingInstances": [{
"InstanceId": "i-1234567890abcdef0",
"CurrentState": { "Name": "shutting-down" },
"PreviousState": { "Name": "running" }
}]
}

Резюме

EC2 Instances

  • EC2 — віртуальні сервери у хмарі. Платите лише за час роботи.
  • Instance Types: T (burstable, Free Tier t3.micro), M (стабільний), C (CPU), R (RAM), I (Storage).
  • Тип завжди можна змінити: зупинити instance → змінити тип → запустити.

Storage

  • AMI — шаблон ОС. Перевіряйте актуальний ID для регіону. Custom AMI для Auto Scaling.
  • EBS — мережеві диски. gp3 для більшості задач. Snapshots для резервних копій.

Networking & Security

  • Security Groups: stateful файрвол. SSH лише з вашого IP. Ніколи 0.0.0.0/0 для порту 22!
  • Elastic IP: стала публічна адреса. Безкоштовна при прикріпленому запущеному instance.

Pricing

  • On-Demand: гнучко, без контракту.
  • Savings Plans: знижка 30–75% на 1-3 роки.
  • Spot: 60–90% знижка, переривувані. Для batch-задач та CI/CD.

Automation

  • User Data: bash/PowerShell скрипт при першому запуску — автоматизація встановлення ПЗ.
  • IMDS (169.254.169.254): отримання метаданих та IAM credentials без Access Keys.

Deployment

  • Linux: SSH + SCP → systemd service (Restart=always) → nginx reverse proxy.
  • Windows: RDP підключення → IIS + .NET 10 Hosting Bundle → New-Website у PowerShell.

Практичні завдання

Рівень 1 (Базовий)

Завдання 1. Поясніть різницю між t3.micro та m6i.large. Коли ви б обрали кожен? Що таке CPU Credits у T-instances?

Завдання 2. Чому небезпечно відкривати SSH порт (22) для 0.0.0.0/0? Як правильно налаштувати Security Group для SSH?

Рівень 2 (Практичний)

Завдання 3. Задеплойте ASP.NET Core API на Linux EC2. Налаштуйте systemd service з Restart=always. Перевірте, що після sudo reboot API автоматично запускається.

Завдання 4. Напишіть User Data скрипт для Ubuntu EC2, який автоматично встановлює .NET 10 та копіює з S3 і запускає ваш API. Протестуйте: запустіть instance і без SSH підключення перевірте, що API відповідає.

Рівень 3 (Архітектура)

Завдання 5. Спроектуйте EC2-архітектуру для .NET API з наступними вимогами: Auto Scaling Group (мін. 2, макс. 10 instances), ALB для балансування, окремий EBS том для логів, custom AMI з встановленим .NET, Health Check через /health endpoint, Spot Instances для економії. Намалюйте схему у PlantUML.

Copyright © 2026