Production-like учебно-портфельный проект по эксплуатации, доставке, наблюдаемости и восстановлению готового Java microservice.
Цель проекта — получить практический опыт полного жизненного цикла Java-приложения в production-like инфраструктуре:
source code
↓
GitHub
↓
CI
↓
Docker image
↓
Docker Hub
↓
CD
↓
Kubernetes / kind
↓
Java service
↓
PostgreSQL + S3/MinIO
↓
metrics + logs + health
↓
Prometheus + Loki + Grafana
↓
incident investigation
↓
backup / restore / rollback
Java-разработка не является целью проекта.
Java microservice используется как готовый объект эксплуатации.
Основной навык, который должен демонстрировать проект:
Умение доставить, развернуть, наблюдать, диагностировать, обновить и восстановить Java-приложение в production-like инфраструктуре.
В качестве application workload используется готовый Java/Spring Boot сервис CloudShare.
Сервис предоставляет функциональность защищённого обмена файлами.
Основная модель данных:
Document
│
├── metadata ──────► PostgreSQL
│
└── content ───────► S3 API / MinIO
Таким образом PostgreSQL и object storage решают разные задачи.
PostgreSQL является authoritative store для metadata/application state.
MinIO предоставляет S3-compatible object storage для содержимого файлов.
Приложение не должно хранить содержимое файлов непосредственно в PostgreSQL.
После завершения проекта необходимо уметь продемонстрировать:
- создание Linux infrastructure environment;
- автоматическое provisioning инфраструктуры;
- configuration management;
- создание и публикацию Docker image;
- CI через GitHub Actions;
- CD через GitHub Actions;
- получение image из Docker Hub;
- deployment Java application в Kubernetes;
- управление deployment через Helm;
- rollback неудачного release;
- подключение Java к PostgreSQL;
- базовое PostgreSQL administration;
- PostgreSQL backup;
- PostgreSQL restore;
- подключение Java к S3-compatible storage;
- использование MinIO;
- Kubernetes health probes;
- application metrics;
- Kubernetes metrics;
- centralized logs;
- Grafana dashboards;
- investigation реального incident;
- диагностику проблем через metrics + logs + health;
- восстановление сервиса после отказа;
- безопасное управление credentials;
- reproducible deployment.
Каждая технология должна существовать только потому, что решает конкретную задачу.
Нельзя добавлять технологию только ради демонстрации знания инструмента.
Основные связи:
Terraform
↓
Провайдер guidoiaquinti/lima для Terraform
↓
Lima VM
↓
Ansible
↓
Linux host
↓
self-hosted GitHub Actions runner
↓
kind Kubernetes cluster
↓
Helm
↓
Java application
├──────────────► PostgreSQL
│
└──────────────► S3 API
↓
MinIO
Java
├── health ────────► Kubernetes probes
├── metrics ───────► Prometheus
└── logs ──────────► log collector
↓
Loki
↓
MinIO
Prometheus ──────► Grafana
Loki ────────────► Grafana
External client
↓
Nginx
↓
Kubernetes Service
↓
Java application
Terraform отвечает только за создание инфраструктурной boundary.
В нашем случае:
Terraform
↓
Lima
↓
Linux VM
Terraform не должен управлять Java application release.
Terraform не должен заменять Helm.
Terraform не должен заменять Ansible.
Ansible приводит созданную Lima VM в необходимое состояние.
Он устанавливает и конфигурирует:
- Docker;
- kubectl;
- kind;
- Helm;
- GitHub Actions self-hosted runner;
- Nginx;
- необходимые системные пакеты;
- каталоги;
- пользователей;
- permissions;
- базовую конфигурацию Linux.
Модель:
Terraform:
"Создай мне машину"
Ansible:
"Сделай эту машину пригодной для эксплуатации"
Lima предоставляет Linux environment для всей локальной production-like лаборатории.
Целевая структура:
Developer machine
│
▼
Lima
│
▼
Linux VM
│
├── Docker
├── kind
├── kubectl
├── Helm
├── Nginx
└── GitHub Actions runner
Lima используется намеренно:
- Kubernetes запускается в Linux environment;
- Ansible работает с реальным Linux host;
- deployment target отделён от основной developer environment;
- infrastructure можно уничтожить и создать заново.
GitHub является центральной точкой source control и automation.
Repository содержит:
application reference
infrastructure
Ansible
Terraform
Helm
Kubernetes configuration
monitoring
scripts
documentation
runbooks
GitHub Actions
Работа с application должна идти через Git:
branch
↓
commit
↓
pull request
↓
CI
↓
merge
↓
release
GitHub Actions отвечает за CI/CD.
Pipeline должен иметь минимум две логические части.
checkout
↓
validate
↓
test
↓
integration test
↓
security scan
↓
Docker build
↓
image validation
↓
push image
new image
↓
deploy to kind
↓
Helm upgrade
↓
wait for rollout
↓
health verification
↓
smoke test
Deployment должен выполняться self-hosted runner, находящимся в Lima VM.
Причина:
GitHub-hosted runner
X
│
│ no direct access
▼
local kind cluster
self-hosted runner
│
▼
Lima VM
│
▼
kind
Runner является частью deployment environment.
Он устанавливается и конфигурируется Ansible.
Модель:
GitHub
│
│ Actions job
▼
Self-hosted runner
│
├── docker
├── kubectl
└── helm
│
▼
kind
Repository должен быть private во время разработки этого варианта deployment architecture.
Необходимо отдельно документировать security implications self-hosted runners.
Docker используется для создания переносимого application artifact.
Java application должна быть упакована в versioned image:
document-service:1.0.0
document-service:1.1.0
document-service:1.2.0
latest не используется как основной deployment identifier.
Deployment должен использовать конкретный version/tag.
Docker Hub является image registry.
Pipeline:
GitHub
↓
GitHub Actions
↓
Docker build
↓
Docker image
↓
Docker Hub
↓
Kubernetes imagePull
Kubernetes не должен зависеть от локального image, созданного на developer machine.
Это необходимо для демонстрации реального artifact delivery lifecycle.
Используется однонодовый kind cluster.
Это осознанное ограничение локальной лаборатории.
Проект НЕ заявляет production-grade Kubernetes HA.
Он демонстрирует:
- Kubernetes workload management;
- deployment;
- service discovery;
- health probes;
- resource limits;
- configuration;
- secrets;
- rollout;
- rollback;
- logs;
- metrics.
Базовые Kubernetes resources:
Namespace
Deployment
Service
ConfigMap
Secret
Ingress / edge integration
PersistentVolumeClaim
Stateful infrastructure не должна автоматически переноситься в Kubernetes только ради демонстрации StatefulSet.
Helm является application release layer.
Структура:
helm/
└── cloudshare/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
└── templates/
├── deployment.yaml
├── service.yaml
├── configmap.yaml
├── secret.yaml
├── ingress.yaml
└── servicemonitor.yaml
Helm отвечает за:
- application configuration;
- image version;
- resource configuration;
- probes;
- environment variables;
- Kubernetes resources;
- release lifecycle.
Необходимо продемонстрировать:
helm install
helm upgrade
helm history
helm rollback
Nginx является edge reverse proxy.
Он не является заменой Kubernetes Service.
Traffic:
Client
↓
Nginx
↓
Kubernetes Service
↓
Java Pod
Nginx отвечает за:
- внешний HTTP endpoint;
- reverse proxy;
- access logs;
- upstream error handling;
- optional TLS termination.
Пример логических endpoints:
api.lab.local
grafana.lab.local
Java application является главным workload проекта.
Приложение не изменяется без необходимости.
Мы не изучаем:
- Spring Boot development;
- Java design patterns;
- application architecture;
- Java coding.
Мы изучаем эксплуатацию уже существующего сервиса.
REST API является способом взаимодействия с workload.
Минимальные операции:
POST /api/v1/files
GET /api/v1/files
GET /api/v1/files/{id}
DELETE /api/v1/files/{id}
POST /api/v1/shares
API используется не только для демонстрации.
Через него выполняются:
- smoke tests;
- health verification;
- incident reproduction;
- performance checks;
- deployment verification.
Пример:
deployment
↓
curl API
↓
HTTP 200/expected response
↓
deployment successful
PostgreSQL хранит:
- file metadata;
- users/application state;
- sharing metadata;
- audit information;
- relational state.
Java dependency:
Java
│
│ JDBC
▼
PostgreSQL
Необходимо отработать базовое administration:
database creation
user creation
roles
permissions
connections
active sessions
locks
database size
basic configuration
logs
Backup является частью production lifecycle.
Минимальный сценарий:
PostgreSQL
↓
pg_dump
↓
backup file
↓
MinIO
↓
postgres-backups bucket
Backup должен содержать:
- timestamp;
- application/database identifier;
- version/format information.
Необходимо проверить не только создание backup, но и его пригодность для восстановления.
Обязательный scenario:
working database
↓
backup
↓
data loss / simulated failure
↓
restore
↓
PostgreSQL
↓
application verification
После restore необходимо проверить application-level behavior через REST API.
Недостаточно получить:
pg_restore completed successfully
Нужно доказать:
database restored
↓
Java reconnects
↓
REST API works
↓
expected document metadata exists
MinIO используется как S3-compatible object storage.
Основной application flow:
POST /files
↓
Java
├── metadata → PostgreSQL
│
└── content → S3 API → MinIO
Buckets:
documents
postgres-backups
loki
Каждый bucket имеет отдельную ответственность.
Java application не должна быть концептуально привязана к MinIO.
Она работает с S3-compatible API.
Поэтому:
Application
↓
S3 API
↓
MinIO
MinIO является текущей implementation.
В дальнейшем backend theoretically можно заменить на AWS S3 без изменения бизнес-модели приложения.
Необходимо продемонстрировать:
upload
↓
object exists
download
↓
object returned
delete
↓
object removed
И проверить обе стороны:
PostgreSQL metadata
+
MinIO object
Например, нельзя считать удаление успешным, если:
PostgreSQL metadata deleted
BUT
MinIO object remains
Это должно стать отдельным troubleshooting scenario.
Java Actuator предоставляет application health.
Kubernetes использует:
startupProbe
readinessProbe
livenessProbe
Модель:
startup
↓
application initialization
readiness
↓
can receive traffic?
liveness
↓
is application process alive?
Readiness и liveness не должны бездумно означать одно и то же.
Особенно важно не превращать временный отказ PostgreSQL в бесконтрольный restart всех Java pods.
Application metrics:
HTTP request count
HTTP error count
HTTP latency
JVM memory
JVM CPU
GC
threads
Infrastructure/Kubernetes metrics:
pod CPU
pod memory
restarts
container status
PostgreSQL metrics:
connections
database size
transactions
locks
Pipeline:
Java
↓
Micrometer
↓
Prometheus
↓
Grafana
Prometheus является metrics backend.
Он должен собирать:
- Java application metrics;
- Kubernetes metrics;
- relevant infrastructure metrics;
- PostgreSQL metrics.
Необходимо создать alert rules минимум для:
application unavailable
high HTTP error rate
high latency
pod restart
high memory usage
database unavailable
Application logs должны идти через container stdout/stderr.
Не нужно писать application logs непосредственно в файлы внутри container.
Pipeline:
Java
↓
stdout/stderr
↓
container runtime
↓
log collector
↓
Loki
Structured JSON logging должен сохранять:
timestamp
level
logger
message
trace/request identifier
service
environment
Loki является centralized log backend.
Он хранит application/container logs.
Object storage:
Loki
↓
S3-compatible storage
↓
MinIO
↓
loki bucket
Это создаёт вторую важную связь MinIO:
MinIO
├── documents
├── postgres-backups
└── loki
Grafana является общей operational UI.
Она должна показывать:
request rate
error rate
latency
JVM memory
GC
threads
pod status
CPU
memory
restarts
deployment status
connections
database size
transactions
locks
Через Loki:
application errors
stack traces
request identifiers
deployment events
Grafana должна использоваться не только как красивый dashboard.
Основной workflow:
Alert
↓
Metrics
↓
Identify affected service
↓
Logs
↓
Identify error
↓
Health checks
↓
Confirm dependency
↓
Fix / rollback / restore
↓
Verify metrics
↓
Verify API
Pipeline считается готовым, если:
git push
↓
CI passes
↓
Docker image created
↓
image pushed to Docker Hub
↓
CD starts
↓
Helm upgrade
↓
Kubernetes rollout
↓
readiness successful
↓
REST smoke test successful
Pipeline должен завершаться ошибкой, если:
- tests fail;
- image build fails;
- security gate fails;
- deployment fails;
- readiness does not become healthy;
- smoke test fails.
После каждого deployment необходимо автоматически проверить:
deployment exists
pods ready
service exists
health endpoint
REST endpoint
database connectivity
object storage connectivity
Минимальный smoke test:
health
↓
create/upload
↓
list
↓
download
↓
delete
Нужно иметь две версии:
v1.0.0
v1.1.0
v1.1.0 должен быть намеренно сломан в controlled scenario.
После deployment:
v1.1.0
↓
failure
необходимо выполнить:
helm rollback
и доказать:
previous release
↓
healthy
↓
REST API works
Итоговая структура repository:
java-production-operations-lab/
│
├── README.md
│
├── docs/
│ ├── architecture.md
│ ├── decisions.md
│ ├── deployment.md
│ ├── operations.md
│ ├── troubleshooting.md
│ ├── backup-restore.md
│ ├── security.md
│ └── incidents/
│ ├── broken-release.md
│ ├── postgres-failure.md
│ ├── minio-failure.md
│ ├── bad-configuration.md
│ └── resource-pressure.md
│
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ ├── outputs.tf
│ └── versions.tf
│
├── ansible/
│ ├── inventory/
│ ├── playbooks/
│ │ ├── site.yml
│ │ ├── docker.yml
│ │ ├── kubernetes.yml
│ │ ├── runner.yml
│ │ └── nginx.yml
│ │
│ └── roles/
│ ├── docker/
│ ├── kind/
│ ├── helm/
│ ├── runner/
│ └── nginx/
│
├── kind/
│ └── cluster.yaml
│
├── helm/
│ └── cloudshare/
│ ├── Chart.yaml
│ ├── values.yaml
│ ├── values-dev.yaml
│ └── templates/
│
├── kubernetes/
│ └── namespace.yaml
│
├── postgres/
│ ├── scripts/
│ │ ├── backup.sh
│ │ ├── restore.sh
│ │ └── verify-restore.sh
│ └── README.md
│
├── minio/
│ ├── buckets/
│ └── README.md
│
├── monitoring/
│ ├── prometheus/
│ │ ├── rules/
│ │ └── dashboards/
│ │
│ ├── loki/
│ ├── alloy/
│ └── grafana/
│ └── dashboards/
│
├── nginx/
│ └── conf.d/
│
├── scripts/
│ ├── bootstrap.sh
│ ├── deploy.sh
│ ├── smoke-test.sh
│ ├── backup.sh
│ ├── restore.sh
│ └── destroy.sh
│
└── .github/
└── workflows/
├── ci.yml
├── docker.yml
└── cd.yml
Локальная лаборатория должна иметь минимум:
developer machine
│
▼
Lima VM
│
├── kind
│
├── Nginx
│
└── self-hosted runner
Внутри Kubernetes:
namespace: cloudshare
Java
Prometheus
Grafana
Loki
Alloy
Stateful dependencies:
PostgreSQL
MinIO
Их размещение должно быть зафиксировано в architecture documentation и не меняться случайно между этапами проекта.
Никогда не хранить реальные credentials в Git.
Минимальные secrets:
POSTGRES_USERNAME
POSTGRES_PASSWORD
S3_ACCESS_KEY
S3_SECRET_KEY
DOCKERHUB_USERNAME
DOCKERHUB_TOKEN
GITHUB_RUNNER_TOKEN
Для Kubernetes application secrets должны передаваться через Kubernetes Secret/Helm values mechanism.
Для GitHub Actions credentials должны находиться в GitHub Secrets.
Application image version:
MAJOR.MINOR.PATCH
Пример:
1.0.0
1.1.0
1.1.1
Git tag:
v1.0.0
Docker tag:
1.0.0
Helm release revision:
1
2
3
Эти три понятия должны быть различимы в документации.
Проект считается завершённым только если выполнены все условия.
- Lima VM создаётся автоматически.
- Infrastructure configuration находится в Terraform.
- Linux host конфигурируется Ansible.
- Все необходимые инструменты устанавливаются автоматически.
- GitHub Actions запускается на Pull Request.
- Tests выполняются автоматически.
- Integration tests выполняются автоматически.
- Security check выполняется автоматически.
- Docker image создаётся автоматически.
- Image публикуется в Docker Hub.
- Deployment выполняется автоматически.
- Kubernetes получает image из Docker Hub.
- Helm управляет release.
- Rollout проверяется.
- REST smoke test выполняется после deployment.
- Неудачный deployment приводит к failed pipeline.
- REST API работает.
- Java подключается к PostgreSQL.
- Java использует S3 API.
- Файл можно загрузить.
- Файл можно получить.
- Файл можно удалить.
- Metadata хранится в PostgreSQL.
- Content хранится в MinIO.
- Создан application user.
- Настроены permissions.
- Выполняется backup.
- Backup сохраняется в MinIO.
- Выполняется restore.
- Restore проверяется через application API.
- Deployment работает.
- Service работает.
- ConfigMap используется.
- Secret используется.
- Startup probe настроена.
- Readiness probe настроена.
- Liveness probe настроена.
- Resources requests/limits настроены.
- Rollout работает.
- Rollback протестирован.
- Java metrics видны в Prometheus.
- Kubernetes metrics видны.
- Application logs собираются.
- Logs доступны через Loki.
- Loki использует MinIO object storage.
- Grafana отображает metrics.
- Grafana позволяет искать logs.
- Есть alerts для основных отказов.
- PostgreSQL failure воспроизведён.
- MinIO failure воспроизведён.
- Broken deployment воспроизведён.
- Bad configuration воспроизведён.
- Resource pressure воспроизведён.
- PostgreSQL restore выполнен.
- Application rollback выполнен.
В repository должны быть не только YAML-файлы.
Необходимо сохранить доказательства эксплуатации.
Например:
docs/evidence/
├── ci-success.png
├── docker-image.png
├── kubernetes-rollout.png
├── helm-history.png
├── helm-rollback.png
├── postgres-backup.png
├── postgres-restore.png
├── minio-buckets.png
├── prometheus.png
├── grafana.png
├── loki.png
└── incident-recovery.png
Каждый screenshot должен сопровождаться коротким описанием:
Problem
Action
Expected result
Actual result
Evidence
Каждый operational scenario должен иметь runbook.
Шаблон:
# Incident: PostgreSQL unavailable
## Symptoms
What the operator observes.
## Impact
What functionality is affected.
## Detection
Which metric / alert / log detects the problem.
## Investigation
Commands and dashboards to inspect.
## Root cause
What caused the failure.
## Recovery
Exact recovery procedure.
## Verification
How to prove that service is healthy again.
## Prevention
What can be changed to reduce recurrence.
Главная демонстрация проекта должна выглядеть так:
Developer changes application version
│
▼
GitHub
│
▼
GitHub Actions CI
│
┌─────┼─────┐
│ │ │
test scan build
│ │ │
└─────┼─────┘
│
▼
Docker image
│
▼
Docker Hub
│
▼
Self-hosted runner
│
▼
Helm
│
▼
Kubernetes
│
▼
Java service
/ \
/ \
▼ ▼
PostgreSQL MinIO
│ │
│ └── documents
│
└── backups ─────► MinIO
Java
├── metrics ───────► Prometheus
├── logs ──────────► Alloy → Loki → MinIO
└── health ────────► Kubernetes
Prometheus ─────────► Grafana
Loki ───────────────► Grafana
Client ─────────────► Nginx ─────► Java
Затем намеренно ломается release:
bad release
↓
Kubernetes rollout
↓
readiness failure
↓
Prometheus alert
↓
Grafana
↓
Loki
↓
root cause
↓
Helm rollback
↓
REST smoke test
↓
service recovered
После этого моделируется потеря данных:
PostgreSQL
↓
data loss
↓
backup from MinIO
↓
restore
↓
Java reconnect
↓
REST verification
Это и есть главный результат проекта.
Этот проект демонстрирует не знание отдельных инструментов.
Он демонстрирует способность построить и эксплуатировать связанный delivery/runtime environment:
Infrastructure
↓
Configuration
↓
CI
↓
Artifact
↓
CD
↓
Runtime
↓
Dependencies
↓
Observability
↓
Incident response
↓
Recovery
Каждый слой зависит от предыдущего и предоставляет основу следующему.
Главная цель:
Take a ready-made Java service from source control to a reproducible production-like environment, operate it under normal and failure conditions, observe it, diagnose incidents, roll back releases, and recover persistent data.
Если компонент нельзя объяснить фразой:
«Без этого компонента конкретная эксплуатационная задача проекта не решается»
— компонент не добавляется.
Это правило распространяется на все будущие технологии.
Не добавлять:
Redis
Kafka
Argo CD
Istio
Terraform Cloud
AWS
EKS
Service Mesh
Vault
Keycloak
только ради расширения списка технологий.
Новая технология появляется только после формулировки:
Operational problem
↓
Why existing stack cannot solve it
↓
New technology
↓
Concrete responsibility
↓
Failure scenario
↓
Operational evidence
Именно этот принцип должен сохраняться на протяжении всего проекта.