Чи можливий стартап без DevOps

Усі статті, обговорення, новини про DevOps — в одному місці. Підписуйтеся на DOU | DevOps!

Усім привіт, мене звати Богдан. Я займаюсь розробкою вже понад 9 років, але залишаюсь постійним шукачем нових підходів до розв’язання складних проблем: простих, що потребують залучення меншої кількості людей, з високими вимогами до якості коду, розширення та безпеки. У цій статті я хочу поділитися досвідом, який саме відповідає всім цим вимогам. Звичайно, що у всього є свої нюанси, і про них ми також згадаємо.

Що робити, якщо ти розробив застосунок і плануєш, що в майбутньому він буде розширятись, а відтак — має відповідати вимогам приватності, навантаження та легкості конфігурації. Для цього буде незайвим певний багаж знань в плані DevOps, якого ти можеш і не мати.

Або як запустити такий застосунок на рівні enterprise проєкту, коли в штаті так само немає окремого DevOps’a? У цій статі поговоримо про те, як за допомогою правильних інструментів можна зробити все максимально якісно та без болю. Ба більше, спробуємо не просто описати ці інструменти та їхні можливості, а дати глобальне розуміння того, як просто випустити застосунок у світ за допомогою сучасних підходів, які добре себе зарекомендували в enterpise проєктах.

Ціль

Розробити застосунок, який буде відповідати наступним вимогам:

  • Автоматичний деплой (deployment) після кожного коміту (commit) на головну гілку (main/master).
  • Підтримка змінних середовища для front-end & back-end.
  • Безпечне з’єднання бази даних із сервером.
  • Розширення застосунку до декількох інстансів.
  • Масштабування бази даних до декількох інстансів.
  • Моніторинг стану ресурсів сервера та бази даних.
  • Сповіщення на пошту або Slack про те, що сервер навантажений або щось пішло не так.
  • Доступ до логів.
  • Можливість увійти до консолі сервера (ssh або аналог).
  • Налаштування CORS та імені домену.
  • Можливість повернення до попередньої версії застосунку (Rollback).
  • Мінімальна кількість простою сервера під час деплою (Zero Downtime Deployment).

Підготовка коду

Перед тим, як все запустити, на серверах потрібно підготувати front-end i back-end. Вони повинні бути сумісними із сервісом DigitalOcean, який ми будемо використовувати надалі. Для цього є лише декілька вимог, головною з яких є реалізація скриптів будування та запуску. Потрібні скрипти розміщаються і зчитуються package.json, а валідація бібліотек відбувається на основі файлів package.json & package-lock.json (yarn.lock).

Другою вимогою є те, щоб проєкти розміщуватись на github або gitlab — це значно спростить інтеграцію з DigitalOcean.

Підготовка front-end

Потрібні файли можна згенерувати та описати в них скрипти самостійно на основі пакетних менеджерів, таких як npmyarn, але ми розглянемо простіший шлях і скористаємось генератором проєкту. Пропоную скористатись npx. Цей пакет доступний глобально в останніх версіях npm.

npx create-react-app awesome-startup-web

Щоб перевірити, чи сервер працює, достатньо виконати наступні команди у директорії створеного проєкту:

npm i
npm start

Тепер, заходячи на адресу localhost:3000, можна побачити робочий front-end сервер.

Змінні середовища

Щоб вважати front-end частину готовою до використання на сервері, нам потрібно подбати про інтеграцію зі змінними середовища. В майбутньому це дозволить інтегруватись із зовнішніми сервісами по типу Sentry, Google Analytics або ж реалізувати різного типу логіку на основі змінних середовища.

Коли ми використовуємо npx як генератор проєкту, змінні середовища зчитуються засобами вбудованих плагінів, що входять до складу webpack. Якщо ж у тебе проєкт іншого типу, то варто почитати про це самостійно. Зі свого боку рекомендую використовувати бібліотеку dotenv-webpack.

Для тестування роботи змінних середовища я додав код, який наведений нижче, до App.js

<p>env: {process.env.REACT_APP_ENV}</p>

Підготовка back-end

В цьому випадку ми скористаємось сучасним фреймворком Nest, оскільки він вже працює зі змінними середовища на основі бібліотеки DotEnv і в такому випадку нам не доведеться цього конфігурувати додатково.

