- BrainTools - https://www.braintools.ru -
Наряду с развитием искусственного интеллекта [1], облачные вычисления названы основополагающей технологией будущего. Наверное, многие слышали, как важны и ценны знания Docker и Kubernetes в современном мире ИТ. Но как подступиться к этим технологиям, если ранее с ними вы не сталкивались?
Я всегда считал, что серьезные вещи начинаются с малого, но это малое должно быть самой сутью интересующего феномена. Предлагаю разобраться, как работает полнофункциональное веб-приложение, реализующее современный стек технологий и, конечно же, развёрнутое в кластере Kubernetes. Сделав первый шаг, вы всегда сможете продолжить свой путь исследования, заниматься самообразованием и отладкой разнообразных приложений, реализующих различные подходы, в том числе и самые современные.
ИТ-технологии развиваются крайне стремительно. Уверен, что вне зависимости от того, с чем связана ваша трудовая деятельность, необходимо самостоятельно исследовать появляющиеся технологии, в том числе и дома. Эта небольшая статья может быть полезной для тех, кто желает сделать первый шаг в этом направлении.
Когда мы говорим про домашний ПК, то чаще всего представляем себе обычный компьютер или ноутбук с операционной системой Windows. Например, такой:
«Ничего примечательного!» — скажете вы. Обычный Lenovo G510 образца 2013 года с процессором устаревшей архитектуры Haswell и 12 Гб оперативной памяти [2], да еще и на Windows 10. Но даже такая конфигурация позволяет запустить кластер Kubernetes (minikube) и работать с ним! Благо дело, в Windows 10 и выше появилась так называемая «Встроенная подсистема для Linux» («Windows Subsystem for Linux»), которая позволяет запустить самый настоящий Ubuntu Linux на вашем компьютере под управлением ОС Windows.
💡 Самый надёжный способ активировать WSL — это перейти в «Панель управления»→«Программы и компоненты»→«Включение или отключение компонентов Windows» и убедиться, что пункт «Подсистема Linux для Windows» активирована.
Кроме того, может потребоваться установка пакета WSL Ubuntu, который традиционно доступен вот по этой ссылке [3].
Для версий Windows старше Windows 10 2004 достаточно выполнить в “Командной строке” или PowerShell:
wsl --install
Эта команда автоматически включит необходимые компоненты, установит ядро WSL и дистрибутив Ubuntu. После установки рекомендуется установить для WSL версию 2 по умолчанию, выполнив команду
wsl --set-default-version 2
WSL 2 работает значительно быстрее и имеет полную совместимость с системными вызовами Linux.
Для работы с кодом нам потребуется IDE. В нашем случае я рекомендую Visual Studio Code [4] по той причине, что в этой IDE полностью реализована поддержка WSL и есть возможность работы в терминале. Просто запустите Visual Studio Code и выберите “Terminal”->”bash”, как показано на рисунке:
🔥 Поздравляю! Вы в командной строке Linux! Располагайтесь поудобнее и почувствуйте себя настоящим сисадмином!
Примечание. Здесь и далее мы будем работать под системным пользователем “root”. Да, это не самая лучшая практика с точки зрения [5] кибербезопасности и она налагает на пользователя повышенную ответственность. Всё дело в том, что команда minikube tunnel, которую мы будем в дальнейшем использовать, требует прав суперпользователя. Но об этом мы поговорим позднее.
Итак, какие же инструменты потребуются для полноценной работы DevOps-“песочницы”? Ниже я приведу таблицу, описывающую список таких инструментов, их назначение и особенности установки.
|
Название |
Описание |
Установка |
|
Java 17 (JDK) |
Java Development Kit. Необходим для запуска backend-приложений, написанных на Java |
apt install openjdk-17-jdk |
|
Docker |
Фреймворк контейнеризации приложений |
apt install -y docker.io [6] usermod -aG docker $USER |
|
minikube |
“Урезанный” Kubernetes c одной нодой – minikube |
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 [7] |
|
kubectl |
Утилита для управления Kubernetes из командной строки |
curl -LO “https://dl.k8s.io/release/$(curl [8] -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl [9]” |
|
Jenkins |
Сборка проекта, настройка непрерывной интеграции (Cuntinious Integration, CI) |
wget -O /usr/share/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key [10] echo “deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc]” https://pkg.jenkins.io/debian-stable [11] binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null apt update systemctl start jenkins sudo systemctl enable jenkins ufw allow 8080 |
Также дополнительно можно установить такие инструменты, как Ansible, Sonatype Nexus, Python 3 – это крайне востребованные фреймворки в DevOps-практике. Но для работы нашего приложения они пока не потребуются.
Если всё сделано верно, то Jenkins начинает запускаться автоматически сразу после старта терминала в Visual Studio Code и становится доступен (в том числе в гостевой ОС Windows) по адресу http://localhost:8080/ [12] .

