
Наряду с развитием искусственного интеллекта, облачные вычисления названы основополагающей технологией будущего. Наверное, многие слышали, как важны и ценны знания Docker и Kubernetes в современном мире ИТ. Но как подступиться к этим технологиям, если ранее с ними вы не сталкивались?
Я всегда считал, что серьезные вещи начинаются с малого, но это малое должно быть самой сутью интересующего феномена. Предлагаю разобраться, как работает полнофункциональное веб-приложение, реализующее современный стек технологий и, конечно же, развёрнутое в кластере Kubernetes. Сделав первый шаг, вы всегда сможете продолжить свой путь исследования, заниматься самообразованием и отладкой разнообразных приложений, реализующих различные подходы, в том числе и самые современные.
Создаем свою домашнюю DevOps‑«песочницу»
ИТ-технологии развиваются крайне стремительно. Уверен, что вне зависимости от того, с чем связана ваша трудовая деятельность, необходимо самостоятельно исследовать появляющиеся технологии, в том числе и дома. Эта небольшая статья может быть полезной для тех, кто желает сделать первый шаг в этом направлении.
Когда мы говорим про домашний ПК, то чаще всего представляем себе обычный компьютер или ноутбук с операционной системой Windows. Например, такой:

«Ничего примечательного!» — скажете вы. Обычный Lenovo G510 образца 2013 года с процессором устаревшей архитектуры Haswell и 12 Гб оперативной памяти, да еще и на Windows 10. Но даже такая конфигурация позволяет запустить кластер Kubernetes (minikube) и работать с ним! Благо дело, в Windows 10 и выше появилась так называемая «Встроенная подсистема для Linux» («Windows Subsystem for Linux»), которая позволяет запустить самый настоящий Ubuntu Linux на вашем компьютере под управлением ОС Windows.
? Самый надёжный способ активировать WSL — это перейти в «Панель управления»→«Программы и компоненты»→«Включение или отключение компонентов Windows» и убедиться, что пункт «Подсистема Linux для Windows» активирована.

Кроме того, может потребоваться установка пакета WSL Ubuntu, который традиционно доступен вот по этой ссылке.
Для версий Windows старше Windows 10 2004 достаточно выполнить в "Командной строке" или PowerShell:
wsl --install
Эта команда автоматически включит необходимые компоненты, установит ядро WSL и дистрибутив Ubuntu. После установки рекомендуется установить для WSL версию 2 по умолчанию, выполнив команду
wsl --set-default-version 2
WSL 2 работает значительно быстрее и имеет полную совместимость с системными вызовами Linux.
Для работы с кодом нам потребуется IDE. В нашем случае я рекомендую Visual Studio Code по той причине, что в этой IDE полностью реализована поддержка WSL и есть возможность работы в терминале. Просто запустите Visual Studio Code и выберите "Terminal"->"bash", как показано на рисунке:

? Поздравляю! Вы в командной строке Linux! Располагайтесь поудобнее и почувствуйте себя настоящим сисадмином!
Примечание. Здесь и далее мы будем работать под системным пользователем "root". Да, это не самая лучшая практика с точки зрения кибербезопасности и она налагает на пользователя повышенную ответственность. Всё дело в том, что команда minikube tunnel, которую мы будем в дальнейшем использовать, требует прав суперпользователя. Но об этом мы поговорим позднее.
Итак, какие же инструменты потребуются для полноценной работы DevOps-"песочницы"? Ниже я приведу таблицу, описывающую список таких инструментов, их назначение и особенности установки.
Название |
Описание |
Установка |
Java 17 (JDK) |
Java Development Kit. Необходим для запуска backend-приложений, написанных на Java |
apt install openjdk-17-jdk |
Docker |
Фреймворк контейнеризации приложений |
apt install -y docker.io usermod -aG docker $USER |
minikube |
"Урезанный" Kubernetes c одной нодой - minikube |
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube |
kubectl |
Утилита для управления Kubernetes из командной строки |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl |
Jenkins |
Сборка проекта, настройка непрерывной интеграции (Cuntinious Integration, CI) |
wget -O /usr/share/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc]" https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null apt update apt install jenkins -y systemctl start jenkins sudo systemctl enable jenkins ufw allow 8080 ufw reload |
Также дополнительно можно установить такие инструменты, как Ansible, Sonatype Nexus, Python 3 - это крайне востребованные фреймворки в DevOps-практике. Но для работы нашего приложения они пока не потребуются.
Если всё сделано верно, то Jenkins начинает запускаться автоматически сразу после старта терминала в Visual Studio Code и ��тановится доступен (в том числе в гостевой ОС Windows) по адресу http://localhost:8080/ .

Согласитесь, достаточно необычно видеть интерфейс 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 |
Получение всех данных таблицы 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 |
Удаление строки таблицы A_DATA с id=6 |
{"response":"200","data":null} |
curl -X POST https://mydataapp.app/api/v1/addrow?data=New+Entry |
Добавление новой строки таблицы A_DATA с текстом "New Entry" |
{"response":"200","data":null} |
Фронтенд
Фронтенд нашего приложения - это микросервис на базе NodeJS/React, который отображает таблицу A_DATA в веб-представлении, а также имеет элементы управления: большая кнопка "+" позволяет добавить новую строку в таблицу; также данные строки могут быть отредактированы inline или удалены специальной кнопкой.

Фронтенд взаимодействует с бэкендом при помощи API, описанного выше.
Под капотом
Бэкенд
Код приложений (frontend и backend) загружен на GitHub и доступен для самостоятельного изучения. Но всё же давайте рассмотрим общие механизмы построения этих приложений. Начнем с бэкенда. Его развертывание в контейнер Docker может быть осуществлено следующим образом:docker build --no-cache -t postgres-java-app_app:latest .
Опция --no-cache тут задана, чтобы гарантированно пересобрать новый образ docker для бэкенда. Что же представляет собой Docker-файл бэкенда? Давайте рассмотрим его подробнее.
# 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. Давайте посмотрим, из каких зависимостей он составлен. Этот 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 для сборки.
Особый интерес также представляет собой файл application.properties, в котором описаны настройки базы данных 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.
Фронтенд
Dockerfile фронтенда выглядит следующим образом:
# 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. Это типичный package.json для React-приложения, созданного с помощью Create React App (CRA).
Приложение очень простое и читается практически без комментариев. Работа с API реализована в App.js.
В мире Kubernetes
Прежде чем начать рассказывать о том, как задеплоить получившееся приложение в k8s (в нашем случае minikube), мне бы хотелось вернуться к нашему Jenkins'у. Благодаря этому оркестратору CI/CD был написан Jenkinsfile, регламентирующий процесс развертывания. Давайте рассмотрим его подробнее и изучим, какие команды выполняют стадии этого Jenkinsfile.
Команда |
Назначение |
git url: 'https://github.com/romo0208/mydataapp.git', 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} |
Применение манифестов 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. Все они разворачиваются в неймспейсе mydataapp.
Давайте попробуем разобраться в их структуре. Для бэкенда мы имеем:
- deployment. Он имеет один контейнер backend с меткой (label) backend, где указан образ микросервиса и порт 8085. Из конфигмапы backend-configmap.yaml берутся данные для настройки SpringBoot-приложения. Вместе с деплойментом указан и сервис (Service), по которому будет осуществляться сетевое взаимодействие.
Деплоймент для фронтенда выглядит проще: тут, кроме указания метки и имени образа, задан адрес бэкенда и, конечно же, сервис для сетевого взаимодействия.
Для простоты отладки readiness и liveness-пробы в манифестах деплойментов закомментированы.
Микросервис с postgresql выполнен в виде отдельного под'а с собственным деплойментом. Здесь используется стандартный образ postgres:13 и еще два дополнительных манифеста:
? postgres-pvc.yaml - PersistentVolumeClaim (PVC) — это манифест Kubernetes, который запрашивает ресурсы хранилища у кластера. Если проводить аналогию, то PVC — это "заявка на выделение диска" для нашего приложения.
? postgres-secret.yaml - secret-манифест для PostgreSQL. Заданные там значения - это base64-закодированные строки "myuser", "mypassword" и "mydatabase" соответственно.
Отдельного внимания заслуживает манифест для ingress приложения:
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, имея лишь разные порты: 80 для фронтенда и 8085 для бэкенда.
Minikube Dashboard
Minikube c полной уверенностью можно назвать полноценным Kubernetes лишь с одной оговоркой: у Minikube всего одна нода с именем minikube. Но даже веб-интерфейс у minikube есть и выглядит он практически идентично с теми элементами управления, что предоставляет, например, OpenShift. Вот, посмотрите, как выглядят поды нашего приложения в Minikube Dashboard:


Кстати, тут тоже есть возможность быстро редактировать yaml-манифесты прямо через веб-интерфейс:

Запускается эта красота очень просто - при помощи команды
minikube dashboard

Проброс трафика в гостевую ОС
Конечно, пощупать приложение можно и внутри WSL, используя команду curl в командной строке, но гораздо интереснее увидеть веб-интерфейс нашего приложения в браузере, который в нашей конфигурации есть лишь в гостевой ОС Windows. Первым делом нужно внести в файл %SystemRoot%\System32\drivers\etc\hosts следующую строку:127.0.0.1 mydataapp.app
Далее в командной строке WSL Ubuntu выполняем следующее:
minikube tunnel

Мы видим, что ingress mydataapp-ingress (mydataapp.app) запускается создания туннеля, пробрасывающего трафик в гостевую ОС 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 приложения:
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
? На этом я прощаюсь с вами! Всем удачи!
Комментарии (2)

WortexLinux
09.11.2025 15:35Я бы советовал кубспрей/вагрант. И получается многонодовая система, аналог небольшого продакшена. А ставить это можно и на убунту, которую ставим на флэшку и не трогаем винду. Сам периодически использую такой стенд на ноуте 8cpu/16gb, вполне хватает на 5 небольших нод. Нагрузочное конечно на таком не погоняешь, но как быстрый стенд вполне стандартного k8s очень удобно. Образа можно один раз скачать и разворачиваться с них.
ruslan_astratov
Спасибо! Было очень интересно и полезно!