Перед тим як розпочати генерацію проєкту потрібно встановити глобально @nestjs/cli.

Всі інструкції можна знайти на офіційній сторінці, а ми лише використаємо ті, які нам потрібні.

npm i -g @nestjs/cli
nest new awesome-startup-api

Щоб переконатись, що все працює, потрібно виконати наступні команди у директорії створеного проєкту:

npm i
npm start

Тепер сервер запущений і ми можемо надіслати на нього запит та отримати очікувану відповідь

curl http://localhost:3000

Змінні середовища

Для back-end особливо важливим аспектом є змінні середовища, оскільки на основі них ми підключаємось до бази даних, зовнішніх сервісів та тримаємо різного типу токени. Фреймворк, який ми використали, також збудований на основі webpack та гарантує нам взаємодію зі змінними середовища. Відтак на даному етапі нічого додатково робити не потрібно.

База даних

Жодна сучасна система не обходиться без бази даних, тому візьмемося до її локального підняття. Я використаю ORM, яка розміщена в рекомендаціях на сторінці фреймворку. При цьому реалізую мінімальну конфігурацію, щоб перевірити, що зв’язок є.

Також запущу її в Docker Container. Це дозволить не встановлювати базу даних на локальний комп’ютер. Для цього нам потрібно встановити Docker на свій комп’ютер.

Приступімо до наступної команди, яка створить і запустить нам контейнер з базою даних

docker run --name postgres-awesome -p 5432:5432 -e POSTGRES_PASSWORD=superuser -d postgres

Тепер перейдемо до застосунку і згідно з інструкцією фреймворку Nest під’єднаємо базу даних.

npm install --save @nestjs/typeorm typeorm pg

Нижче ми додаємо код для ініціалізації TypeORM. При підключенні бази даних в прикладі ми видаляємо параметр sslmode. Так робиться, тому що згідно з документацією бібліотека TypeORM затирає об’єкт ssl повністю з сертифікатом. Це, своєю чергою, не дає створити безпечне з’єднання у випадку, якщо передається будь-який з параметрів ssl, в нашому випадку, — sslmode. Детальніше роз’яснення можна знайти ось тут:

// app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';

const url = new URL(process.env.DATABASE_URL);
url.searchParams.delete('sslmode');

TypeOrmModule.forRoot({
  type: 'postgres',
  url: url.toString(),
    ssl: process.env.CA_CERT && {
      ca: process.env.CA_CERT,
  },
}),

Тепер можна запустити застосунок і переконатись, що все під’єднано.

 DATABASE_URL=postgres://postgres:superuser@localhost/postgres npm start
LOG [InstanceLoader] TypeOrmModule dependencies initialized +25ms
LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +58ms

Ось таке повідомлення ми побачимо, якщо підключення успішне.

Запускаємо підготовлений код

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

Реєструємось на сервісі та переходимо до наступного кроку. Також на даному етапі прошу переконатися, що проєкт знаходиться на github або gitlab репозиторії.

Запускаємо Front-end

Переходимо у розділ Apps та натискаємо кнопку для створення нового застосунку. На першому кроці вибираємо наш репозиторій і основну гілку, з якої при будь-яких змінах автоматично буде відбуватись CI процес.

На другому кроці нам потрібно вибрати план для ресурсів сервера, на даному етапі можна спокійно обійтись найдешевшим, тому вибираємо план за $5 (512 MB RAM | 1 vCPU).

Далі ми можемо додати змінні середовища. Зробимо це, як в прикладі вище.

REACT_APP_ENV=dev

Тепер обираємо потрібний регіон, натискаємо «Створити» і чекаємо завершення процесу побудови і запуску.

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

Запускаємо Back-end

Нам потрібно, як і у прикладі з front-end, перейти до Apps. Створюємо застосунок аналогічно з попереднім. Тепер, не чекаючи завершення, нам потрібно змінити декілька налаштувань, тому переходимо в Settings → вибираємо компонент нашого сервера.

  • Для HTTP Port потрібно встановити порт сервера, в нашому випадку стандартний це 3000.
  • Для Run Command потрібно змінити команду на npm run start:prod.

На цьому етапі наш застосунок мав би запуститись, але він вимагає підключення до бази даних, тому нам потрібно її створити та додати до нашого сервера. Переходимо на сторінку нашого застосунку та натискаємо справа зверху Create → Create/Attach Database.

Тепер вводимо назву (в прикладах надалі ми будем використовувати назву awesome-startup-db) і натискаємо «Створити». Після того як база даних буде створена, DigitalOcean автоматично додасть до середовища змінну DATABASE_URL.

Однією з особливостей використання додатків на DigitalOcean є те, що ми можемо використовувати змінні компонентів, наприклад, як у звичайного класу. Ось які змінні можна використати для бази даних:

Більше змінних та інформацію про їх використання можна знайти на сторінці документації.

Отже, нам потрібно додати сертифікат, щоб реалізувати безпечне підключення до бази даних, тому додамо змінну CA_CERT зі значенням ${awesome-startup-db.CA_CERT}. А остаточна версія змінних буде виглядати наступним чином:

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

Які можливості для нас відкриваються

Після того, як ми запустили front-end i back-end разом з базою даних, ми можемо розглянути, які можливості у нас відкрились, та поглянути, що знаходиться «під капотом». На даному етапі я вже не буду розділяти на front-end і back-end, а буду називати кожну частину застосунком через те, що на рівні DigitalOcean це все застосунки.

З самого початку треба зрозуміти, що для кожного нашого застосунку DigitalOcean створив Docker Container та запакував їх всередину. І оскільки застосунки не взаємодіють із зовнішньою конфігурацією машини та мають відкриті лише потрібні порти, ми можемо бути впевнені на рахунок безпеки.

Конфігурацію цього процесу можна знайти на сторінці налаштувань в розділі App Spec, а більше почитати ось тут.

App Health Check

Даний функціонал дозволяє перевірити стан застосунку. Стандартна перевірка базується на відкритому порті нашого застосунку: якщо він відповідає, то сервер вважається робочим.

Перейшовши на вкладку Insights, ми можемо побачити детальну інформацію на графіку про CPU, RAM, Network, Restart Count. У випадку підключення додаткових компонентів до нашого застосунку, наприклад, бази даних, моніторинг відбувається для них автоматично.

Додатково перевірку стану можна сконфігурувати та переналаштувати, наприклад, на HTTP в налаштуваннях компонента.

Logs

Переходячи до вкладки Runtime Logs, ми маємо доступ в реальному часі до логів кожного з компонентів. Приклад ви можете побачити на фото нижче:

Є можливість інтеграції зі зовнішніми сервісами для збирання логів, такими як PaperTrail, Logtail, Datadog. Така інтеграція надасть доступ до значної кількості фільтрів (наприклад, за вибраним часовим проміжком, а не лише в реальному часі), а також до іншої функціональності, що значно спростить пошук потрібної інформації.

Console

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

Наприклад, якщо в нас є проблема в логіці на основі змінних середовища, то ми можемо просто зайти та переглянути її значення, користуючись командою echo. Перегляньмо значення адреси для підключення до бази даних echo $DATABASE_URL. Результати можна побачити на зображені:

Alerts

Однією з важливих функцій є система сповіщень. Наприклад, можна налаштувати alert для статусу деплою після останніх змін: відбувся успішно або ні. А потім надіслати ці сповіщення на пошту або в канал Slack.

Також можна сконфігурувати сповіщення на основі навантажень. Наприклад, якщо навантаження на процесор буде понад 80% протягом 5 хв, то ми отримаємо сповіщення про це. Ось як це може виглядати:

Environment Variables

Змінні можна додавати на рівні застосунку в цілому або ж на рівні кожного компонента, такого як front-end або back-end. До особливостей можна віднести те, що DigitalOcean дозволяє використовувати змінні на базі назв компонентів.

Наприклад, ми можемо використати посилання до бази даних, отримане через назву, викликавши компонент бази даних. Ось як це виглядає ${awesome-startup-db.DATABASE_URL}. Більше таких змінних можна найти тут.

Source

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

Таким чином ми можемо розділити застосунки для кожного середовища: наприклад, одна гілка буде для development, інша для production.

Scaling

Цей розділ допоможе нам знайти оптимальне рішення для продуктивності нашого застосунку. Ми можемо налаштувати сповіщення про те, що процесор навантажений. А отримавши його, можемо зайти в цей розділ і збільшити ресурси машини.

Основною вимогою до декількох контейнерів є те, що код повинен бути підготовлений до паралельної роботи декількох серверів одночасно. Ось як це виглядає:

Activity

На цій вкладці ми можемо побачити історію подій, які відбулись: наприклад, зміну налаштувань або новий коміт, який спровокував процес деплою. Сильна деталізація дає зрозуміти, що було змінено і ким саме, чи вдався deployment тощо.

При deployment існує система zero-downtime. Це дає нам візуально швидкий реліз з точки зору користувача.

Також присутній механізм Rollback, який дозволяє зробити повернення до останньої робочої версії, більше деталей можна прочитати ось тут. На зображені нижче можна побачити приклад того, як можна зробити rollback до потрібної версії:

Database

До кожного застосунку можна додати базу даних. Для цього з головної сторінки потрібно натиснути Create → Create/Attach Database. Потім відповідно натиснути «Створити». Після створення змінна середовища для підключення буде додана як ми описували раніше.

Варто виділити те, що база даних буде знаходитись в одному й тому ж дата-центрі. Це значно вплине на швидкість та безпечність такої роботи. Адже трафік між сервером і базою даних не буде виходити за межі одного дата-центру.

Додану базу даних можна буде обновити до Managed Database. Це важливо для продукційного середовища, бо ми отримаємо доступ до більшої кількості налаштувань: point-in-time recovery, дамп-бази, розширеного меню користувачів, таблиць баз даних та керування доступом тощо.

Також дуже важливою функціональністю є розширення бази даних: можна створити декілька nodes, а також резервну, яка знадобиться, якщо основна буде недоступна (automated failover). Це досить складні механізми, які ми отримуємо в дуже простому інтерфейсі.

Other Settings

Залишились налаштування, які не потребують великого опису, але можуть бути також корисними.

  • Commands. Можна використати команди з іншою назвою для будування та запуску застосунку.
  • HTTP Port. Конфігурація основного порту для Docker Container, щоб через нього спілкуватись з зовнішніми машинами. Потрібно встановити той, на якому працює сервер, наприклад, для нашого Nest проєкту стандартним є порт 3000.
  • CORS Policy. Тут можна встановити різноманітні обмеження на запити, які будуть потрапляти до нашого сервера:

  • Domains. Тут ми можемо сконфігурувати Domain Name для нашого застосунку. Але на DigitalOcean не можна його купити, тут є лише налаштування для того, щоб прив’язати його до нашого сервера.

DigitalOcean рекомендує для купівлі використовувати name.com. А орієнтовна ціна для непопулярного домену складає до $10. Наприклад, для exdevelopment.org ціна на момент написання статі складає $8.99.

Об’єднання додатків

Також є можливість об’єднання додатків в один. Це робиться так само як і додавання бази даних через кнопку Create → Create resources from source code. У випадку об’єднання нам потрібно буде правильно сконфігурувати роути. Для цього переходимо до налаштувань компонента нашого back-end → HTTP Request Routes та змінюємо на /api.

Переходимо в те ж саме місце налаштувань для нашого компонента front-end та змінюємо адресу в HTTP Request Routes на /. Після всіх цих налаштувань на головній сторінці застосунку Overview можна побачити блок зі сконфігурованими адресами.

Тепер ми можемо перейти на них і переконатись, що все працює і знаходиться в рамках одного застосунку:

Підсумки

Отже, підбиймо підсумки, наскільки ми змогли реалізувати нашу ціль та яка ціна питання.

Реалізація цілей

За допомогою DigitalOcean реалізувати застосунок, який відповідає закладеним цілям та буде випущений на продукційне середовище, не так же й важко і не вимагає від нас вивчення DevOps-інструментів, таких як Jenkins, Kubernates, Docker Swarm тощо. Сервіс дозволяє:

  • додавати базу даних та бути впевненим в безпечному підключені, через те що все можна реалізувати в одному дата-центрі;
  • додавати змінні середовища та зашифрувати їх,
  • переглядати логи та мати доступ до консолі;
  • розширювати інстанції застосунку та бази даних, тим самим підготуватись до збільшення навантажень тощо.

