DevOps для новачків: базові знання та практики
Усі статті, обговорення, новини про DevOps — в одному місці. Підписуйтеся на DOU | DevOps!
Усім привіт, мене звати Михайло Хоменко, я DevOps Engineer в P2H, і у цьому матеріалі вирішив поділитися основами про DevOps для тих, хто хоче розібратись в темі та зрозуміти, що це, які основні концепції напряму та чим він може бути корисним.
Компанії постійно шукають способи підвищення ефективності роботи та зниження витрат. Саме для досягнення цих цілей і застосовується DevOps — методологія, яка дозволяє забезпечити швидкість та якість розробки програмного забезпечення.
Ця стаття не відповість на всі ваші питання, а лише допоможе поверхнево розібратись в темі та, можливо, викличе ще більше питань, які надалі допоможуть перейти від теорії DevOps до практики.
Інша ціль — звернути увагу розробників, QA та інших причетних до IT-сфери спеціалістів на Docker та можливості його застосування у вашій роботі. І після цього чудового дисклеймеру, почнімо з бази.
Що таке DevOps
DevOps — спосіб розробки та впровадження програмного забезпечення, який поєднує розробку (Development) та експлуатацію (Operations). Він охоплює не тільки розробку, але і тестування, розгортання, моніторинг та підтримку програмного забезпечення.
DevOps — це поступова зміна підходу до розробки, при чому не тільки в інструментах, але й в головах людей — менеджерів, аналітиків, розробників, QA, інфракоманди та клієнтів. Саме тому кажуть, що DevOps — це не просто набір практик, а певна культура.
Для чого потрібен DevOps
Основна мета DevOps — це поєднання розробки програмного забезпечення та його ефективної і надійної доставки до користувачів. Для досягнення цієї мети DevOps вирішує наступні задачі:
- Автоматизація процесів: DevOps намагається автоматизувати якомога більше процесів в загальному процесі розробки програмного забезпечення, щоб зменшити час, витрачений на рутинні задачі, та зменшити кількість помилок, що зумовлені людським фактором.
- Керування конфігурацією: DevOps використовує системи контролю версій, щоб зберігати історію змін коду, що дозволяє відстежувати зміни та відновлювати попередні версії коду в разі потреби.
- Тестування: DevOps забезпечує регулярне тестування програмного забезпечення на різних етапах розробки, щоб виявляти та виправляти помилки на ранніх етапах та забезпечувати високу якість продукту.
- Моніторинг: DevOps дозволяє забезпечити моніторинг програмного забезпечення на різних етапах розробки та після випуску, щоб виявляти та виправляти проблеми з доступністю, продуктивністю та безпекою продукту.
- Культура співпраці: DevOps намагається побудувати культуру співпраці між розробниками, тестувальниками та адміністраторами систем. Це дозволяє зменшити час на виправлення помилок, покращити комунікацію та забезпечити ефективнішу роботу всієї команди.
З чого почати: контейнери та Docker
Задля практики DevOps рекомендується звернути увагу на застосування контейнерів.
Контейнери — це зручний інструмент для розгортання та запуску програмного забезпечення на будь-якому пристрої або в будь-якому середовищі. Контейнери забезпечують відокремлення застосунків від операційної системи та залежностей, що дозволяє ефективніше керувати застосунками, зменшити час розгортання та забезпечити незалежність від середовища.
Це те, що допоможе командам почати використовувати єдині (типові) інструменти та підходи для злагодження роботи.
Використання контейнерів — це далеко не новий підхід. І якщо ви почнете гуглити щось про це, то дізнаєтесь, що найпопулярнішою імплементацією контейнерів є Docker. Наступним вашим кроком буде розібратись, що ж таке Docker, як ним користуватись і чому варто використовувати саме контейнери.
Контейнери VS Віртуальні машини
Контейнери та віртуальні машини (VM) є технологіями віртуалізації, які дозволяють запускати застосунки відокремлено від інших застосунків та операційної системи хост-системи. Однак, між ними є деякі відмінності, які і роблять контейнери наступним кроком після використання віртуальних машин.
- Віртуальна машина потребує більшого обсягу ресурсів, оскільки кожна VM має власну копію операційної системи, тоді як контейнери використовують спільну операційну систему з хост-системою.
- Контейнери є менш ізольованими, ніж віртуальні машини, тому вони можуть бути менш безпечними в разі, якщо в хост-системі є якісь проблеми з безпекою. Іншими словами, якщо хост-система скомпрометована, то, можливо, що й контейнери також можуть бути скомпрометовані.
- Контейнери легше та швидше створювати та розгортати, оскільки вони містять тільки застосунок та його залежності, тоді як віртуальна машина має містити повну операційну систему.
- Контейнери зазвичай мають меншу вагу та займають менше місця на диску, ніж віртуальні машини.
- Контейнери мають меншу накладну витрату на систему, що дозволяє їх використовувати для швидшої розробки, тестування та розгортання застосунків.
Гуглимо далі і натрапляємо на ще більше технічних деталей по Docker:
Саме в цьому місці багато людей вирішують, що ніякий Docker їм не треба і повертаються до свого старого доброго локального вебсервера. І це — найбільша проблема, яка призводить до:
- Збереження підходу «в мене локально все працює, а ви розбирайтесь» і відсутності підходу, коли і у вас, і на проді використовується єдиний інструмент.
- Якщо вам треба запустити локально декілька сайтів з різними версіями PHP, MySQL, то версії треба міняти для всіх, і неможливо запустити ці сайти окремо з різними характеристиками.
- Як мені запустити мій застосунок на іншій машині або на сервері? Або в іншого розробника, з яким я хочу поділитись? Починаються незручності.
Хороша новина в тому, що вам, щоб почати використовувати Docker, не треба знати, як саме він влаштований. Достатньо освоїти елементарні операції, а далі, якщо захочеться, розбиратись глибше.
Друга хороша новина полягає в тому, що для запуску Docker на Windows багато зусиль не треба. Достатньо завантажити Docker Desktop і під час інсталяції обрати опцію використання WSL2, що дозволяє запускати нативні лінуксові застосунки на Windows.
Як приклад, запустімо локальний вебсервер nginx. Для цього нам потрібно описати Docker Desktop, що саме ми хочемо запустити. Ми створюємо папку, наприклад, «nginx», і в ній файл з назвою «compose-dev.yaml» з наступним вмістом:
version: '3' services: nginx: image: nginx ports: - 8080:80
Під директивою services ми перераховуємо контейнери, які ми хочемо запустити. Їхні імена можуть бути будь-якими, але логічно давати сервісам імена згідно з їхнім призначенням. У цьому випадку у нас є тільки один контейнер — nginx. Цей контейнер повинен бути запущений з образу nginx (image: nginx). У загальному випадку, назва образу вказується у вигляді repository/image:tag, де repository — це місце, з якого Docker завантажує образ, а tag — це, іншими словами, версія образу.
Якщо repository і tag пропущені, це означає, що образ буде взятий з останньої версії та завантажений з DockerHub, найпопулярнішого місця зберігання образів. Згадаємо тут ще одну директиву — ports. Вона дозволяє звертатися до портів контейнерів (які запускаються у власній мережі) через порти на локальній машині (localhost). Зліва — порт на локальній машині, справа — порт контейнера. Таким чином, за адресою localhost:8080 відповість процес контейнера, який слухає на порту 80, тобто вебсервер.
Директив у файлі може бути ще багато, з повним списком можна ознайомитися на Compose file version 3 reference.
Такий файл — це все, що нам потрібно, щоб запустити nginx. Відкриваємо Docker Desktop, вибираємо Dev Environments та створюємо нове середовище.
В «Choose source» виберіть «Local directory» та вкажіть директорію, в якій було створено файл «compose-dev.yaml».
Docker Desktop завантажить останній образ nginx з DockerHub (або знайде образ на вашому комп’ютері, якщо він був завантажений раніше) та запустить контейнер на його основі.
Натиснувши на запущений контейнер, можна буде побачити його логи. Вони оновлюються в режимі реального часу, тому якщо ми спробуємо, наприклад, відкрити неіснуючий шлях (localhost:8080/nosuchpath, цей факт одразу буде відображений в лозі nginx:
До речі, коли контейнери працюють вже не на машині розробника, а в робочому середовищі, на демо, на проді, їхні логи зазвичай збираються іншими контейнерами та відправляються до спеціальних баз даних, що дає можливість перегляду, пошуку та сповіщення на основі певних повідомлень в логах.
Тепер спробуємо запустити трохи складніший приклад. З метою спрощення поточне Dev Environment видалимо та створимо нове на основі іншого файлу «compose-dev.yaml»:
version: '3' services: wordpress: image: wordpress restart: always depends_on: - mysql ports: - 8080:80 environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_USER: exampleuser WORDPRESS_DB_PASSWORD: examplepass WORDPRESS_DB_NAME: exampledb volumes: - wordpress:/var/www/html mysql: image: mysql restart: always environment: MYSQL_DATABASE: exampledb MYSQL_USER: exampleuser MYSQL_PASSWORD: examplepass MYSQL_ROOT_PASSWORD: '1' volumes: - db:/var/lib/mysql volumes: wordpress: db:
Цей файл взятий з DockerHub WordPress і трохи змінений: ROOT_PASSWORD = 1 і використовується остання версія MySQL.
Зберігати логіни та паролі у тексті, звичайно, не рекомендується. Зазвичай їх зберігають у спеціальних місцях для зберігання секретів (наприклад, змінні Gitlab CI/CD, Hashicorp Vault тощо) та використовують під час деплою. Принаймні, секрети повинні бути винесені в окремий файл, що ми й покажемо далі, але наразі залишимо так.
Сам файл порівняно з першим має декілька ускладнень:
- додана директива depends_on, щоб запустити WordPress після MySQL. При цьому Docker не буде чекати, доки MySQL повністю запуститься. Просто спочатку запуститься один контейнер, потім інший;
- креди MySQL root та ще одного користувача, так само як і база даних, задана у вигляді environment variables. Зрозуміло, що однакові бази даних, імена користувачів та паролі повинні бути задані для обох контейнерів, тільки назви змінних відрізняються. Використовувані змінні описані в DockerHub WordPress;
- додані volumes. Вони дають можливість зберігати деякі файли поза контейнером, просто мапити їх туди. Без них всі зміни всередині контейнерів будуть зникати. Так влаштовані контейнери — вони за замовчуванням не зберігають зроблені в них зміни. А тут ми кажемо, що все, що змінюється в файлах WordPress та базі MySQL під час роботи контейнерів, буде зберігатися й після їх зупинки/ рестарту.
Важливий момент — шлях до mysql-хосту вказаний як WORDPRESS_DB_HOST=mysql.
mysql — це ім’я сервісу mysql у рамках файлу compose-dev.yaml. Назвали б сервіс db, треба було б і WORDPRESS_DB_HOST, і в depends_on поміняти на db.
Тепер запустимо Dev Environment на основі вже нового файлу, відкриємо у браузері localhost:8080 та побачимо вікно ініціалізації Wordpress:
Проініціалізуємо Wordpress, ввівши потрібні дані, зокрема логін та пароль для адмінки.
Змінивши що-небудь у розділі Pages, і після цього рестартувавши контейнер з Dev Environments, можна переконатися, що внесені зміни зберігаються.
Ще трохи ускладнимо приклад: винесемо всі креди в окремий файл і додамо контейнер з phpMyAdmin. Декілька Dev Environments можуть жити поруч один з одним, проте порт 8080 може бути зайнятий тільки одним з них, для інших потрібно порт localhost змінювати.
Для простоти ми видалимо поточний Environment та створимо новий, використовуючи наступну конфігурацію compose-dev.yaml:
version: '3' services: wordpress: image: wordpress restart: always depends_on: - mysql ports: - 8080:80 env_file: - local.env volumes: - wordpress:/var/www/html mysql: image: mysql restart: always env_file: - local.env volumes: - db:/var/lib/mysql phpmyadmin: image: phpmyadmin restart: always depends_on: - mysql ports: - 8081:80 env_file: - local.env volumes: db: wordpress:
Бачимо, що додався контейнер phpMyAdmin і змінні оточення читаються з файлу local.env такого змісту:
WORDPRESS_DB_HOST=mysql WORDPRESS_DB_USER=exampleuser WORDPRESS_DB_PASSWORD=examplepass WORDPRESS_DB_NAME=exampledb MYSQL_DATABASE=exampledb MYSQL_USER=exampleuser MYSQL_PASSWORD=examplepass MYSQL_ROOT_PASSWORD='1' PMA_HOST=mysql PMA_USER=root PMA_PASSWORD='1'
Нагадаємо, що зберігати такий файл у репозиторії не потрібно, його навіть варто додати до .gitignore. Тоді локально він читатиметься, а при деплої, наприклад, буде формуватися автоматично з секретів Gitlab.
Після запуску Dev Environments та ініціалізації Wordpress за адресою localhost:8080, у phpMyAdmin консолі за адресою localhost:8081 можна побачити базу та таблиці Wordpress:
У прикладах вище ми використовували дефолтний Wordpress, доступний з коробки (з образу), але хотілося б мати свій кастомізований, та ще й мати можливість його змінювати.
Завантажимо Wordpress з сайту. Створимо окрему папку, скажімо, demo-wordpress. Розпакуємо в нього архів із Wordpress. Поміняємо щось усередині папки wordpress/, припустимо, з wordpress/wp-content/plugins приберемо дефолтні плагіни та покладемо плагін classic-editor.
compose-dev.yaml будемо використовувати той самий, тільки з двома невеликими змінами:
- заберемо використання volumes з wordpress-container, прибравши секцію volumes;
- як образ для wordpress вкажемо свій образ, mywordpress:v1.
version: '3' services: wordpress: image: mywordpress:v1 restart: always depends_on: - mysql ports: - 8080:80 env_file: - local.env mysql: image: mysql …
Чому саме mywordpress:v1 і чим він відрізняється від звичайного образу wordpress? Ім’я у нього справді довільне, як і версія. Бажано тільки, щоб ім’я образу якось відповідало застосунку у ньому. А ось чим відрізняється від стандартного — так це тим, що веб-каталог /var/www/html у ньому замінений нашим, з кастомізованим вище плагіном.
І як нам такий образ отримати у результаті? Просто. Створюємо файлик з ім’ям Dockerfile:
FROM wordpress COPY ./wordpress /var/www/html
Загадкові слова FROM і COPY за змістом зрозумілі — отримуємо наш образ з образу wordpress шляхом копіювання нашої локальної папки wordpress/ в образ /var/www/html.
Переконайтеся, що вміст папки demo-wordpress виглядає так:
wordpress/ compose-dev.yaml Dockerfile local.env
Описавши, що треба зробити, власне, робимо (крапка в кінці — must have).
$ docker build -t mywordpress:v1 .
Тепер, якщо ви створите новий Dev Environment, вже використовуватиметься створений вами wordpress-образ.
Добре, ми навчилися створювати свої образи локально, але було б добре, якби цей же образ був доступний і на демо, стейдж, та прод оточеннях. Іншими словами, нам треба зробити так, щоб при пуші змін у репозиторій образ, який ми зібрали локально командою docker build, так само збирався б і віддалено, під час процедури складання та деплою на сервері.
Припустимо, наш репозиторій зберігається на Gitlab. Тоді, щоб пояснити Gitlab, що потрібно зробити після пуша в репозиторій, потрібно створити YAML-файл з ім’ям .gitlab-ci.yml і наступною структурою:
stages: # Етапи - stage 1 - stage 2 # Задачі, які треба зробити на кожному етапі Job 1: stage: stage 1 script: - bash command - … Job 2: stage: stage 2 script: - bash command - …
Якщо дуже коротко, оголошуємо етапи складання у розділі stages і завдання (jobs) кожному з етапів. Конкретні кроки кожного із завдань описуються у секціях scripts.
Простий приклад .gitlab-ci.yml:
stages: - hello Say hello: stage: hello script: - echo Hello from pipeline
Після пушу змін до репозиторію, пайплайн привітається з нами :)
Ми не розглядатимемо пристрій CI/CD, ранери та інше цього разу, тільки продемонструємо ще й процес складання образу, про який говорилося вище.
Робочий файл .gitlab-ci.yml вже для складання виглядає так:
image: docker services: - docker:dind variables: IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA stages: - build Build Docker image: stage: build script: - docker build -t $IMAGE . - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin - docker push $IMAGE
Без деталей тут запускається (внизу) команда docker build, яка збирає образ і потім пушить його в Gitlab-repository, звідки він може бути взятий вже для подальшого деплою.
На жаль, обсяг статті не дозволяє описати тут і деплой. Інші нові елементи (image, services, variables) потрібні, щоб була можливість зібрати образ.
Сподіваюсь, цей невеличкий гайд з базовою інформацією буде корисним для вас і ви прояснили для себе, що таке DevOps та чому це важливо для компаній. Також чудово, якщо ви хоч трохи зацікавились використанням Docker та контейнерів, адже це не тільки може допомогти працювати із загальним єдиним інструментом, але й підвищить якість комунікації між розробниками, QA та адміністраторами.
Кожна підписка на наш DevOps Podcast поліпшує настрій всій команді. Підписуйтесь :)
15 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів