Як ми Stage-оточення готували: вибір технологій, розгортання гілки і пайплайну
Усі статті, обговорення, новини про DevOps — в одному місці. Підписуйтеся на DOU | DevOps!
Усім привіт! Мене звати Андрій Товстоног, я CTO в компанії Legit з екосистеми бізнесів Genesis. Legit — найбільша диджитал-медіакомпанія в Субсахарській Африці, чиїми продуктами користуються понад 40 млн людей щомісяця.
Цей текст є логічним продовженням попередньої статті про локальну розробку, у якій ми розглянули варіант побудови локального оточення за допомогою деяких цікавих інструментів та наблизили розробників трохи більше до Production оточення.
На цей раз поділюся нашим досвідом використання K8s кластера для стейджингу, що це нам дало, і чому жити стало краще :) Як завжди, не претендую на звання «капітану очевидності», а просто ділюся нашим досвідом використання різних технологій, розгортання гілки, вибору конкретних інструментів тощо.
Одразу попереджую, що матеріал детальний, а тому немалий за обсягом. Якщо цікаво (а цікаво буде), то велкам.
Передісторія
Нагадаю, що ми почали розробку нового продукту, а разом з тим розбудову нового стеку для локальної розробки, Stage-оточення та згодом — Production оточення.
Наш стек складається з таких компонентів:
- Back-end — PHP 8 із фреймворком Laravel;
- RoadRunner — сервер застосунків для PHP, написаний на Go;
- Front-end — Next.Js;
- CMS — React;
- Databases — MySQL, Redis, Memcached, Elasticsearch;
- WebServer — Nginx;
- локальне оточення — Devspace.
У класичному розумінні Stage-оточення ми стикнулися з певними проблемами, які цілком могли бути вирішені шляхом використання оточень, побудованих на K8s:
- постійне перетягування ковдри, чий Stage. Буває, що тестувати необхідно декілька гілок, а Stage — один;
- незалежна розробка. Девелопер може незалежно від інших виконати деплоймент гілки, щоби подивитись, як виглядатимуть його зміни, перш ніж віддати на тестування;
- блокування Stage на невизначений період часу для демонстрації змін замовнику або з інших причин;
- гранулярне керування ресурсами;
- незалежне тестування;
- перевірка стану запущених сервісів та автоматичний перезапуск під час збоїв;
- підтримка заданої кількості копій застосунку;
- автоматичне нарощування потужностей (горизонтальне масштабування).
Stage-оточення «за класикою»
Нижче схема, як усе було влаштовано до поточної схеми реалізації.
Що ми використовували:
1. AWS EC2 instances
- Jenkins — для легких задач автоматизації. Наприклад, звернення на різноманітні API за розкладом або розгортання helm charts тощо;
- Build Node — для складніших задач. Наприклад, збірка docker images паралельно для шести однотипних проєктів (важкувато йому тоді було, аж пихтів — «паровозик, який зміг» :));
- Stage;
2. AWS ALB — щоби забути про оновлення сертифікатів і зручно закрити Stage-оточення від «злого» зовнішнього світу, а також для термінації HTTPS-з’єднання;
3. AWS ACM — для генерації сертифіката для ALB домену Stage-сервера;
4. AWS Route53 — запис не формувався динамічно (бо тут можна подумати, що запис додавався на льоту), а було додано wildcard — *.example.com.
5. AWS EFS — для зберігання скриптів, які використовуються Jenkins і Build Node, а також неважких артефактів, наприклад, шеринг специфічної інформації про збірки.
Stage-оточення складалося з одного ЕС2 інстансу, у якому крутились усі наші проєкти, як основні, так і допоміжні. Основні проєкти були не в Docker, і це тягнуло за собою купу незручностей та болю, повʼязаного з оновленням певного компонента стека.
Варто зазначити: якщо щось відбувалось із сервером, то тестування, а інколи й розробка дещо гальмувалися на час відновлення. Це доволі неприємно.
Тут можна зауважити: а чому не робили резервні копії або регулярні снепшоти диску? Ця ідея постійно була на поверхні, але відкладалася, бо завжди знаходилися важливіші задачі.
Як виглядав наш воркфлоу з розгортання Stage-оточення:
- розробник створював гілку, пушив до неї свої зміни;
- далі створював Pull Request. На GitHub в нас були створені вебхуки, які відправлялись у Jenkins;
- Jenkins отримував хук та виконував збірку на білд-сервері (Jenkins job, що збирала docker images для розробки);
- в окремій Jenkins job розробник обирав гілку, яку необхідно збілдити, та виконував білд (на Build Node). Після збірки записувалась інформація в EFS зі специфічними параметрами, наприклад, хеш коміту й назва гілки;
- після успішної збірки розробник обирав, яку збірку йому необхідно задеплоїти. Ця інформація підтягувалася з EFS;
- виконував синхронізацію файлів з білд-сервера на Stage-сервер за допомогою утиліти rsync;
- тригерилися відповідні джоби автотестів, які запускалися на Stage URL.
Водночас у поточний момент часу активною могла бути лише одна гілка для тестування.
До чого ми прийшли
Поточне рішення виглядає так (схема 2). З особливостей — використання окремого кластеру K8s для Stage-оточення.
Отже, що ми використали в цьому випадку:
- AWS EKS — окремий кластер K8s, що керується самим AWS;
- AWS ECR — image registry для зберігання зібраних образів;
- AWS ALB — наш інгрес для входу в кластер K8s;
- AWS ACM — генерація сертифікатів;
- AWS Route53 — адміністрування DNS записів;
- AWS EC2 instances:
- Jenkins — усе той самий;
- Build Node — теж без змін.
Звісно, можна дискутувати, чому саме EKS :) Якщо є можливість зняти з себе головний біль відповідальності за K8s Control Plane (оновлення та коректне функціонування), то чому б цим не скористатись? Краще зосередитися на процесі деплою застосунків та їхніх компонентів.
Як змінився флоу? З точку зору розробника — ніяк. Усе, що змінилося — можливість катити гілки паралельно, не заважаючи один одному.
Пропоную заглибитись у деталі, як кажуть, under the hood. Поїхали.
Від більшого до меншого
Розпочнемо з головного компоненту — AWS EKS.
Сильно зупинятися не буду, адже вже написано багато про встановлення та конфігурацію. Для налаштування кластера ми використовували гібридний формат у вигляді Terraform + eksctl (ну добре-добре, вмовили, невеликий опис усе ж додам :)).
Terraform готував основу:
- AWS Virtual private cloud (VPC):
- для private subnets (зрештою використовуємо лише private);
- для public subnets;
- AWS Internet Gateway — для public subnets;
- AWS NAT-Gateway — для private subnets;
- AWS IAM Policy:
- для autoscaling;
- для Application load balancer (ALB);
- SSL сертифікати — через AWS Certificate Manager (ACM);
- AWS Security Groups — для worker group;
- конфігураційні файли для eksctl з output Terraform з префіксом dynamic:
- dynamic-cluster.yaml — містить загальну інформацію про кластер, потрібен для створення самого кластеру утилітою eksctl;
- dynamic-cluster-autoscaling.yaml — контроль за станом worker group, відповідає за Upscaling або Downscaling;
- dynamic-node-groups.yaml — містить лише опис worker group для nginx-ingress. На основі цього файлу потім ручками формується static-node-groups.yaml, у якому описуються інші необхідні worker group;
- dynamic-ingress-manifest.yaml (ingress-alb) — конфігурація ALB. У ньому задаються правила редиректів, дозволені мережі, сертифікати SSL та маршрути для доступу до бекенду.
Ми відмовилися від класичного cluster autoscaler на користь такої штуки як Karpenter. Нижче розповім, що це за «звір», і чому він вартий окремої уваги.
Karpenter — це тулза, яка спрощує інфраструктуру Kubernetes за допомогою своєчасних вузлів для будь-якого кластера. Головна ідея — ефективне використання ресурсів та витрат.
Класичний cluster autoscaler базується на такій абстракції як Auto Scaling Groups (ASG), та взаємодіє з Desired capacity через навантаження застосунку. Це все займає час, тому що треба пройти додатковий крок з оновлення Desired capacity в ASG, після якого відбувається збільшення або зменшення кількості робочих вузлів.
Проблема криється в тому, що якщо у вас worker group створена, наприклад, з c6i.xlarge (4 CPU/8 Mem), то для того, щоб заселити Pod, який не вліз на вже піднятий worker node, з requests 1 CPU та 1 Mem, буде піднятий додатковий worker node c6i.xlarge. Доведеться платити за нього повністю, а інші ресурси (3 CPU/7 Mem) будуть неутилізовані.
Що пропонує Karpenter? Він підіймає worker nodes на вимогу й тільки ті, які оптимально підходять відповідно до ресурсів, що запитуються (на основі resources.requests).
Тобто, розглядаючи приклад вище, він підбере максимально близький за ресурсами тип worker node — у нашому випадку це буде, наприклад, t3.small (2 CPU/2 Mem). Це все буде залежати від типів сконфігурованих instance в конфіг-файлі, і можна задати цілу групу різних типів instance, з яких буде обраний оптимальний тип.
А якщо на першій worker node звільнилося місце, якого достатньо для Pod, який був поселений на другий worker node? Це не проблема. Завдяки інструменту Workload Consolidation цей Pod буде переселено на перший worker node, а додатковий worker node буде видалений.
Такий підхід дозволяє не тримати інстанси, коли вони нам непотрібні (тобто видаляти worker node), а за необхідності підіймати, скільки потрібно.
Worker node стартує (від провіженінгу до Ready-статусу) приблизно за
Не знаю як кому, а для мене економія навіть декількох десятків секунд — це суттєвий показник у реагуванні на раптове збільшення навантаження. Тож рекомендую розглянути цього звіра, бо він дійсно чудовий.
Але одну статичну worker group тримати все ж таки доведеться, тому що контролер має десь жити 😅.
Отже, після цих маніпуляцій ми отримуємо K8s кластер, що складається з наступних компонентів:
- Master nodes — вони ж Control plane, знаходяться під повним контролем AWS;
- Worker nodes — ними займаємось уже самі, хоча є можливість використати Managed Worker Nodes, щоби повністю сконцентруватися на роботі застосунку й максимально абстрагуватися від обслуговування кластера;
- балансувальник навантаження — тут можна крутити по-різному. Наприклад, за замовчанням буде створюватись окремий балансувальник на кожний інгрес ресурс, а можна зробити один. Ми використовуємо один балансувальник, направлений на Nginx Ingress (схема 3);
- Karpenter — для керування міньйонами worker nodes.
Мінус цього підходу полягає в тому, що якщо кластер уже створено, але ви забули щось додати, то повторний запуск eksctl видасть помилку, що такий кластер уже існує. Тому треба або зносити сам кластер і додавати його знову, або створювати кластер з новим імʼям, а старий не забувати видаляти.
До речі, створення кластера займає приблизно 20 хвилин, стільки ж і видалення.
І, звичайно, для запуску цього всього «добра» ми зробили wrapper, який виконає все налаштування за нас. Ну а що — ми ліниві «мамкіни» інженери).
Якщо пройтися каскадом, то отримаємо наступне:
- звернення до ALB через HTTPS;
- ALB перенаправляє увесь трафік на Nginx Ingress через HTTP;
- Nginx Ingress перевіряє конфігурацію та направляє трафік на відповідний Ingress ресурс;
- Ingress ресурс направляє трафік на відповідний Service;
- Service направляє трафік на відповідний Pod.
Ось такий флоу виходить.
А далі зануримось у деталі того, як відбувається розгортання гілки, та які технології використовуються для цього. Отже, поїхали.
Розгортання гілки
Загальний вигляд майже всього стеку представлений на схемі 4, але тут не зображені такі ключові елементи, як Jenkins та його jobs, втім, до цього також доберемось :)
Легенда для схеми 4:
- ns-mysql — неймспейс, що містить Pod MySQL;
- ns-elasticsearch — неймспейс, що містить Pod Elasticsearch;
- ns-example-1 — неймспейс із гілкою ns-example-1;
- ns-example-2 — неймспейс із гілкою ns-example-2.
Отже, на схемі ми можемо бачити дві паралельно розгорнуті гілки ns-example-1 та ns-example-2 для тестування. Як воно працює, нумо розбиратися.
Щоби даний процес коректно функціонував, необхідно дотримуватися деяких умов, а саме — флоу виконання задачі.
Пройдімося етапами:
1. Створення задачі у Jira. Важливий елемент для функціонування — номер. Припустимо, що задача називається ns-example-1. Відповідно, наступна матиме назву ns-example-2 і так далі.
2. Задача призначається розробнику, він створює гілку (наприклад, ns-example-1-recommendation-system) і починає працювати з нею. Робить декілька комітів і вирішує подивитися задачу на Stage-оточенні.
Важливо: commit message потрібно робити у відповідному форматі, наприклад, [ns-example-1] commit message. Це потрібно для того, щоб сам пайплайн функціонував коректно для деплою та/ або оновлення неймспейсу. Назва неймспейсу визначатиметься на основі назви коміту, як номер задачі у квадратних дужках: [ns-example-1].
3. Далі розробник переходить у Jenkins та запускає пайплайн на відповідну гілку.
До речі, ми пробували робити автоматичну збірку неймспейсів на Pull requests, але відмовилися від цього, оскільки не всі задачі потребують розгортання та споживання ресурсів.
4. Після успішного завершення пайплайну в нас формуються відповідні адреси для доступу до сервісу з використанням імені неймспейсу. Наприклад: ns-example-1-admin.site-example.com, ns-example-1-site.site-example.com і таке інше.
Розібравшись з умовами, перейдімо до самого процесу розгортання і того, що там використовується.
Думаю, що попередня схема могла викликати багато запитань, на кшталт «а чому один спільний інстанс elasticsearch?» або «чому декілька різних баз MySQL?». І це чудові питання, адже це одна з фішок, які ми використовуємо для швидшого налаштування неймспейсів.
Нумо розбиратися.
Розгортання пайплайну для Stage-оточення
Почнемо з одного з найголовніших героїв нашого стека — Jenkins. Цей неймовірно крутезний «чувак» бере на себе купу усіляких автоматизованих процесів, у тому числі й процес розгортання пайплайну для Stage-оточення.
У процесі розгортання задіяні: job, що відповідає за актуальність бази даних для Stage-оточення, пайплайн, що виконує збірку та розгортання застосунку, job, що відповідає за автоматичне видалення неймспейсів. Далі про них — окремо і детально.
1. Jenkins Job, який відповідає за актуальність бази даних
Цікава річ, яка запускається за розкладом щоночі, наприклад, о 00:00. Нижче на схемі 5 зображено, як це виглядає, а потім поговоримо, як це працює.
Для цієї схеми задіяні два неймспейси:
- ns-mysql — Jenkins job запускає helm chart, що розгортає statefulset MySQL;
- ns-elasticsearch — розгортається лише один раз «ручками», використовується протягом усього циклу життя Stage-оточення.
Перед запуском helm chart, ми перевіряємо наявність неймспейсу ns-mysql. Якщо він є — видаляємо, немає — створюємо і виконуємо інсталювання helm chart. Видалення неймспейсу гарантує нам, що імпортований бекап бази даних буде відповідати дійсності.
Маємо два види бекапів продакшн-бази:
- dev — це продакшн-база, що має зріз даних за останні 15 днів;
- prod — це, відповідно, повний бекап Production бази.
У більшості випадків ми використовуємо dev варіант. Це набагато швидше та легше, аніж щоразу тягнути базу в декілька гігабайтів. Але бувають випадки, коли є необхідність затягнути повну продакшн-базу, наприклад, для перевірки видалення статей або інші схожі кейси.
У неймспейсі ns-mysql запускається init контейнер, що забирає останній бекап бази даних (як ми визначили вище, тип бекапу — dev) з AWS S3.
Ми бекапимося досить часто на добу, адже бекапів багато не буває :)
Далі, запускається основний контейнер з MySQL, у якому викликається entrypoint-скрипт, що виконує імпортування бекапу в MySQL (детальніше цей метод описаний тут).
Але й це ще не все.
Цей helm chart також містить K8s job сутність, яка відповідає за роботу з elasticsearch, що живе в неймспейсі ns-elasticsearch.
Даний job підʼєднується до elasticsearch та виконує створення індексу (якщо він відсутній) і його «заселення» даними, робить свіжий снепшот створеного індексу, локально, до свого внутрішнього стореджа (Elasticsearch snapshot fs), попередньо видаляючи старий.
Це потрібно, аби щоразу під час розгортання нової гілки не виконувати заселення індексу. Ми просто беремо дані зі снепшоту й ресторемо у відповідний індекс для гілки. Ця операція гарантує нам відповідність даних бази й індексу.
Отже, ця Jenkins job відповідає за актуалізацію бази даних та наповнення індексу elasticsearch свіжими даними.
2. Пайплайн, що виконує збірку та розгортання застосунку
Зануримося ще трохи глибше. Отже, це основний пайплайн, який готує неймспейс для розробників і тестувальників. Він складається з таких кроків:
- клонування репозиторію;
- підготовка глобальних env змінних, потрібних для пайплайну, а саме NAMESPACE та COMMIT_HASH;
- паралельна збірка images із використанням Docker кешу;
- тегування images двома тегами: latest та hash commit;
- пуш images до AWS ECR;
- за допомогою Velero робимо volume snapshot MySQL у неймспейсі ns-mysql;
- виконуємо відновлення volume зі снепшотом через механізм мапінгу неймспейсів;
- виконуємо інсталювання або апгрейд helm chart:
- розгортаємо нову версію застосунку;
- розгортаємо нову версію Nginx;
- копіюємо артефакт на S3 бакет.
У розгортанні helm chart задіяні такі K8s jobs:
1. Копіювання файлів SPA на AWS S3 бакет. Щоразу при розгортанні створюємо новий шлях до файлу з хеш-комітами, тобто якщо гілка буде оновлюватись, кожного разу будуть нові шляхи до файлів SPA.
2. Оновлення конфігурації Nginx, підставляючи шлях до файлів SPA з урахуванням хеш-комітів.
3. Відновлення даних індексу Elasticsearch зі снепшоту в новий індекс, який називається відповідно до назви неймспейсу.
У цьому всьому є дуже цікавий інструмент під назвою Velero. Далі розкажу, що за чувак цей «Валєра» і як із ним потоваришувати).
Одна з багатьох проблем, з якою стикається будь-який інженер, що готує пайплайн для розробки, — яким чином відновлювати бекапи в створеному середовищі. І ми — не виключення. Ми спробували декілька варіантів:
- Завантаження дампу безпосередньо в контейнер з подальшим відновленням через mysqldump. Досить тривалий процес через викачування його з S3 (тут дуже залежить від настрою S3 бакету: буває швидко, а буває досить повільно:)) та його відновлення через mysqldump.
- Завантаження архіву бази — цей варіант був дещо гібридним та використовував той самий механізм з окремим неймспейсом ns-mysql, що відновлювався щоночі, архівував базу одразу у файловій системі й заливав цей архів на S3 бакет. А нове середовище, що розгорталося, викачувало архів та розпаковувало його у свою файлову систему. Цей варіант — дещо швидший, ніж перший, і певний час у нас працював. Загалом нас усе влаштовувало, але дуже «чесалися» руки реалізувати саме третій варіант.
- Використання механізму снепшотів — досить цікавий підхід. Єдине обмеження, яке є в цього механізму в K8s зараз — можливість відновлення снепшотів у іншому неймспейсі, тому цей варіант ми відкинули. Але згодом повернулися до нього, адже знайшли рішення, імʼя якого — Velero.
Отже, Velero, як нам каже перша сторінка офіційної документації velero.io, — це інструмент резервного копіювання для Kubernetes. Він дозволяє робити бекап усього кластеру, полегшуючи його міграцію або оновлення.
Але нас він зацікавив тим, що вміє виконувати відновлення снепшотів у різних неймспейсах, а це обмеження, яке має поточний механізм снепшотів самого K8s. Velero під капотом використовує той самий механізм снепшотів, але трохи з тюнінгом :).
Ми не будемо зупинятися на тому, як бекапити або переносити ресурси кластера, а поговоримо про цільове використання в нашій схемі. Але перед тим розглянемо, як це все працює.
Velero складається із сервера, клієнта та обʼєктного стореджа (у нашому випадку AWS S3). Серверна частина — це бекап контролер у K8s, а клієнт у нашому випадку встановлено на Jenkins. В офіційній документації є опис, як це працює.
Варто розуміти, що кожна операція Velero (бекап або відновлення) — це сторонні ресурси, визначені через CRD. І звісно, сам контролер, який це все обробляє та виконує необхідні операції.
Схематично це виглядає ось так:
Встановити Velero досить просто. Спочатку встановимо клієнтську частину застосунку. Викачуємо архів із необхідною версією, розпаковуємо та переміщуємо бінарний файл до директорії з $PATH.
Далі, вмикаємо підтримку фічі SCI для роботи зі снепшотами. Робиться це наступним чином: velero client config set features=EnableCSI
та перевіряємо за допомогою команди: velero client config get features.
Тепер необхідно встановити серверну частину. Для цього, визначимо деякі змінні:
aws_bucket=${s3_bucket_name}
aws_region=${aws_region}
aws_plugin_vers=v1.6.0
csi_plugin_vers=v0.3.0
Встановимо серверну частину за допомогою команди:
velero install --features=EnableCSI --use-volume-snapshots=true --plugins=velero/velero-plugin-for-aws:$aws_plugin_vers,velero/velero-plugin-for-csi:$csi_plugin_vers --provider aws --bucket $aws_bucket --secret-file ~/.aws/velero --backup-location-config region=$aws_region --snapshot-location-config region=$aws_region
Важливо: --
features=EnableCSI, що вмикає механізм снепшотів із використанням CSI snapshotter (встановлюється окремо від CSI драйверу), який, звісно, уже має бути встановлений у вашому кластері.
--
secret-file ~/.aws/velero — це файл з ключем доступу до AWS S3 бакету, куди Velero буде завантажувати метаінформацію про бекапи та відновлення.
За замовчанням застосунок буде встановлений у неймспейс Velero.
Після цього можемо виконувати бекапи та відновлення. У нашому випадку ми робимо бекап лише volumesnapshots та pvc, бо вони є в зоні видимості конкретного неймспейсу.
Бекап виконуємо за допомогою команди:
velero backup create ${backup_name} -include-namespaces=${namespace_name} --include-resources=volumesnapshots.snapshot.storage.k8s.io,persistentvolumeclaims --selector app=mysql --ttl 24h0m0s --csi-snapshot-timeout 5m --wait
Отже, що ми тут маємо:
- ${backup_name} — це назва бекапу, ми використовуємо імʼя неймспейсу, для якого цей бекап буде створено, наприклад, якщо неймспейс називається ns-example-1, то відповідно й бекап буде називатись ns-example-1;
- ${namespace_name} — це назва неймспейсу, у якому знаходяться ресурси, які потрібно забекапити;
--
include-resources — визначаємо, які саме ресурси нам потрібні, у нашому випадку — vs та pvc;--
selector app=mysql — визначаємо специфічний обʼєкт, до якого належать ресурси;--
ttl 24h0m0s — визначаємо час життя бекапу для автоматичного видалення (цим займається GC самого Velero);--
csi-snapshot-timeout 5m — визначаємо timeout для снепшоту (на випадок, якщо щось піде не так, особисто такого не зустрічав);--
wait — не переводити операцію у background, щоб можна було спостерігати за прогресом.
Для перевірки бекапу можна скористатися командами:
- velero get backup — видасть загальну інформацію в зручному вигляді;
- velero describe backups ${backup_name}
--
details — детальніша інформація з визначеного бекапу.
Виконавши бекап, можна перейти до його відновлення. Для цього необхідно виконати команду:
velero restore create ${restore_name} --from-backup ${backup_name} --include-resources=volumesnapshots.snapshot.storage.k8s.io,persistentvolumeclaims --namespace-mappings ${namespace_name}:${target_namespace} --existing-resource-policy=update --wait
Отже, що ми тут маємо:
- ${restore_name} — використовуємо імʼя неймспейсу, для якого цей рестор буде створено, наприклад, якщо неймспейс називається ns-example-1, то відповідно й рестор буде називатись ns-example-1;
- ${backup_name} — це назва бекапу, що був створений у попередньому кроці, з якого буде виконано рестор;
--
include-resources — визначаємо ресурси, які потрібно відресторити, у нашому випадку — vs та pvc;--
namespace-mappings — це той самий тюнінг, який дозволяє відновлювати снепшоти в інших неймспейсах, тут відповідно вказуємо ${namespace_name}:${target_namespace}, з якого неймспейс і в який необхідно виконати мапінг.
Ось і вся магія — чи не круто?
3. Jenkins job, що відповідає за автоматичне видалення неймспейсів
Її завдання — відстежувати час життя визначених неймспейсів (воу, дякую, кеп). Це зроблено для того, щоб виключити простій з різних причин: десь забули видалити, десь просто відклали задачу на потім. Ми поки встановили час життя — 24 години. Job запускається кожні дві години за планувальником (cron job).
Підсумовуючи, хочу сказати, що схема вийшла доволі непогана та має право на існування. Може здаватися складною і заплутаною, але це лише на перший погляд. Загалом це дозволило використовувати Stage-оточення комфортніше та без зайвих питань, як було раніше.
Ще й купу цікавого софту використали. Enjoy!)
PS. Ну, все, наче закінчив з висновками, тепер можна й помідорів у коменти накидати)
3 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів