Скрипт развертывания приложения в Kubernetes должен принимать параметры среды, собирать Docker-образ, присваивать ему неизменяемый тег, отправлять образ в Container Registry, подготавливать манифесты и выполнять kubectl apply. После этого сценарий обязан дождаться kubectl rollout status и проверить готовность Pod, Service и Ingress, если приложение опубликовано наружу.
Рабочая схема выглядит так: коммит проходит тесты, CI job собирает образ, registry хранит конкретную версию, deploy job выбирает namespace и IMAGE_TAG, Kubernetes создает или обновляет Deployment, ReplicaSet и Pod, а Service направляет трафик по labels. Стратегия RollingUpdate постепенно заменяет Pod, readiness probe не отправляет запросы в неготовый контейнер, liveness probe перезапускает зависший экземпляр.
Сборку запускайте на рабочей машине или CI-сервере. Production-сервер должен получать готовый образ из registry и применять манифесты. Такой подход превращает ручной набор команд в повторяемый процесс с журналом действий, контролем результата и понятным откатом.
Коротко: как работает деплой приложения в Kubernetes
Цепочка операций: от коммита до готового Pod
Сценарий деплоя связывает четыре компонента: Docker создает артефакт, Container Registry хранит его версию, Kubernetes запускает контейнеры, а CI/CD pipeline управляет последовательностью шагов.
- Проверка изменений. Pipeline запускает тесты, линтеры и проверки Dockerfile до публикации образа.
- Сборка. Команда
docker buildсоздает образ приложения из текущей версии исходного кода. - Тегирование. Образу присваивается тег релиза или SHA коммита, например
v2026.09.06илиgit-a13f9c2. - Публикация. Команда
docker pushотправляет образ в registry, доступный узлам Kubernetes. - Выбор среды. Скрипт получает
NAMESPACE,IMAGE_TAG, домен и конфигурацию приложения. - Применение ресурсов.
kubectl apply -fсоздает или обновляет Deployment, Service, ConfigMap и Ingress. - Ожидание обновления.
kubectl rollout statusждет, пока Deployment запустит нужное число готовых Pod. - Smoke-проверка. Проверяются состояние Pod, endpoints Service и доступность HTTP-маршрута через Service или Ingress.
Код возврата kubectl apply подтверждает принятие манифеста API-сервером. Он не подтверждает, что приложение скачало образ, запустилось и отвечает на запросы. Поэтому ожидание rollout и проверка доступности входят в обязательную часть сценария.
Когда скрипта достаточно, а когда нужен CI/CD pipeline
Отдельный Bash-скрипт подходит для локального стенда, K3s, тестового namespace и повторяемых административных операций. Его удобно запускать вручную после сборки образа или использовать как единую команду для небольшой команды.
Для командной разработки и production скрипт лучше запускать внутри CI/CD pipeline. Pipeline добавляет автоматические тесты, журнал выполнения, защищенные секреты, разграничение прав, ручное подтверждение production-релиза и сохранение диагностических данных при ошибке.
Подходы к автоматизации отличаются требованиями к масштабу, контролю изменений и сопровождению. Практическое сравнение shell-скриптов, CI/CD, Docker и Kubernetes приведено в статье о системах развертывания приложений.
Что подготовить перед запуском скрипта
Параметры кластера и целевого окружения
Перед первым запуском зафиксируйте характеристики среды. Одна и та же команда может вести себя по-разному в полноразмерном Kubernetes, K3s, Minikube и managed-кластере.
- Дистрибутив и версию Kubernetes или K3s.
- Операционную систему узлов.
- Архитектуру процессора, например
amd64илиarm64. - Сетевую схему, firewall, доступные порты и правила DNS.
- Ingress-контроллер, например Traefik в типичной установке K3s.
- Внутренний порт приложения, например
8080. - Namespace, в котором будут создаваться ресурсы.
- StorageClass, если приложению нужны PersistentVolume и PersistentVolumeClaim.
- Состав системных компонентов, включая CoreDNS, CNI и Ingress-контроллер.
В учебном сценарии удобно использовать K3s с Traefik. Для внешнего HTTP-доступа понадобятся DNS-запись на адрес кластера и доступные порты 80 или 443. Локальное хранилище K3s имеет ограничения, поэтому stateful-приложения требуют отдельной проверки StorageClass и политики резервного копирования.
Если кластер нужен в облачной инфраструктуре с VDS, хранилищем и Kubernetes, можно рассмотреть Timeweb Cloud. Перед запуском проверьте поддерживаемые версии Kubernetes, сетевые правила, типы дисков и способ выдачи учетных данных.
Проверка kubectl context и прав доступа
Самая опасная ошибка перед деплоем, применение манифестов не в тот кластер. Скрипт должен явно проверять текущий context или получать ожидаемое значение через переменную.
kubectl config current-context
kubectl cluster-info
kubectl get namespace staging
kubectl auth can-i get pods --namespace staging
kubectl auth can-i create deployments --namespace staging
Для production проверяйте сразу три значения: context, namespace и образ. Полезно вывести их в лог до команды kubectl apply. Если context отличается от ожидаемого, сценарий должен завершиться с ошибкой без изменения ресурсов.
Учетная запись deploy job должна иметь минимальные права. Для одного приложения обычно нужны операции с Deployment, Pod, Service, ConfigMap, Ingress и просмотр событий в целевом namespace. Доступ администратора к кластеру для автоматического деплоя повышает риск ошибки.
Доступ к Container Registry
Сначала авторизуйте Docker на машине сборки:
docker login registry.local
В команде используется имя образа с registry и репозиторием, например registry.local/team/catalog. Тег должен однозначно указывать на содержимое образа.
Если registry закрытый, узлы Kubernetes должны получить право скачать образ. Для этого создают Secret типа kubernetes.io/dockerconfigjson и подключают его к Pod через imagePullSecrets. Логины, токены и содержимое kubeconfig нельзя хранить в Bash-файле, YAML или открытом Git-репозитории.
Структура Kubernetes-манифестов для приложения
Минимальный набор ресурсов состоит из Deployment и Service. ConfigMap добавляет конфигурацию приложения, Secret хранит чувствительные значения, а Ingress публикует HTTP или HTTPS-маршруты наружу.
k8s/
app.yaml.tmpl
configmap.yaml
secret.example.yaml
Namespace можно создать заранее отдельной командой или описать отдельным манифестом. Для production чаще используют заранее подготовленный namespace с ограничениями ресурсов, политиками доступа и сетевыми правилами.
Синтаксис apiVersion, kind, metadata и spec разобран с примерами в практическом материале о структуре Kubernetes-манифестов.
Deployment: образ, реплики, ресурсы и стратегия обновления
Deployment описывает желаемое состояние приложения и поддерживает нужное число реплик. При изменении поля spec.template, например образа или переменной окружения, Kubernetes создает новую ревизию ReplicaSet.
| Поле | Назначение | Практический ориентир |
|---|---|---|
replicas | Количество экземпляров приложения | 2 для базового отказоустойчивого сценария |
image | Полное имя Docker-образа и его тег | registry.local/team/app:v2026.09.06 |
selector | Связь Deployment с Pod по labels | Значение совпадает с labels шаблона Pod |
containerPort | Порт процесса внутри контейнера | 8080 |
resources.requests | Ресурсы для планирования Pod | CPU 100m, память 128Mi |
resources.limits | Верхнее ограничение потребления | CPU 500m, память 512Mi |
strategy | Порядок замены Pod | RollingUpdate |
В примере используется RollingUpdate с одной дополнительной репликой и нулевым количеством недоступных Pod. Такая схема снижает риск простоя, если приложение корректно обрабатывает параллельный запуск версий.
Полное руководство по Deployment, probes, requests, limits, rollout и откату доступно в статье об управлении приложениями через Kubernetes Deployment.
Readiness и liveness probes
readinessProbe отвечает на вопрос, может ли Pod принимать трафик. Пока проверка не проходит, Service не направляет запросы в этот Pod.
livenessProbe контролирует жизнеспособность процесса. После нескольких неудачных проверок kubelet перезапускает контейнер.
Для HTTP-приложения можно использовать два endpoint: /ready для готовности и /health для проверки процесса. Пути должны существовать в приложении, а порт probe должен совпадать с портом контейнера или именем порта.
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
Неверный путь, порт или слишком короткая задержка блокируют rollout. Если приложение запускается 40 секунд, значение initialDelaySeconds для liveness должно учитывать это время.
Service и Ingress
Service предоставляет стабильное имя и виртуальный адрес внутри кластера. Он выбирает Pod по selector, поэтому labels Deployment и selector Service должны совпадать.
Ingress нужен при внешнем HTTP или HTTPS-доступе. В K3s запрос принимает Traefik, выбирает правило по домену и передает трафик в Service. Для внутреннего API без публичного адреса достаточно Service.
apiVersion: v1
kind: Service
metadata:
name: ${APP_NAME}
namespace: ${NAMESPACE}
labels:
app: ${APP_NAME}
spec:
selector:
app: ${APP_NAME}
ports:
- name: http
port: 80
targetPort: http
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${APP_NAME}
namespace: ${NAMESPACE}
labels:
app: ${APP_NAME}
spec:
ingressClassName: traefik
rules:
- host: ${DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ${APP_NAME}
port:
number: 80
Для такого Ingress DNS-имя должно указывать на адрес, где Traefik принимает запросы. Если DNS или firewall не настроены, исправный Pod все равно не будет доступен извне.
Рабочий скрипт развертывания приложения в Kubernetes
Входные переменные и предварительная валидация
Скрипт получает параметры через переменные окружения. Режим set -Eeuo pipefail останавливает выполнение при ошибке команды, обращении к незаданной переменной или сбое в конвейере.
Ниже приведен пример, который собирает образ, проверяет context, публикует образ, подставляет переменные в шаблон и ждет завершения rollout.
#!/usr/bin/env bash
set -Eeuo pipefail
: "${NAMESPACE:?NAMESPACE is required}"
: "${IMAGE_TAG:?IMAGE_TAG is required}"
: "${IMAGE_REPOSITORY:?IMAGE_REPOSITORY is required}"
: "${APP_NAME:?APP_NAME is required}"
: "${CONTAINER_PORT:=8080}"
: "${DOMAIN:=app.example.internal}"
: "${EXPECTED_CONTEXT:?EXPECTED_CONTEXT is required}"
command -v docker >/dev/null
command -v kubectl >/dev/null
CURRENT_CONTEXT=$(kubectl config current-context)
if [ "$CURRENT_CONTEXT" != "$EXPECTED_CONTEXT" ]; then
echo "Unexpected Kubernetes context: $CURRENT_CONTEXT" >&2
exit 1
fi
kubectl get namespace "$NAMESPACE" >/dev/null
kubectl auth can-i create deployments --namespace "$NAMESPACE" >/dev/null
kubectl auth can-i get pods --namespace "$NAMESPACE" >/dev/null
if [ "$NAMESPACE" = production ]; then
if [ "$IMAGE_TAG" = latest ]; then
echo "latest is forbidden in production" >&2
exit 1
fi
echo "Production deploy: context=$CURRENT_CONTEXT namespace=$NAMESPACE image=$IMAGE_REPOSITORY:$IMAGE_TAG"
read -r -p "Type production to continue: " CONFIRM
if [ "$CONFIRM" != production ]; then
echo "Deployment cancelled"
exit 1
fi
fi
IMAGE="$IMAGE_REPOSITORY:$IMAGE_TAG"
MANIFEST_FILE=$(mktemp)
trap 'rm -f "$MANIFEST_FILE"' EXIT
echo "Building $IMAGE"
docker build --pull --tag "$IMAGE" .
echo "Pushing $IMAGE"
docker push "$IMAGE"
export NAMESPACE APP_NAME IMAGE CONTAINER_PORT DOMAIN
envsubst '${NAMESPACE} ${APP_NAME} ${IMAGE} ${CONTAINER_PORT} ${DOMAIN}' < k8s/app.yaml.tmpl > "$MANIFEST_FILE"
echo "Applying Kubernetes manifests"
kubectl apply --filename "$MANIFEST_FILE"
echo "Waiting for rollout"
kubectl rollout status "deployment/$APP_NAME" --namespace "$NAMESPACE" --timeout=180s
echo "Checking resources"
kubectl get deployment "$APP_NAME" --namespace "$NAMESPACE"
kubectl get pods --namespace "$NAMESPACE" --selector "app=$APP_NAME" -o wide
kubectl get service "$APP_NAME" --namespace "$NAMESPACE"
if kubectl get ingress "$APP_NAME" --namespace "$NAMESPACE"; then
true
fi
Проверка существования namespace защищает от случайного создания ресурсов в новом пространстве имен из-за опечатки. Если namespace нужно создавать автоматически, добавьте отдельный разрешенный шаг с проверкой допустимого списка значений.
Сборка и публикация Docker-образа
Переменная IMAGE_REPOSITORY хранит имя registry и репозитория без тега:
IMAGE_REPOSITORY=registry.local/team/catalog
IMAGE_TAG=git-a13f9c2
Скрипт собирает итоговый образ с именем registry.local/team/catalog:git-a13f9c2 и отправляет его через docker push. Тег на основе SHA коммита связывает образ с исходным кодом. Релизный тег вроде v2026.09.06 удобен для ручного поиска и журналов.
Повторная сборка на production-сервере нарушает разделение build и deploy. В таком сценарии результат может зависеть от локального Docker-кэша, версии инструментов и состояния исходного каталога. Production получает готовый артефакт из registry.
Подстановка параметров и применение манифестов
Шаблон можно обработать через envsubst. Файлы не приходится копировать для staging и production, меняются только переменные окружения и значения конфигурации.
Файл k8s/app.yaml.tmpl может содержать такие ресурсы:
apiVersion: v1
kind: ConfigMap
metadata:
name: ${APP_NAME}-config
namespace: ${NAMESPACE}
data:
LOG_LEVEL: info
FEATURE_MODE: standard
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${APP_NAME}
namespace: ${NAMESPACE}
labels:
app: ${APP_NAME}
spec:
replicas: 2
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: ${APP_NAME}
template:
metadata:
labels:
app: ${APP_NAME}
spec:
containers:
- name: ${APP_NAME}
image: ${IMAGE}
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: ${CONTAINER_PORT}
envFrom:
- configMapRef:
name: ${APP_NAME}-config
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: ${APP_NAME}
namespace: ${NAMESPACE}
labels:
app: ${APP_NAME}
spec:
selector:
app: ${APP_NAME}
ports:
- name: http
port: 80
targetPort: http
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ${APP_NAME}
namespace: ${NAMESPACE}
spec:
ingressClassName: traefik
rules:
- host: ${DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ${APP_NAME}
port:
number: 80
Перед применением проверьте получившийся YAML:
envsubst '${NAMESPACE} ${APP_NAME} ${IMAGE} ${CONTAINER_PORT} ${DOMAIN}' < k8s/app.yaml.tmpl > /tmp/app.yaml
kubectl apply --dry-run=server --filename /tmp/app.yaml
kubectl apply --filename /tmp/app.yaml
Проверка --dry-run=server отправляет манифест API-серверу без изменения ресурсов. Она помогает обнаружить неверный apiVersion, запрещенное поле или ошибку схемы до настоящего применения.
Ожидание rollout и базовая smoke-проверка
После kubectl apply дождитесь завершения обновления с ограничением времени:
kubectl rollout status deployment/catalog --namespace staging --timeout=180s
kubectl get pods --namespace staging --selector app=catalog
kubectl get service catalog --namespace staging
kubectl get ingress catalog --namespace staging
Успешный rollout означает, что Deployment получил готовые Pod согласно заданной стратегии. Для проверки функции приложения выполните HTTP-запрос из временного Pod внутри кластера или отправьте запрос через внешний Ingress.
kubectl run smoke-client --rm --stdin --tty --restart=Never --image=curlimages/curl -- curl --fail --silent http://catalog/ready
Команда использует внутреннее имя Service. Если запрос завершается ошибкой, сравните состояние Pod, endpoints Service и логи приложения. Для внешнего теста проверьте домен, правила Traefik и доступность порта.
Параметризация скрипта для staging и production
Namespace и тег образа как обязательные параметры
Namespace отделяет ресурсы разных сред и снижает вероятность смешения конфигураций. Один и тот же сценарий можно запустить с разными значениями:
NAMESPACE=staging \
IMAGE_TAG=git-a13f9c2 \
IMAGE_REPOSITORY=registry.local/team/catalog \
APP_NAME=catalog \
CONTAINER_PORT=8080 \
DOMAIN=catalog.staging.example.internal \
EXPECTED_CONTEXT=cluster-staging \
./deploy.sh
NAMESPACE=production \
IMAGE_TAG=v2026.09.06 \
IMAGE_REPOSITORY=registry.local/team/catalog \
APP_NAME=catalog \
CONTAINER_PORT=8080 \
DOMAIN=catalog.example.internal \
EXPECTED_CONTEXT=cluster-production \
./deploy.sh
Для production используйте явный релизный тег или SHA коммита. Тег latest оставьте для локальных экспериментов, где воспроизводимость релиза не требуется.
Конфигурация приложения через ConfigMap и Secret
Параметры, не содержащие секретов, передаются через ConfigMap. В примере это уровень логирования и режим функций:
apiVersion: v1
kind: ConfigMap
metadata:
name: catalog-config
namespace: staging
data:
LOG_LEVEL: info
FEATURE_MODE: standard
Пароли, токены, ключи и строки подключения передаются через Secret или внешний менеджер секретов. Значения можно хранить в защищенных переменных CI/CD и создавать Secret во время deploy job. Поле data использует base64-кодирование, которое не шифрует содержимое, поэтому открытый Secret в Git не защищает учетные данные.
envFrom:
- configMapRef:
name: catalog-config
- secretRef:
name: catalog-secrets
Обычные настройки можно менять без пересборки образа. После изменения ConfigMap или Secret проверьте, требуется ли приложению перезапуск Pod для чтения новых значений.
Защита production от случайного запуска
Скрипт должен выводить перед запуском итоговые значения context, namespace, image repository и image tag. Для production добавьте подтверждение, запрет latest и проверку допустимого списка кластеров.
- Разделите учетные данные staging и production.
- Ограничьте production deploy отдельной CI/CD job.
- Добавьте ручное подтверждение или правило защищенной ветки.
- Запретите пустой, изменяемый или неизвестный тег.
- Проверяйте, что context соответствует целевому окружению.
- Используйте разные домены, лимиты ресурсов и ConfigMap для сред.
Один скрипт отвечает за последовательность команд. Значения среды хранятся отдельно, поэтому исправление логики деплоя не требует синхронного редактирования нескольких копий файла.
Как встроить деплой в CI/CD pipeline
Разделение pipeline на build и deploy
Pipeline удобно разделить на последовательные jobs:
- Test. Проверка кода, зависимостей, Dockerfile и Kubernetes-шаблонов.
- Build. Сборка образа с тегом SHA коммита или версии релиза.
- Push. Публикация образа в Container Registry.
- Deploy staging. Запуск единого скрипта с namespace staging.
- Smoke. Проверка rollout, Pod и HTTP-ответа.
- Approve. Ручное или защищенное разрешение на production.
- Deploy production. Развертывание того же тега в production.
- Verify. Проверка состояния, доступности и событий Kubernetes.
Build job создает артефакт. Deploy job использует его, но не пересобирает приложение. Это сохраняет связь между образом, тестами и production-релизом.
Минимальный вызов deploy job выглядит так:
export NAMESPACE=staging
export IMAGE_TAG="$CI_COMMIT_SHA"
export IMAGE_REPOSITORY=registry.local/team/catalog
export APP_NAME=catalog
export EXPECTED_CONTEXT=cluster-staging
./deploy.sh
Переменные и секреты CI/CD
В защищенном хранилище CI/CD размещают токены registry, kubeconfig или краткоживущие учетные данные, а также значения конфигурации конкретной среды. Секреты не должны попадать в вывод команд, артефакты job и Git-репозиторий.
REGISTRY_USERиREGISTRY_TOKENнужны build job.KUBECONFIGили временный токен нужен deploy job.EXPECTED_CONTEXTзащищает от обращения к чужому кластеру.PRODUCTION_DOMAIN, лимиты ресурсов и имена Secret хранятся отдельно для каждой среды.
Разрешения deploy job ограничьте целевым namespace. Отдельный service account для staging не должен автоматически получать права на production.
Проверка результата как обязательный этап pipeline
Job должна завершаться ошибкой, если rollout не закончился за заданное время, Pod не перешел в Ready или smoke-запрос вернул ошибку.
kubectl rollout status deployment/catalog --namespace staging --timeout=180s
kubectl wait --for=condition=available deployment/catalog --namespace staging --timeout=60s
kubectl get pods --namespace staging --selector app=catalog
kubectl get events --namespace staging --sort-by=.lastTimestamp
При ошибке pipeline сохраняет вывод kubectl describe, логи контейнеров и события. Эти данные сокращают время поиска причины и позволяют отличить проблему образа от ошибки приложения или сети.
Проверка rollout и диагностика проблем
Проверка Deployment и Pod
Проверку выполняйте по цепочке Deployment, Pod, Service, Ingress. Сначала убедитесь, что Deployment получил нужное число доступных реплик.
kubectl get deployment catalog --namespace production
kubectl rollout status deployment/catalog --namespace production --timeout=180s
kubectl get pods --namespace production --selector app=catalog -o wide
kubectl get pods --namespace production --selector app=catalog -o custom-columns=NAME:.metadata.name,READY:.status.containerStatuses[0].ready,STATUS:.status.phase,RESTARTS:.status.containerStatuses[0].restartCount,IMAGE:.spec.containers[0].image
В нормальном результате Pod имеют статус Running, состояние контейнера Ready равно true, а счетчик RESTARTS не растет. Образ в выводе должен содержать ожидаемый IMAGE_TAG.
Анализ логов и событий Kubernetes
Если rollout остановился, получите описание Pod, логи контейнера и последние события:
kubectl describe pod catalog-xxxxx --namespace production
kubectl logs deployment/catalog --namespace production --all-containers=true --tail=200
kubectl get events --namespace production --sort-by=.lastTimestamp
| Признак | Вероятная причина | Проверка |
|---|---|---|
ImagePullBackOff | Ошибка имени образа, тега или доступа к registry | Проверить image, registry credentials и imagePullSecrets |
CrashLoopBackOff | Процесс приложения завершается после запуска | Посмотреть kubectl logs и переменные окружения |
| Pod Running, Ready false | Не проходит readiness probe | Проверить endpoint, порт и время запуска |
| Недостаточно ресурсов | На узле нет свободных CPU или памяти | Изучить события и значения requests |
| Рост рестартов | Падает процесс или срабатывает liveness probe | Сравнить логи, лимиты и параметры liveness |
События показывают причину, которую не всегда видно в логах приложения: отказ планировщика, нехватку памяти, ошибку монтирования тома или проблему загрузки образа.
Проверка Service и Ingress
Если Pod готов, но запрос не проходит, проверьте Service и его endpoints:
kubectl get service catalog --namespace production
kubectl get endpointslice --namespace production -l kubernetes.io/service-name=catalog
kubectl describe service catalog --namespace production
kubectl describe ingress catalog --namespace production
Пустой EndpointSlice обычно означает, что selector Service не совпадает с labels Pod или Pod не прошел readiness probe. Сравните эти значения:
kubectl get pods --namespace production --show-labels
kubectl get service catalog --namespace production -o yaml
Для K3s проверьте класс Ingress traefik, домен, DNS-запись и доступность портов 80 или 443. Внутренний тест через Service помогает отделить сетевую проблему Ingress от ошибки приложения.
Откат релиза и защита от неудачного деплоя
Когда нужно выполнять rollback
Остановите обновление и подготовьте откат, если rollout не завершился за заданный тайм-аут, Pod не проходят readiness probe, новая версия возвращает ошибки в smoke-тесте или контейнер не может скачать образ.
Откат нужен и после формально успешного rollout, если приложение отвечает с ошибками, теряет соединение с базой данных или нарушает критическую бизнес-функцию. Проверка статуса Deployment не заменяет функциональный тест.
Команды для возврата к предыдущей версии
Сначала посмотрите историю ревизий:
kubectl rollout history deployment/catalog --namespace production
kubectl rollout history deployment/catalog --namespace production --revision=3
Затем верните предыдущую ревизию и дождитесь ее готовности:
kubectl rollout undo deployment/catalog --namespace production
kubectl rollout status deployment/catalog --namespace production --timeout=180s
kubectl get pods --namespace production --selector app=catalog
kubectl get service catalog --namespace production
kubectl get ingress catalog --namespace production
После отката сохраните логи и события неудачной версии. Откат восстанавливает рабочее состояние, но не объясняет причину сбоя.
Идемпотентность и повторный запуск
Используйте kubectl apply, фиксированные имена ресурсов и одинаковые шаблоны. Повторный запуск должен приводить кластер к тому же состоянию, если параметры образа и конфигурации не изменились.
- Не удаляйте Deployment перед каждым обновлением.
- Не создавайте новые имена Pod вручную, ими управляет Deployment.
- Проверяйте существование namespace до применения манифестов.
- Обрабатывайте ситуацию, когда предыдущий rollout еще не завершился.
- Не переиспользуйте изменяемый тег для разных содержимых образа.
- Задавайте тайм-аут rollout и понятный код ошибки.
Типичные ошибки в скрипте развертывания приложения в Kubernetes
Деплой считается успешным после kubectl apply
kubectl apply сообщает о принятии объектов Kubernetes. Контейнер после этого может не запуститься, не скачать образ или не пройти probe.
Исправление: ждать kubectl rollout status, проверять Ready-состояние Pod и выполнять smoke-запрос. При тайм-ауте нужно вывести логи, описание ресурсов и события.
Плавающие теги и повторяемая сборка
Тег latest скрывает содержимое релиза. Один и тот же манифест может запустить разные образы в разные моменты, а rollback не даст надежной гарантии возврата к прежнему содержимому.
Используйте уникальные теги релиза или SHA коммита, храните связь между исходным кодом и образом в registry. Не собирайте приложение на production-сервере, сборка должна проходить в build job или на подготовленной рабочей машине.
Секреты и параметры окружения находятся в репозитории
Пароли и токены в Git попадают в историю коммитов и могут остаться доступными после удаления строки из последней версии файла.
Исправление: хранить чувствительные значения в Secret, защищенных переменных CI/CD или внешнем менеджере секретов. ConfigMap используйте для обычных параметров и разделяйте значения staging и production.
- Неверный namespace. Перед применением проверяйте разрешенный список namespace и текущий context.
- Несовпадение labels и selector. Используйте одинаковое значение
appв Deployment, Pod и Service. - Отсутствие probes. Добавьте readiness и liveness с реальными endpoint приложения.
- Неограниченное ожидание rollout. Используйте тайм-аут, например 180 секунд, и завершайте job с ошибкой.
- Нет плана отката. Проверьте историю Deployment и заранее подготовьте команду
kubectl rollout undo.
Чек-лист готового сценария деплоя
Минимальный набор проверок перед production
Перед рабочим запуском сначала выполните деплой в staging на том же наборе манифестов. После smoke-проверки используйте для production тот же IMAGE_TAG, который прошел тестирование.
- Образ собирается на CI-сервере или рабочей машине, production не выполняет сборку.
- Используется уникальный
IMAGE_TAG, тегlatestзапрещен. - Container Registry доступен build job и узлам Kubernetes.
- Namespace задан явно и проверяется до запуска.
- Kubernetes context совпадает с ожидаемым кластером.
- Права
kubectlограничены целевым namespace. - Манифесты параметризованы через namespace, образ, тег и конфигурацию среды.
- Deployment содержит replicas, requests, limits и стратегию RollingUpdate.
- Readiness probe проверяет реальный endpoint готовности.
- Liveness probe контролирует процесс и учитывает время запуска.
- Service выбирает Pod по совпадающим labels и selector.
- Ingress настроен только при необходимости внешнего HTTP или HTTPS-доступа.
- Rollout ожидается с ограничением времени.
- Проверяются состояние Pod, логи, события, Service и Ingress.
- Секреты не хранятся в открытом виде в Git и логах pipeline.
- Для неудачного релиза подготовлена команда
kubectl rollout undo. - Результат staging и production сохраняется в журнале CI/CD.
Готовый сценарий должен завершаться только после подтверждения состояния приложения. Применение манифестов, успешный rollout и рабочий HTTP-ответ проверяют разные уровни деплоя, поэтому каждый из них нужен в автоматической цепочке.