- Мета Проєкту
- Подальші плани
- TODO
- Структура директорій
- Вихідний код
- Програмні засоби
- Модель процесу розробки
- Git Flow
- Середовище Dev
- Середовище Stage
- Середовище Production
- Опис CI/CD
- Створення файлів конфігурацій та kubernetes deployment з шаблонів
- Підключення Kubernetes кластеру до GitLab
- Опис Ansible ролі
rancher-cluster - Ansible роль 'rancher-cluster'
- Actions:
- ./tasks/rancher-cluster.yml:
- ./tasks/rancher-control.yml:
- ./tasks/rancher-nodes.yml:
- ./tasks/rancher-server.yml:
- ./tasks/ubuntu20/base-packages.yml:
- ./tasks/ubuntu20/docker.yml:
- ./tasks/ubuntu20/hosts.yml:
- ./tasks/ubuntu20/pip.yml:
- ./tasks/ubuntu20/rancher-dirs.yml:
- ./tasks/ubuntu20/timesync.yml:
- ./tasks/ubuntu20/upgrade.yml:
- Tags:
- Variables:
- TODO:
- Author Information
- Actions:
- Приклад Inventory для ролі
rancher-cluster - Опис ресурсів Terraform
- Як створювати та оновлювати документацію по Проєкту
Мені, як Devops спеціалісту, було потрібно вдосконалити наступні навички:
-
Вивчення підходів та інструментів для автоматичного створення документації по інфраструктурі. Максимально автоматизувати цей процес.
-
Підібрати баланс кількість/якість/розуміння по тому як подавати інформацію про інфраструктуру та CI/CD
-
Робота с Docker, збирання образів
-
Робота з on-line сервісом GitLab:
- GitLab CI/CD, gitlab-runner
- GitLab private Docker registry
- GitLab Terraform state files registry
- інтегрування з кластерами Kubernetes
-
Робота з Ansible
-
Робота з Terraform
-
Робота з хмарними сервісами AWS: EKS, Route53, VPC, IAM, ALB, EC2,
-
Створення на базі bare-metal серверів та гіпервізору Proxmox VE інфраструктури для хостингу Kubernetes
-
AWS Cloud Watch, AWS Cloud Trail
-
Google Cloud Platform
-
ELK Stack або Loki
-
Istio Service Mesh
-
Prometheus
-
Grafana
-
системи documentation as code на кшталт Hugo або Docuzaurus
-
інтегрування Hashicorp Vault з Gitlab, Rancher, AWS EKS
-
Jenkins, JenkinsX
-
FluxCD
- Додати в terraform
aws_acm_certificateдля https termination сабдоменів - Рішення для Ingress EKS:
- Nginx Ingress + External DNS
- AWS Application Load Balancer
- AWS Application Load Balancer + External DNS
- Додати в EKS Kubernetes Extentions
- Persistent Volume Claim в AWS EKS
- Створити в terraform декілька IAM з різним рівнем для декількох розробників
- Доробити CI/CD deploy на production
./src– містить вихідні коди мікросервісів проєкт./tools– допоміжні застосунки для проєкту- Docker файли для утиліт, які можуть використовуватись на комп`ютері розробника в docker compose
- bash-скрипти для автоматичного парсінгу та створення
документації -
ansible-autodoc,terraform-docs,merge-markdown - рендерінг jinja2-templates за допомогою
jinja2-renderer
./ci– містить yaml-файли, які використовуються в.gitlab-ci.yml. Кожен файл відповідає за окремий сервіс. Всі файли мають однакову структуру та генеруються з шаблону.gitlab– містить налаштування для з`єднання з кластерами./img– містить всі зображення, яки використовують в документації./templates– містить шаблони jinja2 для створення yaml-файлів для.gitlab-ci.ymlта kubernetes deployment./kubernetes– містить згенеровані з шаблонів yaml-файли для kubernetes deployment./docs– містить конфігураційні файли дляterraform-docs,merge-markdownта для окремих файлів документації, які не генеруються автоматично./ansible– містить ansible-роль для створення кластера Kubernetes за допомогою Rancher./terraform– містить ресурси Terraform для створення AWS EKS
Основою цього тестового Проєкту є досить вдалий код від Google (він дійсно працює) — Online Boutique. Це "хмарний" веб-додаток для електронної комерції, де користувачі можуть переглядати товари, додавати їх у кошик та купувати. Online Boutique складається з 11 мікросервісів, які створені на різних мовах програмування.
| Мікросервіс | Мова програмування | Опис |
|---|---|---|
| frontend: ./src/frontend |
Go | HTTP-сервер для обслуговування веб-сайту. Не потребує реєстрації/входу та автоматично генерує ідентифікатори сеансу для всіх користувачів. |
| cartservice: ./src/cartservice |
C# | Зберігає товари в кошику користувача в Redis і отримує їх. |
| productcatalogservice: ./src/productcatalogservice |
Go | Надає список продуктів із файлу JSON і можливість пошуку продуктів і отримання окремих продуктів. |
| currencyservice: ./src/currencyservice |
Node.js | Конвертує одну грошову суму в іншу валюту. Використовує реальні значення, отримані від Європейського центрального банку. |
| paymentservice: ./src/paymentservice |
Node.js | Стягує з указаної інформації кредитної картки (фіктивну) вказану суму та повертає ідентифікатор транзакції. |
| shippingservice: ./src/shippingservice |
Go | Дає оцінку вартості доставки на основі кошика для покупок. Відправляє товари за вказаною адресою (імітація) |
| emailservice – ./src/emailservice | Python | Надсилає користувачам електронний лист із підтвердженням замовлення (макет). |
| checkoutservice: ./src/checkoutservice |
Go | Отримує кошик користувача, готує замовлення та організовує оплату, доставку та сповіщення електронною поштою. |
| recommendationservice: ./src/recommendationservice |
Python | Рекомендує інші продукти на основі того, що надано в кошику. |
| adservice: ./src/adservice |
Java | Надає текстові оголошення на основі заданих контекстних слів. |
| loadgenerator: ./src/loadgenerator |
Python/Locust | Постійно надсилає запити, що імітують реалістичні потоки покупок користувачів, до інтерфейсу. |
| Назва | Для чого використовувався |
|---|---|
| Docker, Docker Compose | Складання образів Docker, запуск декількох образів в одному робочому процесі |
| Сервіс GitLab.com | Хостинг програмного коду, хостинг приватного реєстру docker, deploy контейнерів в Kubernetes, хостинг Terraform state, Ci/CD з допомогою gitlab-runner |
| Ansible | Автоматичне розгортання Rancher на віртуальних машинах |
| Terraform | Створення кластера EKS на AWS, та супутніх сервісів – Route53, ELB, KMS, VPC |
| Proxmox VE | Створення на bare-metal інфраструктури для Stage (UAT) |
| Rancher (RKE1) | Розгортання та адміністрування Kubernetes на віртуальних машинах. Документація |
| merge-markdown | Автоматична компіляція html сайту, та Readme.md з документацією по всім розділам. Документація. |
| ansible-autodoc | Автоматичне створення документації з коментарів коду Ansible role. Документація |
| terraform-docs | Автоматичне створення документації з коментарів коду Terraform файлів. Документація |
| jinja2-render | Створює файли за допомогою jinja2-templates. Документація |
| detect-secrets | Шукає файли з sensitive content у файлах Проєкту. Документація |
| pluralith | Візуалізує інфраструктуру aws із файлу terraform.state ( не зовсім корисний але допомагає бачити масштаб ). Документація |
Структуру розробки можна поділити на три складові:
- Програмний код, який супроводжується розробниками
- Створення Docker-образів з програмного коду, хостинг образів в приватному реєстрі Docker
- Запуск та оркестрування контейнерів на базі цих образів
Для ускладнення проєкт програмний код всіх мікросервісів був об'єднаний в monorepo, у
вигляді ./src/{microservice-name}
Використовується схема gitlab-flow:
- Dev – основна гілка розробки.
- Stage – гілка для тестування (UAT – User Acceptible Testing)
- Production – гілка, на основі якої працює продуктивне середовище
Розгортується на комп'ютері розробника. Складання образу відбувається в Docker за допомогою gitlab-runner. Він може
працювати на комп'ютері розробника, або на окремому сервері. Після складання образ публікується у приватному реєстрі на
GitLab. Йому призначаються теги -- latest, та git short sha1.
Це дозволяє використовувати образи, які складені іншими розробниками. Їх можна знаходити по схемі:
№ завдання в Jira ===> № у назві git-branch ===> git short-sha ===> docker-image: short-sha1
Запуск та розгортання відбувається в docker-compose.yml. Спочатку там використовуються образи з тегом latest.
Треба додати docker-compose.yml в .gitignore, та потім використовувати в ньому робочі
теги ( latest , short sha1).
В моєму випадку використовувались 5 віртуальних машин: 2 ядра CPU, 4-8 Gb оперативної пам'яті, Ubuntu 20 LTS. Зовнішній доступ — фіксований зовнішній IP, віртуальна машина з PFSense + Haproxy.
На базі Rancher був розгорнутий кластер Kubernetes з окремим project таnamespace для цього Проєкту. Також було
підключено приватний реєстр GitLab для образів Docker.
Зовнішній трафік був спрямований на PFSense + Haproxy з використанням Letsencrypt для SSL-termination, далі він
пересилався на NodeIP кластера Kubernetes
1. Обробка змін в каталогах с вихідним кодом мікросервісів.
Основний процес розробки. При змінах в певних каталогах src/{project_name}/* запускаються автоматичні дії:
-
Збирання образу Docker
-
После збирання,образу призначається "службовий" тег, який визначається у змінній
$DEBUG:
DEBUG_TAG: $CI_REGISTRY/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA-
Образ с тегом з пункту 2 завантажується в реєстр. Необхідно щоб було зафіксовано артифакт успішного збирання образу, якщо далі буде збій pipeline.
-
Завантаження із реєстру образа с тегом
$DEBUG -
Надання тегів
DEV_TAG:- тег для розробників, щоб вони завжди могли працювати з останньою версією –$CI_REGISTRY/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/$IMAGE_NAME:latest, тегуBRANCH_TAG-$CI_REGISTRY/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/$IMAGE_NAME:$CI_COMMIT_BRANCH- тег гілки з якої відбувається build. Це зроблено з метою ілюстрації. Можна додавати теги поgit-tag,releaseта ін. -
Завантаження у реєстр образів с тегами з пункту 5
-
Зміна образа, який працює з відповідним
debpoimentу кластерахkubernetes. Наприклад, для тестування, образ змінюється на тег$DEBUG.
2. Мати гнучкі функції по збиранню образів Docker
Повинна бути профілактика реєстру образів – видалення старих, безпекові тестування та інше. Але інколи для debug або для усунення технічного боргу, потрібно мати старий образ, або образ якогось git-commit. Може виникнути ситуація, коли потрібно мігрувати в інший приватний реєстр Docker образів. Для цього є функція автоматичного збирання образу якщо в gitc ommint є спеціальний текст.
Наприклад, щоб зібрати образ для мікросервісу cartservice потрібно в тексті опису git commit
вказати cartservice-manual . Тоді буде автоматично збиратись відповідний образ по build workflow з першого розділу.
**3. Мати можливість автоматично робити Kubernetes Deployment у відповідний кластер **
Для всіх сервісів створені deployment в кластер kubernetes, які розташовані в
в ./kubernetes/deploy-{envirinment_name}-{service_name}.yaml.
Тобто файл Kubernetes Deployment для мікросервісу
cartserviceдля середовищаstageбуде мати назвуdeploy-stage-cartservice.yaml
Якщо ці фйли змінено, то за допомогою образу bitnamy:kubectl виконується оновлення deployment, що змінився. Або
створюється новий, якщо з`являється новий файл.
Змінні, які не є частиною IaC-продуктів (Ansible, Terraform) використовуються у наступних файлах:
- Gitlab CI — на рівні Проєкту в панелі керування сервером, на рівні змінних у
.gitlab-cy.yml. Ці змінні опрацьовуються gitlab-runner - ./templates/ci-template.yaml.j2 — змінні, які використовуються
jinja2-renderпри створенні шаблонів, з яких генеруються файли створення docker образів мікросервісів в./ci/ci-{{service}}.yml - ./templates/deploy-template.yaml.j2 — змінні, які використовуються
jinja2-renderпри створенні шаблонів, з яких генеруються файли kubernetes deployment для мікросервісів./kubernetes/{service}-{git_branch_or_stage}-deploiment.yml
| Змінна | Опис |
|---|---|
CI_REGISTRY_USER |
Користувач приватного реєстру Docker Gitlab. Встановлюється як глобальна змінна групи або Проєкту в Gitlab |
CI_REGISTRY_PASSWORD |
Пароль користувача приватного реєстру Docker Gitlab. Встановлюється як глобальна змінна групи або Проєкту в Gitlab |
DEBUG_TAG |
$CI_REGISTRY/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA - складений тег для образів з git-short-sha |
BRANCH_TAG |
$CI_REGISTRY/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/$IMAGE_NAME:$CI_COMMIT_BRANCH - складений тег для образів з git-branch |
DEV_TAG |
$CI_REGISTRY/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/$IMAGE_NAME:latest - складений тег для образів з latest |
DEBUG_IMAGE |
$IMAGE_NAME:$CI_COMMIT_SHORT_SHA |
DOCKER_REGISTRY_URL |
url Docker Hub або аналогів, використовувати, якщо не плануєте користуватись приватним реєстром Docker Gitlab |
DOCKER_REGISTRY_USER |
login користувача у Docker Hub або аналог |
DOCKER_REGISTRY_PASSWD |
пароль користувача Docker Hub або аналогів |
BUILD_CONTEXT |
Docker WORKDIR - відносно цієї директорії буде відбуватися складання образу Docker |
IMAGE_NAME |
Назва образу мікросервісу |
KUBE_CONTEXT |
uri зв'язку gitlab.com з кластером Kubernetes |
PROJECT_NAMESPACE |
Kubernetes namespace в якому будуть розгорнуті deployments мікросервисів |
DEFAULT_IMAGE |
Образ по замовчуванню, на базі якого gitlab-runner буде складати образи мікросервісів |
| Змінна | Опис |
|---|---|
project_path |
GitLab uri поточного Проєкту на сервері. Приклад: gitlab.example.com/group-name/project-name |
branch |
Гілка git, в якої розробляються скрипти для деплою |
service |
Назва мікросервісу |
build_context |
Docker WORKDIR - відносно цієї директорії буде відбуватися складання образу Docker, дублюється за відповідною змінною в BUILD_CONTEXT GitLab Ci |
environment_name |
Назва оточення, на яке буде розгортатись deploy. Дублює змінну GitLab CI –CI_ENVIRONMENT_NAME |
environment_url |
URL оточення, на яке буде розгортатись deploy. Дублює змінну GitLab CI –CI_ENVIRONMENT_URL |
autorules |
Перелік директорій в Проєкті, в яких будуть відстежуватись зміни файлів для автоматичного збирання образів та deploy |
Приклади використання змінних:
Проєкт gitlab та git branch, з якого будуть включені файли ci_docker_build.yml та ci_variables.yml:
include:
- project: "{{ project_path }}"
ref: "{{ branch }}"
file:
- ci/ci_docker_build.yml
- ci/ci_variables.ymlВизначення GitLab environment:
environment:
name: { { environment_name } }
url: { { environment_url } }Визначення правил для автоматичного збирання образів докер для сервісу:
.{{ service }}_auto_rules:
rules:
- changes:
{ { autorules } }
when: always| Змінна | Опис |
|---|---|
deployment_name |
Назва kubernetes deploymet, яка буде вказана в Cluster Control. Бажано вказувати назву мікросервісу, або вказувати зв`язок з оточенням. Приклад: "deployment_name ": "stage-emailservice". Використовується в labels |
image_pull_secrets |
Kubernetes Secret, який містить ідентіфікатори доступу до приватного реєстру docker образів |
app_name |
Назва мікросервісу. Приклад: "app_name": "emailservice". Використовується в labels |
app_version |
Версія або реліз мікросервісу. Бажано використовувати правила для Semantic Versioning. Використовується в labels |
project_name |
Якщо використовується Rancher, назва Проєкту у Rancher. В Rancher до POD у межах Проєкту заборонений трафік POD за межами Проєкту. Це дає додаткову можливість захисту. !!! Проєкт не namespace. В межах Проєкту може буди декілько namespace. Використовується в labels |
namespace_name |
namespace в який буде проводитись Kubernetes Deployment. Використовується в labels |
app_creator |
email або інший контакт розробника. Використовується в labels |
app_supporter |
Відповідальний за support. mail або інший контакт. Використовується в labels |
environment_stage |
Назва оточення, на яке буде розгортатись deploy. Дублює змінну GitLab CI –CI_ENVIRONMENT_NAME, та environment_name в ./templates/ci-template.yaml.j2. Використовується в labels |
environment_uri |
URL оточення, на яке буде розгортатись deploy. Дублює змінну GitLab CI –CI_ENVIRONMENT_URL, та environment_url в ./templates/ci-template.yaml.j2. Використовується в labels |
app_component |
Яку функцію виконує мікросервіс в загальному продукті. Використовується в labels. Приклад: "app_component": "email-campain" |
business_domain |
Показує зв`язок між мікросервісом, та бізнес-функцією на яку він має вплив. Використовується в labels. Приклад: "business_domain": "marketing" |
business_owner |
Контакт відділу або відповідального за бізнес-функцію. Використовується в labels. Приклад: "business_owner": "marketing@company.com" |
app_instance |
На якій базі розгорнуто оточення для deploy. baremetal, rancher, eks. Використовується в labels. Приклад: "app_instance": "aws-eks"; "app_instance": "baremetal-rancher"; "app_instance": "ec2-kubespray" |
app_replicas |
Кількість екземплярів мікросервісу, які мають працювати |
image |
Повний URL образу Docker. Приклад для Gitlab: registry.gitlab.com/{gitlab_group}/project/{service_name}:{tag} |
requests_memory |
Скільки відводити оперативної пам`яті (мінімум) для одного екземпляру контейнера |
requests_cpu |
Скільки відводити cpu_millicores (мінімум) для одного екземпляру контейнера |
limits_memory |
Скільки відводити оперативної пам`яті (максимум) для одного екземпляру контейнера |
limits_cpu |
Скільки відводити cpu_millicores (максимум) для одного екземпляру контейнера |
ports_container_port |
Порт, на якому POD приймає вхідний трафік |
port_name |
Назва порту |
ports_protocol |
Протокол, по якому працює POD в Kubernetes Deployment. TCP, UDP, SCTP |
service_port |
Порт, на який контейнер приймає вхідний трафік. Відноситься до Kubernetes Service |
service_protocol |
Протокол, по якому працює Kubernetes Service. TCP, UDP, SCTP. Приклад: spec.ports.protocol: TCP. Відноситься до Kubernetes Service |
service_target_port |
Порт на який буде спрямовано трафік з service_port. Відноситься до Kubernetes Service |
service_ip_type |
Яким чином Kubernetes Service публікує порти — ExternalName, ClusterIP, NodePort, LoadBalancer. Відноситься до Kubernetes Service |
app_env |
Змінні для специфікації контейнеру |
{service} — змінна, в яку підставляється назва мікросервісу (
./scr/{service})
Згідно схеми gitlab-flow — це основна робоча гілка. Після коміту автоматично буде створено образ Docker з відповідними тегами:
- {service}-image:latest — основний робочий артефакт гілки dev. Використовується в
docker-compose.ymlна комп'ютері розробника. Для тестування власної розробки та останніх комітів мікросервісів колег - {service}-image:sha1 — використовується для зв'язку: № Jira Task ===> № Jira Task у назві git-branch ===> git
short-sha ===> {service}-image:sha1. Потрібен для debug, або для перевірки робочих гіпротез на
локальному
docker-compose.yml. - {service}-image:dev — сінонім {service}-image:latest
Всі ці теги будуть завантажені у Docker Registry (Docker Hub або приватний)
Гілка для тестів.
Створює образ з тегами:
- {service}-image:sha1 — debug або Jira Task
- {service}-image:stage — артифакт, який буде автоматично викладено на середовище stage (Proxmox + Rancher) за допомогою ./kubernetes/{service}-stage-deploiment.yml
Всі ці теги будуть завантажені у Docker Registry (Docker Hub або приватний)
Гілка для робочого середовища.
Створює образ з тегами:
- {service}-image:sha1 — debug або Jira Task
- {service}-image:master — артифакт, який буде автоматично викладено на середовище production (AWS EKS) за допомогою ./kubernetes/{service}-master-deploiment.yml
Всі ці теги будуть завантажені у Docker Registry (Docker Hub або приватний)
Головний файл стандарний — ./gitlab-ci.yml у "корені" Проєкту. Він включає решту файлів, яка підключаються за
допомогою директиви include.
У сервісі gitlab.com у мене не вийшло підключати файли за допомогою (local), повідомлення linter — "file not found"
include: - local: '/templates/.gitlab-ci-template.yml'Тому довелося використовувати "hack":
include: - project: "project_name" ref: "ci-template" file: - ci/ci_docker_build.yml - ci/ci_variables.yml - ci/kubectl_handler.ymlДе у якості
project_nameбув цей Проєкт.
Звісно, що є принята практика створювати окремий Проєкт для всій логіки CI/CD, та писати узагальнені скрипти. Але це занадто ускладнює учбовий Проєкт.
Але для коректного використання оператору
includeпотрібно вказувати параметрref:.Тому всі зміні в скриптах GitLabCI, Kubernetes Deployment та інше потрібно виконувати в гілці
ci-template.Звісно це не зовсім зручно, ало поки буде так.
Кожен сервіс має свій "іменний" CI/CD-файл ./ci/ci-{service}.yml та свій Kubernetes
Deploiment ./kubernetes/{service}-stage-deploiment.yml.
- build-debug-image — створюється образ docker, надається тег short-sha, образ завантажується в docker registry
- pull-debug — вивантаження з реєстру, надання інших тегів
- push-image — завантаження нових тегів в реєстр
- deploy — деплой в kubernetes cluster
До Проєкту додані файли, які можна використовуваті в інших Проєктах.
./ci/ci_docker_build.yml — містить hidden jobs по
створенню, тегуванню та завантаженню образів. Ці дії можна підключати за
допомогою anchors
./ci/ci_variables.yml — містить основні змінні, які підключаються до всіх ci-файлів.
Ці два файли підключаються в кожен ./ci/ci-{service}.yml
Система назви jobs:
- {{service}}_auto_rules — правила, по яким спрацьювує автоматичний build docker image
- {{service}}_manual_rules — правила, по яким спрацьовує ручний build docker image
- auto_{{service}}_debug — автоматичне створення и push docker image:sha1
- manual_{{service}}_debug — ручне створення и push docker image:sha1
- pull_auto_{{service}}_sha — pull образів docker з артефакту auto_{{service}}_debug
- pull_manual_{{service}}_sha — pull образів docker з manual_{{service}}_debug
- push_currencyservice_image — push образів docker з тегами latest, branch_name
Відстежування змін файлів у директоріях:
.emailservice_auto_rules:
rules:
- changes:
- src/{{service}}/**/**/*
when: alwaysВідстежування тексу git commit:
.emailservice_manual_rules:
rules:
- if: "$CI_COMMIT_MESSAGE =~ /emailservice-manual/"
when: alwaysЗавантаження образів docker в реєстр:
push_{{ service }}_image:
stage: push-image
# Змінні
variables:
<<: *global_var
IMAGE_NAME: { { service } }
# Перевикористання jobs з умовами для завантаження образів
extends:
- .deploy_latest_tag_to_registry
- .deploy_branch_tag_to_registry
# Умиви, за яких відбувається завантаження образів
rules:
- !reference [ ".{{ service }}_auto_rules", "rules" ]
- !reference [ ".{{ service }}_manual_rules", "rules" ]
# Залежність від виконання попередніх jobs
needs:
- job: pull_manual_{{ service }}_sha
optional: true
- job: pull_auto_{{ service }}_sha
optional: trueЗавантаження образу за тегом latest, якщо назва git brabch дорівнюєdev, для використання на комп`ютері розробника
за допомогою docker compose:
# Extnetd from ./ci/ci_docker_build.yml
.deploy_latest_tag_to_registry:
stage: push-image
script:
- docker tag $DEBUG_TAG $DEV_TAG
- docker push $DEV_TAG
rules:
- if: "$CI_COMMIT_BRANCH =~ /dev/"
when: auto
when: on_successЗавантаження образу з тегом branch_name, якщо назва git brach дорівнює stage або main:
# Extnetd from ./ci/ci_docker_build.yml
.deploy_branch_tag_to_registry:
stage: push-image
script:
- docker tag $DEBUG_TAG $BRANCH_TAG
- docker push $BRANCH_TAG
rules:
- if: "$CI_COMMIT_BRANCH =~ /stage/ || $CI_COMMIT_BRANCH =~ /main/"
when: on_successКожен мікросервіс має свої конфігураційні файли:
- ./ci/ci-{service}.yml — GitLab CI файл для створення образів мікросервісів та розгортання їх у кластер Kubernetes
- ./kubernetes/{service}-{environment}-deployment.yaml — Kubernetes Deployment для мікросервісу для конкретного
оточення (
stage,prod) - ./kubernetes/{service}-{environment}-service.yaml — Kubernetes Service для мікросервісу для конкретного
оточення (
stage,prod)
Всі ці файли створюються з jinja2 templates за допомогою python скрипта jinja2-render.
pip install jinja2-renderЗастосування:
jinja2-render -c {файл контексту} -f {шаблон з якого створюються файли} -o {віхідний файл} {пункт контексту}файл контексту – json-файл в якому визначаються змінні та їх значення, які потім будуть застосовані в вихідних файлах.
шаблон з якого створюються файли – jinja2 шаблон, в якому застосовуються змінні
пункт контексту – першій рівень у списку в json
Приклад:
файлу контексту – debpoy-prod-env.py:
CONTEXTS = {
"adservice": {
"deployment_name": "prod-adservice",
"deployment_app": "adservice",
"app_replicas": "1",
"image": "registry.gitlab.com/devops1121/microservices-demo-google/adservice:stage",
"image_pull_secrets": "registry.gitlab",
"app_name": "adservice",
"app_version": "0.1.0",
"project_name": "microservices-demo",
"namespace_name": "prod-microservices-demo",
"app_creator": "developer_company.com",
"app_supporter": "devops_company.com",
"environment_stage": "prod",
"environment_uri": "prod-env.company.com",
"app_component": "advertise-service",
"business_domain": "sales",
"business_owner": "marketing_company.com",
"app_instance": "baremetal-rancher",
"requests_memory": "512Mi",
"requests_cpu": "500m",
"limits_memory": "1Gi",
"limits_cpu": "1000m",
"ports_container_port": "9555",
"ports_protocol": "TCP",
"port_name": "grpc",
"service_port_name": "grpc",
"service_port": "9555",
"service_protocol": "TCP",
"service_target_port": "9556",
"service_ip_type": "ClusterIP",
"app_env": "- name: PORT\n value: \"9555\"\n - name: \"DISABLE_STATS\"\n value: \"1\"\n - name: \"DISABLE_TRACING\"\n value: \"1\"\n"
},
"cartservice": {
"deployment_name": "prod-cartservice",
"deployment_app": "cartservice",
"app_replicas": "1",
"image": "registry.gitlab.com/devops1121/microservices-demo-google/cartservice:stage",
"image_pull_secrets": "registry.gitlab",
"app_name": "cartservice",
"app_version": "0.1.0",
"project_name": "microservices-demo",
"namespace_name": "prod-microservices-demo",
"app_creator": "developer_company.com",
"app_supporter": "devops_company.com",
"environment_stage": "prod",
"environment_uri": "prod-env.company.com",
"app_component": "advertise-service",
"business_domain": "sales",
"business_owner": "marketing_company.com",
"app_instance": "baremetal-rancher",
"requests_memory": "512Mi",
"requests_cpu": "500m",
"limits_memory": "1Gi",
"limits_cpu": "1000m",
"ports_container_port": "7070",
"ports_protocol": "TCP",
"port_name": "grpc",
"service_port_name": "web",
"service_port": "7070",
"service_protocol": "TCP",
"service_target_port": "7070",
"service_ip_type": "ClusterIP",
"app_env": "- name: REDIS_ADDR\n value: \"redis-cart:6379\"\n"
}
}Файл шаблону – deploy-template.yaml.j2
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ deployment_name }}
namespace: {{ namespace_name }}
labels:
deployment_name: {{ deployment_name }}
app.kubernetes.io/name: {{ app_name }}
app.kubernetes.io/version: "{{ app_version }}"
project_name: {{ project_name }}
namespace_name: {{ namespace_name }}
app.kubernetes.io/created-by: {{ app_creator }}
app.kubernetes.io/managed-by: {{ app_supporter }}
environment_stage: {{ environment_stage }}
environment_uri: {{ environment_uri }}
app.kubernetes.io/component: {{ app_component }}
business_domain: {{ business_domain }}
business_owner: {{ business_owner}}
app.kubernetes.io/instance: {{ app_instance }}
spec:
replicas: {{ app_replicas }}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
template:
metadata:
name: {{ deployment_name }}
namespace: {{ namespace_name }}
labels:
deployment_name: {{ deployment_name }}
deployment_app: {{ deployment_app }}
app.kubernetes.io/name: {{ app_name }}
app.kubernetes.io/version: "{{ app_version }}"
project_name: {{ project_name }}
namespace_name: {{ namespace_name }}
spec:
containers:
- name: {{ deployment_name }}
image: {{ image }}
imagePullPolicy: Always
resources:
requests:
memory: {{ requests_memory }}
cpu: {{ requests_cpu }}
limits:
memory: {{ limits_memory }}
cpu: {{ limits_cpu }}
ports:
- containerPort: {{ ports_container_port }}
protocol: {{ ports_protocol }}
name: {{port_name}}
env:
{{app_env}}
restartPolicy: Always
imagePullSecrets:
- name: {{ image_pull_secrets }}
selector:
matchLabels:
deployment_app: {{ deployment_app }}
namespace_name: {{ namespace_name }}
environment_stage: {{ environment_stage }}Створити Kubernetes deployment для adservice:
jinja2-render -c ./debpoy-prod-env.py -f ./deploy-template.yaml.j2 -o ./adservice-prod-deploy.yaml adserviceСтворити Kubernetes deployment для cartservice:
jinja2-render -c ./debpoy-prod-env.py -f ./deploy-template.yaml.j2 -o ./cartservice-prod-deploy.yaml cartservice- файл контексту для Gitlab CI — ./templates/ci-env.py
- шаблон з якого створюються файли Gitlab CI — ./templates/ci-template.yaml.j2
- вихідні файли — ./ci/ci_{service}.yml
- bash скрипт, який автоматично створює файли для всіх сервісів в ./src/ — ./tools/ci-template.sh
-
файл контексту для Kubernetes Deployment — ./templates/debpoy-stage-env.py (оточення
stage), ./templates/debpoy-prod-env.py (оточенняprod) -
шаблон з якого створюються файли Kubernetes Deployment — ./templates/deploy-template.yaml.j2
-
вихідні файли — ./kubernetes/{service}-{environment}-deployment.yaml
-
bash скрипт, який автоматично створює файли для всіх сервісів в ./src/ — ./tools/kube-deploy-template.sh Застосування:
./kube-deploy-template.sh prod або ./kube-deploy-template.sh stage
Скрипт використовує результат команди
ls ./srcяк перелік значень для створення відповідних файлів. Тобто, якщо є каталог сервісаadservice, то повинен будт відповідний контекс у файлі контексту.
У адмініструванні Kubernetes можуть бути різні ситуації, коли потрібно створювати та зміновати різні об`єкті Kubernetes. Ці віпалки важко продумати заздалегідь та створити відповідні jinja2 templates. Тому решту файлів для об` єктів Kubernetes потрібно створювати самойстійно у форматі:
./kubernetes/{service}-{environment}-{kubernetes-resource}.yaml.
Зміни у диркторії ./kubernetes/ обпрацьовуються автоматично GitLabCI pipeline
gitla-agent інтегрує Gitlab с кластером Kubernetes. Працює навіть за NAT.
Потрібно створити порожній файл в .gitlab/agents/{agent-name}/config.yaml
Потім зареєструвати агента в GitLab:
- Infrastructure ==> Kubernetes clusters ==> Create agent
- Ввести
{agent-name}та отримати токен -{cluster-token} - В кластері:
helm repo add gitlab https://charts.gitlab.io helm repo update helm upgrade --install `{agent-name}` gitlab/gitlab-agent \ --namespace gitlab-agent-`{agent-name}` \ --create-namespace \ --set image.tag=v15.9.0-rc1 \ --set config.token=`{cluster-token}` \ --set config.kasAddress=wss://kas.gitlab.com
Це нам каже офіційна документація.
Але в мене в Rancher запрацювало якщо додати HELM репозіторій та додади агента за допомогою yaml:
affinity: { }
config:
kasAddress: wss://kas.gitlab.com
token: `{ cluster-token }`
extraEnv: [ ]
fullnameOverride: ''
image:
pullPolicy: IfNotPresent
repository: registry.gitlab.com/gitlab-org/cluster-integration/gitlab-agent/agentk
tag: 'v15.2.0'
imagePullSecrets: [ ]
nameOverride: ''
nodeSelector: { }
podAnnotations:
prometheus.io/path: /metrics
prometheus.io/port: '8080'
prometheus.io/scrape: 'true'
rbac:
create: true
resources: { }
serviceAccount:
annotations: { }
create: true
tolerations: [ ]
Агент може працювати в двох режимах:
- GitOps workflow - GitLab agent в кластері періодично відстежує зміни у віхідному коді Kubernetes Deployment, та робить pull з репозіторію Проєкту
- GitLab CI/CD workflow - за допомогою
kubectlpipeline push зміни у віхідному коді Kubernetes Deployment у кластер
Якщо використовувати GitLab CI/CD workflow, то файл .gitlab/agents/{agent-name}/config.yaml повинен мати такий
вигляд:
ci_access:
projects:
- id: path/to/projectДля встановлення Kubernetes використовується RKE1 (Rancher Kubernetes Engine v.1) від Rancher Labs.
RKE — це дистрибутив Kubernetes, сертифікований CNCF, якій повністю працює в контейнерах Docker
В ролі використовуються 5 віртуальних машин (нод):
- 1 Rancher Server — на неї встановлюється первинний набір ПЗ та нода для кластера
etcd - 2 Control Plane
Node —
kube-apiserver,etcd,cloud controller manager,kube-controller manager,kube-sheduler - 2 Worker Node —
kubelet,kubeproxy
В інфраструктурі використовується ProxmoxVE KVM з Ubuntu 20.04 (LTS)
Оскільки Rancher Kubernetes Engine ще "молодий" продукт — ще не все добре працює, тому рекомендовано використовувати саме ті версії системного ПЗ, які 100% працюють в цій ролі:
- rancher_image: rancher:v2.6.6
- rancher_agent_image: rancher-agent:v2.6.6
- ansible_distribution: ubuntu
- ansible_distribution_release: focal
- rancher_k8s_version: 1.20.15
- docker: 5:20.10.12~3-0~ubuntu-focalРоль створена для автоматичного встановлення кластера kubernetes для навчання та експериментів.
Actions performed by this role
- Імпортує
rancher_api_keyз "віртуального" хоста. Створює Kubernetes кластер зі значеннями зміннихrancher_k8s_versionтаrancher_cluster_name. Генерує bearer token для реєстрації нод -rancher_registration_token, посилання для реєстрації нод -rancher_registration_link, команду для реєстрації нод -rancher_node_register, до якої буде додаватись зміннаrancher_role_flags, в залежності від ролі ноди в кластері. Всі ці змінні експортуються через "віртуальні" хости для інших задач. (rancher-cluster)
- Очікує три хвилини щоб кластер повністю розгорнувся, імпортує значення змінної
export_node_startз віртуального хоста, додаєControl Planeноду до кластера. Задача виконується тільки на нодах де в inventoryrancher_node_role: "control"(rancher-cluster)
- Очікує 5 хвилин щоб кластер та
Control Planeноди повністю розгорнулися, імпортує значення змінноїexport_node_startз віртуального хоста, додаєWorker Nodeдо кластера. Задача виконується тільки на нодах де в inventoryrancher_node_role: "node"(rancher-cluster)
- Запускає на ноді з
rancher_node_role: "server"контейнерranсher-server, який запускає процес інсталювання, та приймає з'єднання на портах 80,443. На ці операції відведено 3 хвилини (в залежності від "заліза" можна вказати свої параметри). Потім за допомогоюcurlздійснюються 3 спроби логину на сервер - це "хак", бо Rancher незрозуміло поводить себе с токенами, дуже часто їх змінює. Експерименти показали, що з третьої спроби логін на сервер працює без помилок. Також все запрацювало тільки після заміниansible.builtin.commandнаansible.builtin.shellв завданнях. Результат сесії логіну - це json, з якого за допомогоюjqзнаходиться значення параметраtoken. Він зберігається для інших завдань за допомогою "віртуального" host в inventory:ansible.builtin.add_host. Таким чином, експортується зміннаexport_login_token. За допомогоюrancher_login_token.stdoutзавантажується api key з сервера, який також експортується через "віртуальний" хост, як зміннаrancher_api_key. За допомогоюrancher_api_keyпризначуєтеся значення змінноїrancher_server_url_name. (rancher-cluster)
- Визначає які пакети вже є в системі та встановлює лише ті, яких не вистачає. Необхідні пакети визначаються у
змінній
base_packages. Виразname: {{ base_packages | difference(ansible_facts.packages) }}порівнює список пакетів зі змінною та з наявними в системі, тому встановлюється тільки різниця. (rancher-cluster)
- Підключає apt repository від Docker, перемикає на нього інсталятор, встановлює Docker, вмикає маршрутизацію
ipv4, вимикає firewall (тимчасове рішення для тестів), завантажує образи Docker від Rancher, які визначаються через змінніrancher_agent_imageтаrancher_image. (rancher-cluster)
- Призначає ноді необхідний
hostnameта генерує коректний файл/etc/hosts, в якому вказані всі ноди с їх IP-адресами таhostname. Для цього використовується шаблон з/templates/hosts.j2(rancher-cluster)
- Встановлення необхідних python pip-пакетів. Пакети можна додавати в перелік
loop. Потребує пакетpython3-pip, який потрібно встановити на ноду. (rancher-cluster)
- Створює каталоги для даних Rancher, які потім будуть примонтовані в Docker контейнери через
volumes. ( rancher-cluster)
- Створює конфігураційний файл для демона точного часу
tymesyncd, завантажує на всі ноди, та перезапускає сервіс, щоб не було розбіжностей в часі. Використовується шаблон/templates/timesyncd.conf.j2, але без змінних для серверів часу та інших параметрів. (rancher-cluster)
- Вимикає автоматичне оновлення пакетів, оновлює наявні до останньої версії, та перезавантажує ноди на випадок якщо було встановлено нове ядро linux. (rancher-cluster)
ansible_port:22- SSH host portansible_host: `` - Host IPansible_distribution:ubuntu- Підтримуються: Alpine, Altlinux, Amazon, Archlinux, ClearLinux, Coreos, Centos, Debian, Gentoo, Mandriva, NA, OpenWrt, OracleLinux, RedHat, Slackware, SMGL, SUSE, VMwareESX. Нижній регістр.ansible_distribution_release:focal- Версія дистрибутиву. В цієї ролі використовується focal, тобто Ubuntu 20.04 (LTS)rancher_image:rancher:v2.6.6- Тег Docker образу сервера Rancher. Буде встановлено на ноду зrancher_node_role: "server"rancher_agent_image:rancher-agent:v2.6.6- Тег Docker образу агента rancher, котрий встановлюється на кожну ноду для зв'язку з сервером Rancher. Буде встановлено на ноду зrancher_node_role: "node"rancher_node_role:node- "Маркер" який вказує на роль ноди в кластері.server- головний сервер, на базі якого буде будуватися кластер, та встановлюватися необхідне програмне забезпечення на ноди. Потрібна хоча б одна нода с цією роллю.control- буде встановлено KubernetesControl Plane.node- робоча нода (Worker Node), на яку буде встановлено ПЗ Rancher. Ця змінна по змісту схожа наrancher_role_flags, але в неї інша функція.rancher_k8s_version:1.20.15- Версія Kubernetes, яку буде встановлено на всі ноди.rancher_setup_password: `` - Пароль для з'єднання с web-інтерфейсом та API для адміністрування. Адреса для з'єднання визначається в змінноїrancher_server_url_name. Потрібно вказати тільки на нодах з `rancher_node_role: "server"`rancher_role_flags:--worker- Роль, яку буде виконувати нода.--etcd- одна з нод в кластері etcd, які зберігають поточний стан кластера Kubernetes. Потрібно як найменш 3 ноди с цією роллю.--controlplane- нода на якої виконуються Kubernetes shedulers та ін., може бути в одному екземплярі, але бажано щоб їх було декілька.--worker- будуть встановленіkubelet,kube-proxy. Для--etcdта--controlplaneпотрібно достатньо ресурсів, тому бажано відводити для них не менш ніж 8Gb оперативної пам'яті. Всі ці ролі також можуть буди встановлені на одну ноду.rancher_server_url_name:"https://192.168.101.141"- Адреса для з'єднання с web-інтерфейсом та API сервера. Бажано використовувати IP-адресу. Або коректно налаштувати локальну DNS-зону на DNS-сервері.rancher_init_ip: `` - IP адреса до якої будуть з'єднуватися ноди за допомогоюcurl. Потрібно вказувати IP-адресу ноди з `rancher_node_role: "server"`rancher_cluster_name: `` - Ім'я кластера Kubernetes, який буде створено після встановлення всього необхідного ПЗ. Всі сценарії в ролі будуть використовувати з'єднання нод з цим кластером.base_packages:["bash-completion", "bind9utils", "curl", "git", "git-extras", "htop", "iptraf", "lsof"]- Базові програмні пакети, яки необхідні для системного адміністрування ноди.
- Замінити вимкнення firewall логікою, яка відкриває потрібні сервіси в залежності від значення
змінної
rancher_node_role(rancher-cluster)
- Додати логіку пошуку події оновлення ядра, та поставити як умову для перезавантаження ноди, якщо встановлено нове ядро. (rancher-cluster)
This playbook was created by: Andriy pustovit
Documentation generated using: Ansible-autodoc
kubernetes:
gather_facts: false
hosts:
node1.example.net:
ansible_port: 22
ansible_host: 192.168.101.141
ansible_distribution: ubuntu
ansible_distribution_release: focal
ansible_fqdn: "node1.example.net"
ansible_domain: "example.net"
ansible_hostname: "node1.example.net"
inventory_hostname: "node1.example.net"
ansible_nodename: "node1"
rancher_image: "rancher:v2.6.6"
rancher_agent_image: "rancher-agent:v2.6.6"
rancher_node_role: server
rancher_k8s_version: "1.20.15"
rancher_setup_password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
rancher_role_flags: "--etcd --controlplane --worker"
rancher_server_url_name: "https://192.168.101.141"
rancher_init_ip: 192.168.101.141
rancher_cluster_name: dev-cluster
ansible_ens18:
device: ens18
ipv4:
address: 192.168.101.141node1.example.net — дуже важливий параметр для встановлення FQDN ноди, на його основі будуть створені коректні
файли /etc/hosts.
Це дасть можливість нодам вільно комунікувати, що є обов'язковими умовами для коректної роботи ролі.
rancher_setup_password - задає пароль для доступу до web-інтерфейсу сервера
ansible_ens18:
device: ens18
ipv4:
address: 192.168.101.141Дуже важливий параметр — опис мережевих інтерфейсів, залежить від ОС, яка використовується на нодах. В Ubuntu
це ens18. На цій основі створюється коректний файл /etc/hosts.
Використовується в шаблоні ansible/roles/rancher-cluster/templates/hosts.j2
{ % for host in groups [ 'kubernetes' ] % }
{ % if 'ansible_ens18' in hostvars[ host ] % }
{ { hostvars[ host ][ "ansible_ens18" ][ "ipv4" ][ "address" ] } } { { hostvars[ host ][ "inventory_hostname" ] } }
{ % endif % }
{ % endfor % }| Name | Version |
|---|---|
| tls | 4.0.4 |
| aws | 4.29.0 |
| kubernetes | 2.16.1 |
| Name | Version |
|---|---|
| aws | 4.29.0 |
| kubernetes | 2.16.1 |
| pgp | 0.2.4 |
| tls | 4.0.4 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| aws_access_key | AWS access key. Використовується як ідентифікатор для з'єднання с AWS API | string |
n/a | yes |
| aws_secret_key | AWS secret key. Використовується для шифрування сесії з'єднання с AWS API | string |
n/a | yes |
| region | AWS регіон де буде розташовано Проєкт | string |
n/a | yes |
| project | Назва Проєкту, яку можна використовувати у складі ресурсів | string |
n/a | yes |
| az_count | Кількість AZ. На основі кількості AZ створюються ресурси VPC | number |
n/a | yes |
| eks_version | Версія Kubernetes для AWS EKS | string |
"1.24" |
no |
| resource_tags | Tag, які будуть використовуватись для всіх ресурсів. Треба вказувати у 'provider { default_tags {} }' | map(string) |
{ |
no |
| public_dns_zone_name | Публічний домен, якій буде використовуватись на Проєкті. До якого спрямовуватись трафік користувачів продукту | string |
n/a | yes |
| public_route53_records | Піддомени та інші ресурси AWS Route53 DNS - CNAME, A, MX, TXT та інші. Але поки без використання Route53 aliases |
list(object({ |
n/a | yes |
| private_dns_zone_name | Приватні DNS домени, які можуть використовуватись для внутрішніх сервісів | list(object({ |
n/a | yes |
| private_route53_records | Піддомени в приватних DNS зонах для внутрішніх сервісів | list(object({ |
n/a | yes |
| vpc_cidr | Блок CIDR Amazon VPC | string |
n/a | yes |
| subnet_cidr_bits | Кількість бітів subnet для VPC Subnets. 8 дає маску /24 | number |
n/a | yes |
| vpc_public_subnets | CIDRs public-підмереж, які будуть доступні із зовні через AWS Internet Gateway | list(string) |
n/a | yes |
| vpc_private_subnets | CIDRs private-підмереж, які можуть бути доступні із зовні через AWS NAT Gateway + AWS Elastic IP | list(string) |
n/a | yes |
| node_group_desired_size | Бажана кількість EC2 нод, на яких буде розгорнуто EKS кластер | number |
n/a | yes |
| node_group_max_size | Максимальна кількість EC2 нод, на яких буде розгорнуто EKS кластер | number |
n/a | yes |
| node_group_min_size | Мінімальна кількість EC2 нод, на яких буде розгорнуто EKS кластер | number |
n/a | yes |
| node_instance_type | EC2 тип нод | string |
n/a | yes |
| node_instance_disk_size | Розмір жорсткого диску EC2 ноди | number |
n/a | yes |
| Name | Description |
|---|---|
| region | AWS region, в якому встановлюється інфраструктура |
| cluster_name | Назва EKS кластера. Визначається за допомогою змінної var.project |
| cluster_endpoint | Endpoint для з`еднання з EKS control plane, який буде використовуватись для інструментів розробників та адміністраторів - kubectl, lens, тощо` |
| cluster_ca_certificate | TLS сертифікат, який використовується як credentials для EKS control plane |
| oidc_provider_arn | Унікальне им`я ресурсу Amazon ARN(Amazon Resource Name) провайдера OIDC (OpenID). Це допомагає Kubernetes Services кластера EKS взаємодіяти з API AWS` |
| aws_nat_gateway_public_ip | Зовнішня IP-адреса, яку використовує Public NAT Gateway. Він же Elastic IP |
Документация створюється шляхом об`єднання :
- файлів *.md з каталогу
./docs/, які створюються самостійно - файлів *.md з каталогу
./ansible/roles/rancher-cluster/docs/doc, які створюються і самостійно, і автоматично за допомогоюansible-autodoc - файлу Readme.md з каталогу
./terraform/doc/, якій створюється автоматично за допомогоюterraform-docs
за допомогою утіліти merge-markdown
Для автоматичного створення використовується bash скріпт ./tools/makedocs.sh
Конфигураційні файли утіліт та документация:
ansible-autodoc — ./ansible/autodoc.conf.yaml. Документація по роботі
terraform-docs — ./terraform/.terraform-docs.yml. Документація по роботі
- merge-markdown —
./docs/makedocs.ymlДокументація по роботі
Всі зображення та схеми потрібно зберігати в каталозі ./img