Согласитесь, достаточно необычно видеть интерфейс Jenkins на домашнем компьютере. Но, надо сказать, работает он быстро и в такой конфигурации. Задаем пароль администратора Jenkins и создаем там свой первый проект!
Давайте проверим, что и остальные инструменты работают нормально. Ниже приведена таблица, которая показывает, как можно произвести валидацию корректности установленных инструментов.
|
Название |
Команда |
|
Java 17 (JDK) |
java -version |
|
Docker |
docker images |
|
minikube |
minikube start –driver=docker –force |
|
kubectl |
kubectl get pods -n default |
Давайте посмотрим, как система отреагирует на запуск каждой из этих команд:
java -version
docker images
minikube start --driver=docker --force
|
root@ROMAN:/mnt/c/dev/a_data# minikube start –driver=docker –force |
kubectl get pods -n default
Ну, вот и всё! Наша DevOps-“песочница” настроена и готова к использованию! Давайте теперь попробуем изучить и запустить наше приложение!
Давайте построим самое простое, но полезное приложение – записную книжку. Бэкенд данного приложения должен представлять собой SpringBoot-совместимый микросервис, обменивающийся с фронтендом сообщениями в формате Json. Бэкенд должен использовать базу данных Postgres, и для простоты мы создадим всего одну таблицу A_DATA с двумя атрибутами: первичным ключом и атрибутом для хранения данных:
CREATE TABLE IF NOT EXISTS A_DATA (
id SERIAL PRIMARY KEY,
a_data VARCHAR(2000) NOT NULL
);
Бэкенд должен поддерживать API по добавлению, удалению и чтению кортежей из таблицы базы данных, как представлено в таблице ниже:
|
API |
Описание |
Пример ответа |
|
curl -X GET https://mydataapp.app/api/v1/getall [13] |
Получение всех данных таблицы A_DATA в JSON-формате |
{“response”:”200″,”data”:[{“id”:62,”data”:”New Entry”},{“id”:65,”data”:”Test”},{“id”:67,”data”:”Kubernetes управляет миром!”}]} |
|
curl -X DELETE https://mydataapp.app/api/v1/deleterow?id=6 [14] |
Удаление строки таблицы A_DATA с id=6 |
{“response”:”200″,”data”:null} |
|
curl -X POST https://mydataapp.app/api/v1/addrow?data=New+Entry [15] |
Добавление новой строки таблицы A_DATA с текстом “New Entry” |
{“response”:”200″,”data”:null} |
Фронтенд нашего приложения – это микросервис на базе NodeJS/React, который отображает таблицу A_DATA в веб-представлении, а также имеет элементы управления: большая кнопка “+” позволяет добавить новую строку в таблицу; также данные строки могут быть отредактированы inline или удалены специальной кнопкой.
Фронтенд взаимодействует с бэкендом при помощи API, описанного выше.
Код приложений (frontend и backend) загружен на GitHub [16]и доступен для самостоятельного изучения. Но всё же давайте рассмотрим общие механизмы построения этих приложений. Начнем с бэкенда. Его развертывание в контейнер Docker может быть осуществлено следующим образом:
docker build --no-cache -t postgres-java-app_app:latest .
Опция –no-cache тут задана, чтобы гарантированно пересобрать новый образ docker для бэкенда. Что же представляет собой Docker-файл [17] бэкенда? Давайте рассмотрим его подробнее.
# Build stage
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
COPY . .
RUN mvn clean package -DskipTests
# Run stage
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /app/target/demo-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8085
ENTRYPOINT ["java", "-jar", "app.jar"]
В данном Dockerfile используется многоступенчатая сборка (multi-stage build). На первом этапе (build) используется образ Maven с JDK 17 для сборки JAR-файла приложения. На втором этапе используется облегченный образ только с JDK 17, куда копируется собранный JAR-файл из первого этапа. Приложение запускается и становится доступным на порту 8085.
Если сборка осуществляется Maven, то должен быть и pom.xml [18]. Давайте посмотрим, из каких зависимостей он составлен. Этот pom.xml описывает типичное современное Spring Boot приложение с:
👉 REST API (Starter Web)
👉 Работой с БД через JPA/Hibernate (Starter Data JPA)
👉 PostgreSQL как основная СУБД
👉 Миграциями БД через Flyway
👉 Упрощением кода через Lombok
👉 Тестированием через Spring Test
Приложение будет работать на Java 17 с Spring Boot 3.1.0 и использовать Maven для сборки.
Особый интерес [19] также представляет собой файл application.properties [20], в котором описаны настройки базы данных Postgres, а также миграция через FlyWay:
# Server port
server.port=8085
# Database configuration
spring.datasource.url=jdbc:postgresql://postgres:5432/mydatabase
spring.datasource.username=myuser
spring.datasource.password=mypassword
spring.datasource.driver-class-name=org.postgresql.Driver
# Hibernate properties
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# Flyway configuration
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
spring.flyway.baseline-on-migrate=true
server.address=0.0.0.0
Классы SpringBoot-приложения, описывающие работу с API, доступны в пакете com.example.demo [21].
Dockerfile [22]фронтенда выглядит следующим образом:
# Build stage
FROM node:16 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Данный Dockerfile использует многоступенчатую сборку для создания образа фронтенд-приложения. На этапе сборки (build stage) используется образ Node.js 16, где устанавливаются зависимости через npm и выполняется сборка проекта. На финальном этапе (production stage) используется облегченный образ nginx, куда копируются статические файлы из предыдущего этапа и конфигурация nginx. Итоговый образ обслуживает приложение на порту 80.
Зависимости фронтенд-приложения описаны в файле package.json [23]. Это типичный package.json для React-приложения, созданного с помощью Create React App (CRA).
Приложение очень простое и читается практически без комментариев. Работа с API реализована в App.js [24].
Прежде чем начать рассказывать о том, как задеплоить получившееся приложение в k8s (в нашем случае minikube), мне бы хотелось вернуться к нашему Jenkins’у. Благодаря этому оркестратору CI/CD был написан Jenkinsfile [25], регламентирующий процесс развертывания. Давайте рассмотрим его подробнее и изучим, какие команды выполняют стадии этого Jenkinsfile.
|
Команда |
Назначение |
|
git url: ‘https://github.com/romo0208/mydataapp.git [26]‘, branch: ‘master’ |
Клонирование Git-репозитория |
|
|
Копирование целевых файлов в рабочую директорию |
|
nohup minikube start –driver=docker –force > /tmp/minikube.log 2>&1 & |
Запуск minikube с драйвером Docker |
|
minikube image build -t postgres-java-app_app:latest . |
Сборка образа бэкенда внутри minikube docker registry |
|
minikube image load postgres-java-app_app:latest –overwrite=true |
Загрузка образа бэкенда в minikube |
|
minikube image build -t frontend-app_frontend:latest . |
Сборка образа фронтенда внутри minikube docker registry |
|
minikube image load frontend-app_frontend:latest –overwrite=true |
Загрузка образа фронтенда в minikube |
|
kubectl apply -f ${file.name [27]} |
Применение манифестов Kubernetes |
|
nohup minikube dashboard > /tmp/minikube-dashboard.log 2>&1 & |
Запуск minikube dashboard |
Как мы можем видеть, данный Jenkinsfile просто последовательно выполняет команды в Jenkins-джобе типа “Pipeline script from SCM”.
Видно, что теперь мы используем не docker build, а minikube image build, что делает доступными образы наших микросервисов непосредственно во встроенном registry нашего minikube. Особый шаг – это вызов команды kubectl apply, где последовательно применяются манифесты kubernetes [28]. Все они разворачиваются в неймспейсе mydataapp.
Давайте попробуем разобраться в их структуре. Для бэкенда мы имеем:
– deployment [29]. Он имеет один контейнер backend с меткой (label) backend, где указан образ микросервиса и порт 8085. Из конфигмапы backend-configmap.yaml [30] берутся данные для настройки SpringBoot-приложения. Вместе с деплойментом указан и сервис (Service), по которому будет осуществляться сетевое взаимодействие.
Деплоймент для фронтенда выглядит проще [31]: тут, кроме указания метки и имени образа, задан адрес бэкенда и, конечно же, сервис для сетевого взаимодействия.
Для простоты отладки readiness и liveness-пробы в манифестах деплойментов закомментированы.
Микросервис с postgresql выполнен в виде отдельного под’а с собственным деплойментом [32]. Здесь используется стандартный образ postgres:13 и еще два дополнительных манифеста:
🚀 postgres-pvc.yaml [33] – PersistentVolumeClaim (PVC) — это манифест Kubernetes, который запрашивает ресурсы хранилища у кластера. Если проводить аналогию, то PVC — это “заявка на выделение диска” для нашего приложения.
🚀 postgres-secret.yaml [34] – secret-манифест для PostgreSQL. Заданные там значения – это base64-закодированные строки “myuser”, “mypassword” и “mydatabase” соответственно.
Отдельного внимания [35] заслуживает манифест для ingress [36]приложения:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mydataapp-ingress
namespace: mydataapp
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- mydataapp.app
secretName: mydataapp-tls
rules:
- host: mydataapp.app
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8085
Здесь видно, что сервисы приложения будут использовать общий хост mydataapp.app [37], имея лишь разные порты: 80 для фронтенда и 8085 для бэкенда.
Minikube c полной уверенностью можно назвать полноценным Kubernetes лишь с одной оговоркой: у Minikube всего одна нода с именем minikube. Но даже веб-интерфейс у minikube есть и выглядит он практически идентично с теми элементами управления, что предоставляет, например, OpenShift. Вот, посмотрите, как выглядят поды нашего приложения в Minikube Dashboard:
Кстати, тут тоже есть возможность быстро редактировать yaml-манифесты прямо через веб-интерфейс:
Запускается эта красота очень просто – при помощи команды
minikube dashboard
Конечно, пощупать приложение можно и внутри WSL, используя команду curl в командной строке, но гораздо интереснее увидеть веб-интерфейс нашего приложения в браузере, который в нашей конфигурации есть лишь в гостевой ОС Windows. Первым делом нужно внести в файл %SystemRoot%System32driversetchosts следующую строку:
127.0.0.1 mydataapp.app [37]
Далее в командной строке WSL Ubuntu выполняем следующее:
minikube tunnel

Мы видим, что ingress mydataapp-ingress (mydataapp.app [37]) запускается создания туннеля, пробрасывающего трафик в гостевую ОС Windows. Таким образом, обратиться к приложению мы можем при помощи браузера (например, Chrome) при переходе на https://mydataapp.app. Кстати, именно из-за этой операции, требующей повышенных привилегий, все операции проводились под системным пользователем.
Но вот беда: браузеры на основе Chromium больше не поддерживают подключение по http, а работать по https с нашим приложением они тоже отказываются из-за самоподписанного сертификата. Как же быть? На самом деле, выход есть. Нам поможет утилита mkcert, помогающая выпустить сертификат доверия.
Выпускаем сертификат и устанавливаем его по следующей инструкции:
sudo apt install libnss3-tools
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
# Установить CA в систему
mkcert -install
# Создать сертификат для домена
mkcert mydataapp.app
# Создать Secret в Kubernetes
kubectl create secret tls mydataapp-tls --cert=mydataapp.app.pem --key=mydataapp.app-key.pem -n mydataapp
Созданный tls secret прописывается в манифесте Ingress [36]приложения:
tls:
- hosts:
- mydataapp.app
secretName: mydataapp-tls
Кроме того, полученный корневой сертификат необходимо скопировать в Windows и добавить в список доверенных. Делается это при помощи оснастки “Сертификаты” “Консоли управления (MMC)”.
После всех манипуляций наше приложение наконец-то стало доступным в ОС WIndows! Давайте посмотрим на него подробнее!
Нажмем F12 в Chrome, чтобы увидеть “Инструменты разработчика” и обновим страницу. Видно, что фронтенд обратился к бэкенду, вызвав метод API “getall”:
Давайте теперь добавим новую запись:
Аналогичным образом можно понаблюдать, как выполняются операции редактирования записей и удаления.
🎯 Поздравляю! Вот мы и написали и отладили простейшее микросервисное приложение, развернутое в среде kubernetes с использованием Jenkins и многих современных лучших практик! Надеюсь, что эта небольшая статья поможет вам сделать первые шаги в понимании архитектуры современных веб-приложений и погрузиться в увлекательный мир kubernetes и сопутствующих ему DevOps-практик!
Часто задаваемые вопросы
Как посмотреть логи того или иного пода?
Рассмотрим, например, бэкенд. Получим все его поды:
kubectl get pods -n mydataapp -l app=backend
root@ROMAN:/mnt/c/dev/a_data# kubectl get pods -n mydataapp -l app=backend
NAME READY STATUS RESTARTS AGE
backend-5799b7b9c5-vz9v4 1/1 Running 20 (149m ago) 46d
root@ROMAN:/mnt/c/dev/a_data#
Далее выполняем:
kubectl logs -n mydataapp <имя-пода-бэкенда>, т.е. в нашем случае:
kubectl logs -n mydataapp backend-5799b7b9c5-vz9v4
Как «прогуляться» по файловой системе контейнера того или иного пода?
Давайте посмотрим, какие файлы лежат внутри docker-образа фронтенда. Что там нам NPM насобирал? Поступаем таким образом. Получаем поды фронтенда:
kubectl get pods -n mydataapp -l app=frontend
root@ROMAN:/mnt/c/dev/a_data# kubectl get pods -n mydataapp -l app=frontend
NAME READY STATUS RESTARTS AGE
frontend-75fc9fbdbf-99tkg 1/1 Running 3 (156m ago) 2d4h
root@ROMAN:/mnt/c/dev/a_data#
kubectl exec -it -n mydataapp <имя-пода-фронтенда> — /bin/sh, т.е. в нашем случае:
kubectl exec -it -n mydataapp frontend-75fc9fbdbf-99tkg -- /bin/sh
Как остановить кластер minikube?
minikube stop
Как запустить тестовый под для диагностики?
kubectl run -it --rm curl-test3 --image=curlimages/curl -n mydataapp -- sh
Как перезагрузить под, например, фронтенд?
kubectl rollout restart deployment/frontend -n mydataapp
🎉 На этом я прощаюсь с вами! Всем удачи!
Автор: coratory
Источник [38]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/21681
URLs in this post:
[1] интеллекта: http://www.braintools.ru/article/7605
[2] памяти: http://www.braintools.ru/article/4140
[3] ссылке: https://ubuntu.com/desktop/wsl
[4] Visual Studio Code: https://code.visualstudio.com/download
[5] зрения: http://www.braintools.ru/article/6238
[6] docker.io: http://docker.io
[7] https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64: https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
[8] https://dl.k8s.io/release/$(curl: https://dl.k8s.io/release/%24(curl
[9] https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl: https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl
[10] https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key: https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
[11] https://pkg.jenkins.io/debian-stable: https://pkg.jenkins.io/debian-stable
[12] http://localhost:8080/: http://localhost:8080/
[13] https://mydataapp.app/api/v1/getall: https://mydataapp.app/api/v1/getall
[14] https://mydataapp.app/api/v1/deleterow?id=6: https://mydataapp.app/api/v1/deleterow?id=6
[15] https://mydataapp.app/api/v1/addrow?data=New+Entry: https://mydataapp.app/api/v1/addrow?data=New+Entry
[16] GitHub : https://github.com/romo0208/mydataapp/tree/master/frontend-app
[17] Docker-файл: https://github.com/romo0208/mydataapp/blob/master/postgres-java-app/Dockerfile
[18] pom.xml: https://github.com/romo0208/mydataapp/blob/master/postgres-java-app/pom.xml
[19] интерес: http://www.braintools.ru/article/4220
[20] application.properties: https://github.com/romo0208/mydataapp/blob/master/postgres-java-app/src/main/resources/application.properties
[21] com.example.demo: https://github.com/romo0208/mydataapp/blob/master/postgres-java-app/src/main/java/com/example/demo/
[22] Dockerfile : https://github.com/romo0208/mydataapp/blob/master/frontend-app/Dockerfile
[23] package.json: https://github.com/romo0208/mydataapp/blob/master/frontend-app/package.json
[24] App.js: https://github.com/romo0208/mydataapp/blob/master/frontend-app/src/App.js
[25] Jenkinsfile: https://github.com/romo0208/mydataapp/blob/master/Jenkinsfile
[26] https://github.com/romo0208/mydataapp.git: https://github.com/romo0208/mydataapp.git
[27] file.name: http://file.name
[28] манифесты kubernetes: https://github.com/romo0208/mydataapp/tree/master/k8s
[29] deployment: https://github.com/romo0208/mydataapp/blob/master/k8s/backend-deployment.yaml
[30] backend-configmap.yaml: https://github.com/romo0208/mydataapp/blob/master/k8s/backend-configmap.yaml
[31] Деплоймент для фронтенда выглядит проще: https://github.com/romo0208/mydataapp/blob/master/k8s/frontend-deployment.yaml
[32] деплойментом: https://github.com/romo0208/mydataapp/blob/master/k8s/postgres-deployment.yaml
[33] postgres-pvc.yaml: https://github.com/romo0208/mydataapp/blob/master/k8s/postgres-pvc.yaml
[34] postgres-secret.yaml: https://github.com/romo0208/mydataapp/blob/master/k8s/postgres-secret.yaml
[35] внимания: http://www.braintools.ru/article/7595
[36] ingress : https://github.com/romo0208/mydataapp/blob/master/k8s/mydataapp-ingress.yaml
[37] mydataapp.app: http://mydataapp.app
[38] Источник: https://habr.com/ru/articles/964546/?utm_source=habrahabr&utm_medium=rss&utm_campaign=964546
Нажмите здесь для печати.