Ці всі пункти дозволяють ствердити, що без DevOps-інженера ми можемо підготувати самостійно повноцінний застосунок та розділити його між середовищами.

Ціна

Виконання поставлених цілей обійшлось в суму $17 в місяць:

  • кожен з двох компонентів застосунку коштував по $5;
  • інтегрована розробницька база даних — $7. При розширенні бази даних до версії Managed Database ціна зросте де $15, а в сумі — до $25.

DigitalOcean не має свого сервісу для купівлі доменного ім’я, а рекомендує використовувати name.com. Орієнтовна ціна непопулярного доменного імені на момент написання статі складає до $10.

Що далі

Звичайно, що цю тему можна розвивати та поглиблювати. Тож наостанок пропоную перелік топіків, які допоможуть створити комплексніші enterprise застосунки. Пишіть у коментарях, що з цього було б найцікавіше.

  1. Як релізити лише якісний код, який на етапі створення Pull Request пройшов всі перевірки якості (quality gates), такі як Unit Tests, Linter та інше.
  2. Як оптимізувати одночасну роботу декількох серверів на різних машинах.
  3. Як уникнути прив’язки github або gitlab і реалізувати все як в даній статі, але на основі docker containers.
  4. Як працювати з помилками та дізнаватись про них на продакшені.
👍ПодобаєтьсяСподобалось7
До обраногоВ обраному5
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Та можливо взагалі то. Але є але. Це стосується масштабу проекту та його навантаження, інтеграцій та технологій які використовуються, також архітектури. Тобто на якомусь етапі потрібно буде то все переробляты. Покрити всі потреби out of box, буде як дорого так і можливо складно або неможливо в масштабуванні.

Звичайно можна — якщо ти будеш девопсом. Головне щоб був таким само хорошим, як і програміст — бо інакше ефективність така собі. Х*йовий девопс по ціні хорошого програміста.

Кілька років тому були теми «стартап без програмістів», навіть хтось щось комусь продав )
В бізнесі, а особливо в стартапі, головне комусь дорого продати цей бізнес!
Тому без девопса можна, головне вміти пітчити, знайти мільйонерів, яким зможете продати ;)

дуже добре що є такі рекомендації, бо виправити потім буде дорожче, консультанти по інфраструктурі завжди будуть мати свій шмат хлібчику.

вітаю, ви, здається, пилите версію simloud.com для бідних

На мою думку, відповідь на це запитання така: сучасний проект без DevOps вже не можливий, але без DevOps інженера — запросто.

В мене друг підприємець-інді розробник, заробляє на своїх проектах, працює в парадигмі Ops: копіює код на хостинг руцями та запускає, все працює норм

Передаємо привіт Cloud Functions v2 з їх обмеженням по репо-хостингам.

Дуже рекомендую для стартапів які не мають бюджету знайти можливість отримати годинну консультацію з тямущим девопсом який підкаже що і як робити, щоб коли проєкт виросте то ’підходпити’ його не було проблемою

Чомусь склався такий досвід що Кожного разу коли приходив на проєкт де на початку не було девопса (або тямущого ліда/архітекта який по суті був девопсом), то у результаті, щоб проєкт став ’нормальним’ - менше їв грошей, краще перформив і тд. потрібно було наймати велику команду девопсів і у результаті ця ’початкова’ економія вилазила боком.
І навпаки, проєкт де з початкових стадій були б хоча б консультації з девопсом і коли проєкт бачив що вони будуть рости залучали девопса фултайм то навіть коли проєкт ставав величезним і з великими навантаженнями та команда девів була 15-20+ то всеодно вистачало одного девопса який ще і секюріті міг займатися.

Слухай, класна стаття насправді, але якщо питання чи можна стартап без девопс, то увесь продемонстрований висновок на жаль це — якщо стартап на коліні робить простеньку аппку і деплоїть її в копійчаний vps, і розробник зробить все сам, то таки можна :)

На Heroku подібні апки можна було кліпать без девопса ще років десять тому

Пишіть про 1-4. QA, PM теж нема?
Коли тоді і на яких проєктах потрібні devops?

Девопс потрібен на етапі масштабування і продакшену.
А так на етапах ресерчу/альфа — будь-який розробник якось Docker запустить. І робити git pull/docker-compose build/docker-compose restart теж зможе

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

Підписатись на коментарі