Не DevOps-гайд, не «как установить кластер». Именно то, с чем ты столкнёшься как разработчик когда твой сервис едет в K8s.
🔹 Что куда деплоится
Твой jar живёт в Docker-образе. Образ запускается как Pod — минимальная единица в K8s. Pod эфемерен: упал, рестартовал, переехал на другую ноду — это нормально, это не баг.
Нода — это физическая или виртуальная машина в кластере.
Кластер состоит из нескольких нод, на каждой из них K8s запускает поды. Когда нода падает или уходит на обслуживание — K8s автоматически перезапускает её поды на других нодах.
Как разработчику тебе не нужно знать на какой ноде живёт твой под, K8s сам решает это на основе requests ресурсов.
Чтобы управлять несколькими копиями пода используют Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
spec:
containers:
- name: order-service
image: order-service:1.4.2
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
requests — сколько K8s резервирует для пода при планировании. limits — жёсткий потолок. Превысил memory limit — OOMKill. Превысил CPU limit — троттлинг.
Это напрямую влияет на JVM: именно от limits.memory нужно считать -XX:MaxRAMPercentage.
🔹 Как сервисы находят друг друга
Pod умирает и поднимается с новым IP. Поэтому обращаться к поду напрямую нельзя. Service — стабильный DNS-адрес и балансировщик поверх набора подов.
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 8080
Внутри кластера твой payment-service обращается к order-service просто по имени
K8s DNS резолвит это в нужный ClusterIP. Никаких Eureka, никакого Consul — для базового service discovery это не нужно.
🔹 K8s не знает что твой сервис готов
K8s видит что контейнер запустился. Но не знает что Spring контекст поднялся, Liquibase отработал и сервис готов принимать трафик.
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
Readiness — сервис готов принимать трафик. Пока не прошёл, pod исключается из балансировки Service.
Liveness — сервис живой. Не прошёл несколько раз подряд, pod рестартует. Сюда не стоит вешать проверку зависимостей (БД, кафка) — если БД легла, рестарт сервиса не поможет.
Spring Boot Actuator отдаёт оба эндпоинта из коробки при
management.endpoint.health.probes.enabled=true.🔹 ConfigMap и Secret
Хардкодить урлы и пароли в application.properties внутри образа — плохая идея. Образ должен быть одинаковым для dev, staging и prod.
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
data:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orders
SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092
---
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
data:
SPRING_DATASOURCE_PASSWORD: cGFzc3dvcmQ= # base64
В Deployment пробрасываешь как env-переменные — Spring Boot подхватывает автоматически через @Value или application.properties биндинг.
Kubernetes не требует от разработчика знания всего кластерного стека. Но понимать как твой сервис деплоится, как K8s решает готов ли он к трафику и как корректно завершает работу — это уже твоя зона ответственности, не DevOps.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava