У попередніх розділах ми навчилися працювати з Docker — створювати образи, запускати контейнери, об'єднувати їх через Docker Compose. Ви вже знаєте, що контейнер — це ізольований процес з власною файловою системою, мережею та ресурсами. Але чому Kubernetes не працює з контейнерами напряму? Чому не можна просто сказати «запусти контейнер mcr.microsoft.com/dotnet/aspnet:8.0», як у Docker?
Щоб відповісти на це питання, потрібно зрозуміти фундаментальну проблему, яку вирішує Kubernetes: оркестрація розподілених застосунків у production-середовищі. У реальних системах застосунок рідко складається з одного процесу. Часто потрібні допоміжні компоненти, які працюють поруч з основним застосунком і тісно з ним взаємодіють.
Уявіть, що ви розгортаєте ASP.NET Core Web API у production. Окрім самого API, вам потрібно:
Усі ці компоненти мають працювати разом, на одному сервері, з спільною мережею та спільними файлами. Якщо основний застосунок переміститься на інший сервер — допоміжні контейнери мають переміститися разом з ним. Якщо основний застосунок зупиниться — допоміжні контейнери теж мають зупинитися.
У Docker Compose ви вирішували це через depends_on та спільні мережі, але це працює лише на одному хості. У розподіленому кластері Kubernetes потрібна інша абстракція — Pod.
Pod (від англ. «стручок», як стручок гороху з кількома горошинами всередині) — це найменша розгортана одиниця у Kubernetes. Pod може містити один або кілька контейнерів, які:
localhostЩоб краще зрозуміти різницю, подивимося на діаграму:
На діаграмі видно ключову різницю:
docker run, docker stop тощо.Розглянемо конкретний приклад. У вас є ASP.NET Core API, який пише логи у файл /var/log/app/app.log. Ви хочете відправляти ці логи в Elasticsearch для централізованого зберігання.
Підхід 1: Один контейнер Додати бібліотеку Serilog.Sinks.Elasticsearch у ваш C# код. Але тепер ваш застосунок залежить від Elasticsearch — якщо Elasticsearch недоступний, застосунок може падати або працювати повільно.
Підхід 2: Два контейнери в одному Pod
Переваги:
Це і є суть Pod — розділення відповідальності між контейнерами, які працюють разом.
Тепер, коли ви розумієте навіщо потрібен Pod, давайте дамо точне визначення.
Pod — це найменша розгортана одиниця у Kubernetes, яка представляє один або кілька контейнерів, що:
node-1, всі його контейнери будуть на node-1.localhost і можуть використовувати одні й ті самі порти (але не конфліктувати між собою).Коли Kubernetes створює Pod, він насправді створює спеціальний "інфраструктурний" контейнер (pause container), який резервує мережевий namespace. Всі інші контейнери Pod приєднуються до цього namespace. Результат:
10.244.1.5)localhostlocalhost:porthttp://localhost:5000. Не потрібно знати IP-адресу контейнера API, не потрібно налаштовувати Docker networks — все працює через localhost.Контейнери в Pod можуть монтувати одні й ті самі volumes. Це дозволяє їм обмінюватися файлами без мережевих запитів. Наприклад:
/var/log/app/app.log/var/log/app/app.log та відправляє в ElasticsearchОбидва контейнери монтують один і той самий volume у різні шляхи своєї файлової системи. Для контейнера 1 це /var/log/app, для контейнера 2 — теж /var/log/app (або будь-який інший шлях).
Тепер, коли ви розумієте концепцію Pod, давайте навчимося описувати Pod у форматі YAML. Kubernetes використовує декларативний підхід — ви описуєте бажаний стан системи у YAML-файлі, а Kubernetes робить все необхідне, щоб досягти цього стану.
Ми розглянемо специфікацію Pod поступово: спочатку найпростіший приклад, потім додаватимемо поля одне за одним, пояснюючи кожне детально.
Найпростіший Pod містить один контейнер. Ось мінімально необхідна конфігурація:
apiVersion: v1
kind: Pod
metadata:
name: simple-pod
spec:
containers:
- name: nginx
image: nginx:1.27
Це все, що потрібно для створення Pod! Давайте розберемо кожне поле.
Версія схеми API Kubernetes, яку ви використовуєте для створення цього ресурсу.
На що це впливає? Вона вказує Kubernetes, за якими правилами (схемою валідації) розпізнавати та перевіряти поля у вашому YAML-файлі. Різні версії API можуть мати різні доступні поля. Якщо вказати версію неправильно, Kubernetes видасть помилку та не зможе розпарсити маніфест.
Аналогія з розробки:
Це дуже схоже на версіонування у звичайних Web API. Коли ви створюєте маршрути /api/v1/users та /api/v2/users, кожен з них очікує свій формат JSON-запиту. Так само і в Kubernetes: версія вказує на конкретний «контракт» даних.
Чому для Pod пишеться просто v1?
Pod — це фундаментальний («ядерний») ресурс, який існує з першого дня створення Kubernetes. Він належить до так званої Core API Group (базової групи). Для цієї групи префікс опускається, тому ми вказуємо лише версію схеми — v1.
Чому для Deployment пишеться apps/v1?
У Kubernetes є сотні різних ресурсів. Щоб не тримати їх усі в одній величезній схемі, їх розділили на логічні групи API (API Groups). Загальний формат запису виглядає як назва-групи/версія-схеми.
apps — це назва групи API, яка містить ресурси для управління робочими навантаженнями та життєвим циклом застосунків (Deployments, StatefulSets, DaemonSets).v1 — це стабільна версія схеми для цієї конкретної групи.v1 — це стабільна версія, яка існує з самого початку Kubernetes. Інші ресурси можуть мати інші версії (наприклад, apps/v1 для Deployment).Pod. Kubernetes підтримує багато типів ресурсів: Service, Deployment, ConfigMap тощо. Поле kind вказує, що саме ви створюєте.name.my-app, api-server-1, web-frontend.kubectl logs, kubectl exec тощо.[registry/]repository[:tag]. Якщо не вказано registry — використовується Docker Hub. Якщо не вказано tag — використовується latest (не рекомендується для production!). Приклади: nginx:1.27, mcr.microsoft.com/dotnet/aspnet:8.0, myregistry.azurecr.io/myapp:v1.2.3.Збережіть YAML у файл simple-pod.yaml та створіть Pod:
Що означають ці колонки:
metadata.name)1/1 означає 1 з 1)Running, Pending, Failed тощо)Щоб побачити детальну інформацію про Pod, використовуйте kubectl describe:
Тут ви бачите:
Node) запущено PodТепер додамо типові поля, які використовуються у більшості Pod.
Labels — це key-value пари, які використовуються для ідентифікації та групування ресурсів. Вони критично важливі для роботи Service, Deployment та інших ресурсів Kubernetes.
apiVersion: v1
kind: Pod
metadata:
name: labeled-pod
labels:
app: myapp
tier: backend
environment: production
spec:
containers:
- name: app
image: myapp:1.0.0
app (назва застосунку), version (версія), tier (рівень: frontend/backend), environment (dev/staging/production).Навіщо потрібні labels?
Уявіть, що у вас 100 Pod різних застосунків. Як Service дізнається, до яких Pod направляти трафік? Через labels! Service має селектор app: myapp, і він знаходить всі Pod з такою міткою.
Декларація портів, які контейнер відкриває:
apiVersion: v1
kind: Pod
metadata:
name: web-pod
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
protocol: TCP
name: http
containerPort: 5000.TCP (за замовчуванням) або UDP. У більшості випадків використовується TCP.http, https, grpc, metrics.ports не відкриває порти назовні кластера! Воно лише документує, які порти використовує контейнер. Щоб зробити Pod доступним ззовні, потрібен Service (це тема наступних статей).Змінні оточення — основний спосіб передачі конфігурації в контейнери:
apiVersion: v1
kind: Pod
metadata:
name: env-pod
spec:
containers:
- name: app
image: myapp:1.0.0
env:
- name: DATABASE_URL
value: 'postgresql://db:5432/mydb'
- name: LOG_LEVEL
value: 'info'
- name: ASPNETCORE_ENVIRONMENT
value: 'Production'
-e у docker run. Кожен елемент масиву має поля name (назва змінної) та value (значення).DATABASE_URL, API_KEY, PORT.appsettings.json. Наприклад, змінна ConnectionStrings__DefaultConnection перевизначить ConnectionStrings:DefaultConnection у JSON (зверніть увагу на подвійне підкреслення __ замість :).Kubernetes дозволяє вказати, скільки CPU та пам'яті потрібно контейнеру:
apiVersion: v1
kind: Pod
metadata:
name: resource-pod
spec:
containers:
- name: app
image: myapp:1.0.0
resources:
requests:
memory: '128Mi'
cpu: '250m'
limits:
memory: '256Mi'
cpu: '500m'
requests (мінімум, який гарантується) та limits (максимум, який контейнер може використати).Ki (кібібайт), Mi (мебібайт), Gi (гібібайт). Це бінарні одиниці (1 Mi = 1024 Ki), а не десяткові. Приклади: 128Mi, 1Gi, 512Mi.m (міліядра). 1000m = 1 ядро CPU. 250m = 0.25 ядра (25% одного ядра). Можна писати як 0.25 або 250m — це еквівалентно. Приклади: 100m, 500m, 1, 2.requests.memory. Якщо контейнер спробує виділити більше пам'яті — Kubernetes вб'є процес (OOMKilled).requests.cpu. Якщо контейнер спробує використати більше CPU — він буде throttled (Linux CFS quota).requests: 128Mi, limits: 256Mi означає "гарантуй мені 128 МБ, але дозволь використати до 256 МБ, якщо на вузлі є вільна пам'ять".За замовчуванням Kubernetes запускає команду, вказану в Dockerfile (ENTRYPOINT та CMD). Але ви можете перевизначити її:
apiVersion: v1
kind: Pod
metadata:
name: command-pod
spec:
containers:
- name: app
image: busybox:1.36
command: ['sh', '-c']
args: ['echo Hello from Kubernetes! && sleep 3600']
ENTRYPOINT з Dockerfile. Це команда, яка буде виконана при старті контейнера. Формат: масив рядків. Перший елемент — виконуваний файл, решта — аргументи. Приклад: ["dotnet", "MyApp.dll"].CMD з Dockerfile. Це аргументи, які передаються команді з command. Якщо command не вказано — args передаються ENTRYPOINT з Dockerfile. Формат: масив рядків. Приклад: ["--port", "8080", "--verbose"].command.args.command стає виконуваним файлом, args — його аргументами.ENTRYPOINT ["dotnet", "MyApp.dll"]. Ви можете додати аргументи через args: ["--environment", "Production"], і контейнер запуститься як dotnet MyApp.dll --environment Production.Тепер об'єднаємо все, що ми вивчили, в один Pod:
apiVersion: v1
kind: Pod
metadata:
name: full-example-pod
labels:
app: myapp
version: '1.0'
tier: backend
spec:
containers:
- name: app
image: mcr.microsoft.com/dotnet/samples:aspnetapp
ports:
- containerPort: 8080
protocol: TCP
name: http
env:
- name: ASPNETCORE_URLS
value: 'http://+:8080'
- name: ASPNETCORE_ENVIRONMENT
value: 'Production'
- name: ConnectionStrings__DefaultConnection
value: 'Server=db;Database=mydb;User=sa;Password=YourStrong@Passw0rd'
resources:
requests:
memory: '128Mi'
cpu: '250m'
limits:
memory: '512Mi'
cpu: '1000m'
command: ['./aspnetapp']
Цей Pod:
full-example-pod та мітки для ідентифікаціїmcr.microsoft.com/dotnet/samples:aspnetapp), який можна запустити одразу без створення власного проєкту./aspnetapp (перевизначаючи стандартну команду для демонстрації роботи поля command)Створіть цей Pod та перевірте його стан:
Тепер, коли ви знаєте, як описати Pod у YAML, давайте розберемося, що відбувається з Pod від моменту створення до завершення. Розуміння життєвого циклу критично важливе для діагностики проблем та написання надійних застосунків.
Kubernetes відстежує стан Pod через поле status.phase. Pod проходить через кілька фаз протягом свого життя:
Розглянемо кожну фазу детально:
Окрім фази Pod, кожен контейнер всередині Pod має власний стан. Це дає більш детальну інформацію про те, що відбувається:
reason вказує конкретну причину: ContainerCreating, ImagePullBackOff, CrashLoopBackOff.startedAt показує, коли контейнер запустився.exitCode — код завершення процесу (0 = успіх, інше = помилка)reason — причина завершення (Completed, Error, OOMKilled)startedAt та finishedAt — часові міткиПереглянути детальний стан контейнерів можна через kubectl describe:
Kubernetes може автоматично перезапускати контейнери, які завершились. Поведінка залежить від restartPolicy:
apiVersion: v1
kind: Pod
metadata:
name: restart-demo
spec:
restartPolicy: Always # Always | OnFailure | Never
containers:
- name: app
image: myapp:1.0
restartPolicy застосовується до всього Pod, а не до окремих контейнерів. Усі контейнери у Pod мають однаковий restartPolicy. Не можна налаштувати різні політики для різних контейнерів одного Pod.Коли контейнер падає і перезапускається, Kubernetes не перезапускає його миттєво. Використовується exponential backoff (експоненційна затримка):
Це запобігає ситуації, коли контейнер падає миттєво після запуску (наприклад, через неправильну конфігурацію) і створює нескінченний цикл перезапусків, навантажуючи систему.
Якщо ви бачите стан CrashLoopBackOff — це означає, що контейнер падає одразу після запуску, і Kubernetes чекає перед наступною спробою.
У цьому випадку потрібно виправити код застосунку — контейнер падає через необроблений виняток у C# коді.
Ми вже згадували, що контейнери в Pod можуть мати спільні volumes для обміну даними. Тепер розглянемо це детально.
За замовчуванням файлова система контейнера ефемерна (тимчасова). Коли контейнер перезапускається — всі зміни у файловій системі втрачаються. Контейнер стартує з чистого стану, як визначено в образі.
Приклад проблеми:
Ваш ASP.NET Core API пише логи у файл /var/log/app/app.log. Контейнер падає і перезапускається. Всі логи втрачено — файл /var/log/app/app.log не існує в новому контейнері.
Рішення: Використати volume — постійне (або спільне) сховище, яке існує незалежно від життєвого циклу контейнера.
Kubernetes підтримує багато типів volumes. Розглянемо найважливіші для початку:
emptyDir — це порожня директорія, яка створюється разом з Pod і видаляється разом з ним. Дані зберігаються на диску вузла, але існують лише протягом життя Pod.
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
volumes:
- name: cache
emptyDir: {}
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: cache
mountPath: /app/cache
name) та тип (наприклад, emptyDir, configMap, secret).volumeMounts.medium: "Memory" для створення volume в RAM (швидше, але обмежено пам'яттю).spec.volumes[], який потрібно змонтувати./app/cache, /var/log, /etc/config.false (read-write). Корисно для конфігурацій та секретів, які не повинні змінюватися.Використання emptyDir:
emptyDirне зберігаються після видалення Pod. Якщо Pod перезапускається (контейнер падає) — дані зберігаються. Але якщо Pod видаляється (наприклад, через kubectl delete pod) — дані втрачаються назавжди.ConfigMap та Secret — це ресурси Kubernetes для зберігання конфігурацій та чутливих даних. Їх можна монтувати як volumes:
apiVersion: v1
kind: Pod
metadata:
name: config-pod
spec:
volumes:
- name: config
configMap:
name: app-config
- name: secrets
secret:
secretName: app-secrets
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
- name: secrets
mountPath: /etc/secrets
readOnly: true
Припустимо, у вас є ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database.conf: |
host=db.example.com
port=5432
logging.conf: |
level=info
format=json
Коли ви змонтуєте цей ConfigMap у /etc/config, контейнер побачить два файли:
/etc/config/database.conf з вмістом host=db.example.com\nport=5432/etc/config/logging.conf з вмістом level=info\nformat=jsonКожен ключ у ConfigMap стає окремим файлом у змонтованій директорії.
appsettings.json та монтувати його у контейнер. ASP.NET Core автоматично прочитає файл при старті. Це дозволяє змінювати конфігурацію без перезбірки Docker-образу.Розглянемо практичний приклад — ASP.NET Core API пише логи у файл, а другий контейнер читає ці логи:
apiVersion: v1
kind: Pod
metadata:
name: shared-logs-pod
spec:
volumes:
- name: logs
emptyDir: {}
containers:
# Основний застосунок
- name: app
image: myapp:1.0
volumeMounts:
- name: logs
mountPath: /var/log/app
env:
- name: ASPNETCORE_URLS
value: 'http://+:8080'
# Контейнер для читання логів
- name: log-reader
image: busybox:1.36
volumeMounts:
- name: logs
mountPath: /var/log/app
readOnly: true
command: ['sh', '-c']
args: ['tail -f /var/log/app/app.log']
Що відбувається:
logs типу emptyDirapp монтує цей volume у /var/log/app (read-write)log-reader монтує той самий volume у /var/log/app (read-only)app пише логи у /var/log/app/app.loglog-reader читає файл /var/log/app/app.log через tail -fОбидва контейнери бачать один і той самий файл через спільний volume!
Перевірити логи можна через kubectl logs:
Опція -c log-reader вказує, з якого контейнера читати логи (оскільки в Pod два контейнери).
Тепер застосуємо все, що ми вивчили, для створення реального .NET застосунку у Kubernetes. Ми створимо простий Minimal API, який повертає список задач (To-Do List).
Створіть новий проєкт ASP.NET Core Minimal API:
Відредагуйте Program.cs:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// In-memory список задач
var todos = new List<Todo>
{
new(1, "Вивчити Kubernetes", false),
new(2, "Створити Pod", false),
new(3, "Задеплоїти застосунок", false)
};
app.MapGet("/", () => "Todo API is running!");
app.MapGet("/api/todos", () => todos);
app.MapGet("/api/todos/{id}", (int id) =>
{
var todo = todos.FirstOrDefault(t => t.Id == id);
return todo is not null ? Results.Ok(todo) : Results.NotFound();
});
app.MapPost("/api/todos", (Todo todo) =>
{
todos.Add(todo);
return Results.Created($"/api/todos/{todo.Id}", todo);
});
app.Run();
record Todo(int Id, string Title, bool IsCompleted);
Це простий API з трьома ендпоінтами:
GET / — перевірка здоров'яGET /api/todos — отримати всі задачіGET /api/todos/{id} — отримати задачу за IDPOST /api/todos — створити нову задачуСтворіть Dockerfile у корені проєкту:
# Етап 1: Збірка
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# Копіюємо .csproj та відновлюємо залежності
COPY TodoApi.csproj .
RUN dotnet restore
# Копіюємо решту файлів та збираємо
COPY . .
RUN dotnet publish -c Release -o /app/publish
# Етап 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
# Копіюємо зібраний застосунок
COPY --from=build /app/publish .
# Налаштовуємо порт
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
# Запускаємо застосунок
ENTRYPOINT ["dotnet", "TodoApi.dll"]
Це multi-stage Dockerfile:
Зберіть Docker-образ:
Якщо ви використовуєте Minikube, завантажте образ у Minikube:
docker build, він зберігається у Docker daemon вашої хост-системи. Команда minikube image load копіює образ у Docker daemon Minikube, щоб Kubernetes міг його використати.Альтернатива: Опублікувати образ у Docker Hub або іншому registry, і Kubernetes завантажить його звідти.Створіть файл todoapi-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: todoapi
labels:
app: todoapi
version: '1.0'
spec:
containers:
- name: api
image: todoapi:1.0
imagePullPolicy: Never # Не завантажувати з registry, використовувати локальний образ
ports:
- containerPort: 8080
name: http
protocol: TCP
env:
- name: ASPNETCORE_ENVIRONMENT
value: 'Development'
- name: ASPNETCORE_URLS
value: 'http://+:8080'
resources:
requests:
memory: '64Mi'
cpu: '100m'
limits:
memory: '128Mi'
cpu: '250m'
Always — завжди завантажувати з registry (навіть якщо образ є локально)IfNotPresent — завантажувати лише якщо образу немає локальноNever — ніколи не завантажувати, використовувати лише локальний образNever, щоб Kubernetes не намагався завантажити образ з Docker Hub.Створіть Pod:
Перевірте логи застосунку:
Застосунок запущено! Тепер потрібно отримати доступ до нього.
Оскільки Pod має IP-адресу лише всередині кластера, використайте kubectl port-forward для доступу з вашої машини:
Тепер відкрийте новий термінал та протестуйте API:
Чудово! Ваш .NET застосунок працює у Kubernetes Pod.
Kubernetes надає кілька команд для діагностики Pod:
Переглянути детальну інформацію:
Виконати команду всередині контейнера:
Опція -it означає інтерактивний режим з TTY (як docker exec -it).
Переглянути використання ресурсів:
Pod використовує 5 міліядер CPU (0.005 ядра) та 45 МБ пам'яті — значно менше, ніж наш ліміт у 250m CPU та 128Mi пам'яті.
У цій статті ми детально розглянули Pod — атомарну одиницю Kubernetes:
Концепція Pod
YAML специфікація
metadata (назва, мітки), spec.containers (образ, порти, змінні оточення, ресурси, команда запуску). Кожне поле має чітке призначення та правила використання.Життєвий цикл
restartPolicy (Always, OnFailure, Never).Volumes
emptyDir — для тимчасових даних, configMap та secret — для конфігурацій та чутливих даних..NET приклад
Ключовий висновок: Pod — це низькорівневий примітив. Він дає гнучкість для складних сценаріїв, але не надає автоматизації для production (self-healing, масштабування, rolling updates). У наступних статтях ми вивчимо патерни використання Pod (init-контейнери, sidecar) та вищі абстракції (Deployment), які керують Pod автоматично.
Створіть свій перший Pod з офіційним веб-сервером Nginx та налаштуйте локальний доступ до нього через порт-форвардинг, щоб перевірити його працездатність.
Маніфест nginx-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx-practice
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
name: http
Кроки для виконання:
nginx-pod.yaml та збережіть його.Вимоги:
http://localhost:8080 або виконавши команду curl http://localhost:8080.Дослідіть, як контейнери всередині одного Pod ділять між собою мережевий простір та IP-адресу. Для цього запустіть веб-сервер та допоміжну утиліту в межах єдиного Pod.
Маніфест multi-container-net.yaml:
apiVersion: v1
kind: Pod
metadata:
name: net-sharing-practice
spec:
restartPolicy: Never
containers:
- name: web-server
image: nginx:1.27
- name: utility
image: busybox:1.36
command: ["sleep", "3600"]
Кроки для виконання та перевірки:
utility за допомогою kubectl exec:
Питання для самоперевірки:
utility зміг успішно виконати запит до localhost:80, хоча веб-сервер запущено в іншому контейнері web-server?80?Налаштуйте спільну ефемерну папку (emptyDir) між двома контейнерами: один з яких періодично генерує логи, а інший — зчитує їх у реальному часі.
Маніфест shared-volume-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-practice
spec:
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "while true; do echo \"[$(date)] Log entry from writer\" >> /data/message.txt; sleep 5; done"]
volumeMounts:
- name: shared-data
mountPath: /data
- name: reader
image: busybox:1.36
command: ["sh", "-c", "sleep 3; tail -f /data/message.txt"]
volumeMounts:
- name: shared-data
mountPath: /data
readOnly: true
Кроки для виконання та перевірки:
reader для перевірки виводу:
Питання для самоперевірки:
readOnly: true захищає дані від контейнера-читача? Що станеться, якщо спробувати виконати запис із контейнера reader?/data/message.txt, якщо контейнер writer впаде та перезапуститься? А якщо видалити весь Pod?Дослідіть поведінку Kubernetes при завершенні контейнера з помилкою за різних політик перезапуску.
Кроки для виконання:
restart-practice.yaml:
apiVersion: v1
kind: Pod
metadata:
name: restart-never-practice
spec:
restartPolicy: Never # Змініть також на Always та OnFailure
containers:
- name: broken-app
image: busybox:1.36
command: ["sh", "-c", "sleep 5; exit 1"]
restartPolicy на Never, Always та OnFailure (та змінюючи назву Pod у metadata.name).Питання для самоперевірки:
STATUS) та кількість перезапусків (RESTARTS) для кожного з трьох Pods через 2 хвилини після запуску.CrashLoopBackOff та яка його мета?Налаштуйте жорсткі ліміти пам'яті для контейнера та запустіть у ньому процес, що споживає більше дозволеного, щоб побачити роботу Linux Out-Of-Memory (OOM) Killer.
Маніфест oom-practice-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: oom-practice
spec:
restartPolicy: Never
containers:
- name: memory-hog
image: polonel/stress:latest
command: ["stress", "--vm", "1", "--vm-bytes", "150M", "--timeout", "15s"]
resources:
limits:
memory: "50Mi"
cpu: "200m"
requests:
memory: "25Mi"
cpu: "100m"
Кроки для виконання та перевірки:
describe:
Питання для самоперевірки:
limits.memory) миттєво вбиває процес?Exit Code 137?Навчіться монтувати файли конфігурації через ConfigMap безпосередньо у файлову систему контейнера та спостерігати за їх синхронізацією.
Маніфест config-practice.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-settings
data:
config.json: |
{
"theme": "dark",
"features": ["auth", "billing"]
}
---
apiVersion: v1
kind: Pod
metadata:
name: config-practice
spec:
volumes:
- name: config-volume
configMap:
name: app-settings
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "while true; do cat /etc/config/config.json; echo ''; sleep 10; done"]
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
Кроки для виконання та перевірки:
kubectl apply -f config-practice.yaml."theme": "dark" на "theme": "light", збережіть файл та вийдіть з редактора.Питання для самоперевірки: