Це багатосторінковий друкований вигляд цього розділу. Натисність щоб друкувати.

Повернутися до звичайного перегляду сторінки.

Навчальні матеріали

У цьому розділі документації Kubernetes зібрані навчальні матеріали. Кожний матеріал показує, як досягти окремої мети, що більша за одне завдання. Зазвичай навчальний матеріал має декілька розділів, кожен з яких містить певну послідовність дій. До ознайомлення з навчальними матеріалами вам, можливо, знадобиться додати у закладки сторінку з Глосарієм для подальшого консультування.

Основи

Конфігурація

Застосунки без стану (Stateless Applications)

Застосунки зі станом (Stateful Applications)

Кластери

Сервіси

Що далі

Якщо ви хочете написати навчальний матеріал, у статті Використання шаблонів сторінок ви знайдете інформацію про тип навчальної сторінки і шаблон.

1 - Привіт Minikube

З цього навчального матеріалу ви дізнаєтесь, як запустити у Kubernetes простий Hello World застосунок на Node.js за допомогою Minikube і Katacoda. Katacoda надає безплатне Kubernetes середовище, що доступне у вашому браузері.

Примітка:

Також ви можете навчатись за цим матеріалом, якщо встановили Minikube локально.

Цілі

  • Розгорнути Hello World застосунок у Minikube.
  • Запустити застосунок.
  • Переглянути логи застосунку.

Перш ніж ви розпочнете

У цьому навчальному матеріалі ми використовуємо образ контейнера, зібраний із наступних файлів:

var http = require('http');

var handleRequest = function(request, response) {
  console.log('Received request for URL: ' + request.url);
  response.writeHead(200);
  response.end('Hello World!');
};
var www = http.createServer(handleRequest);
www.listen(8080);
FROM node:6.14.2
EXPOSE 8080
COPY server.js .
CMD [ "node", "server.js" ]

Більше інформації про команду docker build ви знайдете у документації Docker.

Створення Minikube кластера

  1. Натисніть кнопку Запуск термінала

    
    

    Примітка:

    Якщо Minikube встановлений локально, виконайте команду minikube start.
  1. Відкрийте Kubernetes дашборд у браузері:

    minikube dashboard
    
  1. Тільки для Katacoda: у верхній частині вікна термінала натисніть знак плюс, а потім -- Select port to view on Host 1.
  1. Тільки для Katacoda: введіть 30000, а потім натисніть Display Port.

Створення Deployment

Pod у Kubernetes -- це група з одного або декількох контейнерів, що об'єднані разом з метою адміністрування і роботи у мережі. У цьому навчальному матеріалі Pod має лише один контейнер. Kubernetes Deployment перевіряє стан Pod'а і перезапускає контейнер Pod'а, якщо контейнер перестає працювати. Створювати і масштабувати Pod'и рекомендується за допомогою Deployment'ів.

  1. За допомогою команди kubectl create створіть Deployment, який керуватиме Pod'ом. Pod запускає контейнер на основі наданого Docker образу.

    kubectl create deployment hello-node --image=registry.k8s.io/e2e-test-images/agnhost:2.53 -- /agnhost netexec --http-port=8080
    
  1. Перегляньте інформацію про запущений Deployment:

    kubectl get deployments
    
    У виводі ви побачите подібну інформацію:
    
    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    hello-node   1/1     1            1           1m
    
  1. Перегляньте інформацію про запущені Pod'и:

    kubectl get pods
    
    У виводі ви побачите подібну інформацію:
    
    NAME                          READY     STATUS    RESTARTS   AGE
    hello-node-5f76cf6ccf-br9b5   1/1       Running   0          1m
    
  1. Перегляньте події кластера:

    kubectl get events
    
  1. Перегляньте конфігурацію kubectl:

    kubectl config view
    
    
    

    Примітка:

    Більше про команди kubectl ви можете дізнатися зі статті Загальна інформація про kubectl.

Створення Service

За умовчанням, Pod доступний лише за внутрішньою IP-адресою у межах Kubernetes кластера. Для того, щоб контейнер hello-node став доступний за межами віртуальної мережі Kubernetes, Pod необхідно відкрити як Kubernetes Service.

  1. Відкрийте Pod для публічного доступу з інтернету за допомогою команди kubectl expose:

    kubectl expose deployment hello-node --type=LoadBalancer --port=8080
    
    Прапорець `--type=LoadBalancer` вказує, що ви хочете відкрити доступ до Service за межами кластера.
    
  1. Перегляньте інформацію про Service, який ви щойно створили:

    kubectl get services
    
    У виводі ви побачите подібну інформацію:
    
    NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
    hello-node   LoadBalancer   10.108.144.78   <pending>     8080:30369/TCP   21s
    kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          23m
    
    Для хмарних провайдерів, що підтримують балансування навантаження, доступ до Service надається через зовнішню IP-адресу. Для Minikube, тип `LoadBalancer` робить Service доступним ззовні за допомогою команди `minikube service`.
    
  1. Виконайте наступну команду:

    minikube service hello-node
    
  1. Тільки для Katacoda: натисніть знак плюс, а потім -- Select port to view on Host 1.
  1. Тільки для Katacoda: запишіть п'ятизначний номер порту, що відображається напроти 8080 у виводі сервісу. Номер цього порту генерується довільно і тому може бути іншим у вашому випадку. Введіть номер порту у призначене для цього текстове поле і натисніть Display Port. У нашому прикладі номер порту 30369.

    Це відкриє вікно браузера, в якому запущений ваш застосунок, і покаже повідомлення "Hello World".
    

Увімкнення розширень

Minikube має ряд вбудованих розширень, які можна увімкнути, вимкнути і відкрити у локальному Kubernetes оточенні.

  1. Перегляньте перелік підтримуваних розширень:

    minikube addons list
    
    У виводі ви побачите подібну інформацію:
    
    addon-manager: enabled
    dashboard: enabled
    default-storageclass: enabled
    efk: disabled
    freshpod: disabled
    gvisor: disabled
    helm-tiller: disabled
    ingress: disabled
    ingress-dns: disabled
    logviewer: disabled
    metrics-server: disabled
    nvidia-driver-installer: disabled
    nvidia-gpu-device-plugin: disabled
    registry: disabled
    registry-creds: disabled
    storage-provisioner: enabled
    storage-provisioner-gluster: disabled
    
  1. Увімкніть розширення, наприклад metrics-server:

    minikube addons enable metrics-server
    
    У виводі ви побачите подібну інформацію:
    
    metrics-server was successfully enabled
    
  1. Перегляньте інформацію про Pod і Service, які ви щойно створили:

    kubectl get pod,svc -n kube-system
    
    У виводі ви побачите подібну інформацію:
    
    NAME                                        READY     STATUS    RESTARTS   AGE
    pod/coredns-5644d7b6d9-mh9ll                1/1       Running   0          34m
    pod/coredns-5644d7b6d9-pqd2t                1/1       Running   0          34m
    pod/metrics-server-67fb648c5                1/1       Running   0          26s
    pod/etcd-minikube                           1/1       Running   0          34m
    pod/influxdb-grafana-b29w8                  2/2       Running   0          26s
    pod/kube-addon-manager-minikube             1/1       Running   0          34m
    pod/kube-apiserver-minikube                 1/1       Running   0          34m
    pod/kube-controller-manager-minikube        1/1       Running   0          34m
    pod/kube-proxy-rnlps                        1/1       Running   0          34m
    pod/kube-scheduler-minikube                 1/1       Running   0          34m
    pod/storage-provisioner                     1/1       Running   0          34m
    
    NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
    service/metrics-server         ClusterIP   10.96.241.45    <none>        80/TCP              26s
    service/kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP       34m
    service/monitoring-grafana     NodePort    10.99.24.54     <none>        80:30002/TCP        26s
    service/monitoring-influxdb    ClusterIP   10.111.169.94   <none>        8083/TCP,8086/TCP   26s
    
  1. Вимкніть metrics-server:

    minikube addons disable metrics-server
    
    У виводі ви побачите подібну інформацію:
    
    metrics-server was successfully disabled
    

Вивільнення ресурсів

Тепер ви можете видалити ресурси, які створили у вашому кластері:

kubectl delete service hello-node
kubectl delete deployment hello-node

За бажанням, зупиніть віртуальну машину (ВМ) з Minikube:

minikube stop

За бажанням, видаліть ВМ з Minikube:

minikube delete

Що далі

2 - Створення кластера

3 - Розгортання застосунку

4 - Вивчення застосунку

5 - Налаштування

6 - Stateless-застосунки

7 - Використання Source IP

Застосунки, що працюють у кластері Kubernetes, виявляють одне одного та взаємодіють між собою й із зовнішнім світом через абстракцію Service. У цьому документі пояснюється, що відбувається з Source IP пакетів, надісланих на різні типи Service, і як ви можете змінити цю поведінку відповідно до ваших потреб.

Перш ніж ви розпочнете

Терміни

У цьому документі використовуються такі терміни:

NAT
Трансляція мережевих адрес
Source NAT
Заміна Source IP в пакеті; у цьому документі це зазвичай означає заміну на IP-адресу вузла.
Destination NAT
Заміна Destination IP в пакеті; у цьому документі це зазвичай означає заміну на IP-адресу Pod
VIP
Віртуальна IP-адреса, така як та, що призначається кожному Service у Kubernetes
kube-proxy
Мережевий демон, який керує віртуальними IP Service на кожному вузлі

Необхідні умови

Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою minikube або використовувати одну з цих пісочниць:

У прикладах використовується невеликий вебсервер nginx, який повертає Source IP отриманих запитів через HTTP-заголовок. Ви можете створити його так:

kubectl create deployment source-ip-app --image=registry.k8s.io/echoserver:1.10

Результат:

deployment.apps/source-ip-app created

Цілі

  • Відкрити доступ до простого застосунку через різні типи Service
  • Зрозуміти, як кожен тип Service обробляє Source IP NAT
  • Зрозуміти компроміси, повʼязані зі збереженням Source IP

Source IP для Service з Type=ClusterIP

Пакети, надіслані на ClusterIP зсередини кластера, ніколи не обробляються Source NAT, якщо kube-proxy працює в режимі iptables (типовий режим). Режим kube-proxy можна дізнатися, запитавши http://localhost:10249/proxyMode на вузлі, де працює kube-proxy.

kubectl get nodes

Результат схожий на цей:

NAME                           STATUS     ROLES    AGE     VERSION
kubernetes-node-6jst   Ready      <none>   2h      v1.13.0
kubernetes-node-cx31   Ready      <none>   2h      v1.13.0
kubernetes-node-jj1t   Ready      <none>   2h      v1.13.0

Отримайте режим проксі на одному з вузлів (kube-proxy слухає порт 10249):

# Запустіть це в оболонці на вузлі, який хочете запитати.
curl http://localhost:10249/proxyMode

Результат:

iptables

Ви можете протестувати збереження Source IP, створивши Service для застосунку source IP:

kubectl expose deployment source-ip-app --name=clusterip --port=80 --target-port=8080

Результат:

service/clusterip exposed
kubectl get svc clusterip

Результат схожий на:

NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
clusterip    ClusterIP   10.0.170.92   <none>        80/TCP    51s

А також звернувшись до ClusterIP з Podʼа в тому самому кластері:

kubectl run busybox -it --image=busybox:1.28 --restart=Never --rm

Результат схожий на цей:

Waiting for pod default/busybox to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.

Потім ви можете виконати команду всередині цього Podʼа:

# Запустіть це всередині терміналу "kubectl run"
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc noqueue
    link/ether 0a:58:0a:f4:03:08 brd ff:ff:ff:ff:ff:ff
    inet 10.244.3.8/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::188a:84ff:feb0:26a5/64 scope link
       valid_lft forever preferred_lft forever

…потім використайте wget, щоб надіслати запит до локального вебсервера

# Замініть "10.0.170.92" на IPv4-адресу Service з назвою "clusterip"
wget -qO - 10.0.170.92
CLIENT VALUES:
client_address=10.244.3.8
command=GET
...

client_address завжди є IP-адресою Podʼа-клієнта, незалежно від того, чи перебувають Pod-клієнт і Pod-сервер на одному вузлі, чи на різних вузлах.

Source IP для Service з Type=NodePort

Пакети, надіслані на Service з Type=NodePort, типово обробляються Source NAT. Ви можете протестувати це, створивши Service NodePort:

kubectl expose deployment source-ip-app --name=nodeport --port=80 --target-port=8080 --type=NodePort

Результат:

service/nodeport exposed
NODEPORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services nodeport)
NODES=$(kubectl get nodes -o jsonpath='{ $.items[*].status.addresses[?(@.type=="InternalIP")].address }')

Якщо ви працюєте у хмарного провайдера, можливо, вам доведеться відкрити правило брандмауера для nodes:nodeport, зазначеного вище. Тепер ви можете спробувати звернутися до Service ззовні кластера через виділений вище порт вузла.

for node in $NODES; do curl -s $node:$NODEPORT | grep -i client_address; done

Результат схожий на:

client_address=10.180.1.1
client_address=10.240.0.5
client_address=10.240.0.3

Зверніть увагу, що це неправильні IP клієнтів, це внутрішні IP кластера. Ось що відбувається:

  • Клієнт надсилає пакет до node2:nodePort
  • node2 замінює Source IP-адресу (SNAT) у пакеті своєю власною IP-адресою
  • node2 замінює Destination IP-адресу пакета на IP Podʼа
  • пакет спрямовується до node 1, а потім до точки доступу
  • відповідь Podʼа спрямовується назад до node2
  • відповідь Podʼа надсилається клієнту

Візуально:

схема 01 source IP для nodeport

Схема. Source IP Type=NodePort з використанням SNAT

Щоб уникнути цього, у Kubernetes є можливість зберегти Source IP клієнта. Якщо ви встановите для service.spec.externalTrafficPolicy значення Local, kube-proxy спрямовує запити лише до локальних точок доступу і не пересилає трафік до інших вузлів. Цей підхід зберігає початкову Source IP-адресу. Якщо локальних точок доступу немає, пакети, надіслані до вузла, відкидаються, тому ви можете покладатися на правильну Source IP-адресу в будь-яких правилах обробки пакетів, які ви можете застосувати до пакета, що проходить до точки доступу.

Встановіть поле service.spec.externalTrafficPolicy так:

kubectl patch svc nodeport -p '{"spec":{"externalTrafficPolicy":"Local"}}'

Вивід:

service/nodeport patched

Повторіть тест:

for node in $NODES; do curl --connect-timeout 1 -s $node:$NODEPORT | grep -i client_address; done

Вивід подібний до:

client_address=198.51.100.79

Зауважте, що ви отримали лише одну відповідь із правильною IP-адресою клієнта від одного вузла, на якому працює Pod точки доступу.

Ось що відбувається:

  • клієнт надсилає пакет на node2:nodePort, який не має точок доступу
  • пакет відкидається
  • клієнт надсилає пакет на node1:nodePort, який має точки доступу
  • node1 направляє пакет до точки доступу з правильною Source IP-адресою

Візуально:

схема 02 source IP для nodeport

Схема. Source IP Type=NodePort зберігає Source IP-адресу клієнта

Source IP для Service з Type=LoadBalancer

Пакети, надіслані на Service з Type=LoadBalancer, типово обробляються Source NAT, оскільки всі вузли Kubernetes, доступні для планування, у стані Ready можуть отримувати трафік від балансувальника навантаження. Тому якщо пакети надходять на вузол без точки доступу, система через проксі спрямовує їх на вузол з точкою доступу, замінюючи Source IP пакета на IP-адресу вузла (як описано в попередньому розділі).

Ви можете перевірити це, відкривши доступ до застосунку source-ip-app через балансувальник навантаження:

kubectl expose deployment source-ip-app --name=loadbalancer --port=80 --target-port=8080 --type=LoadBalancer

Вивід:

service/loadbalancer exposed

Виведіть IP-адреси Service:

kubectl get svc loadbalancer

Вивід подібний до цього:

NAME           TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)   AGE
loadbalancer   LoadBalancer   10.0.65.118   203.0.113.140     80/TCP    5m

Потім надішліть запит на зовнішню IP-адресу цього Service:

curl 203.0.113.140

Вивід подібний до цього:

CLIENT VALUES:
client_address=10.240.0.5
...

Однак якщо ви працюєте в Google Kubernetes Engine/GCE, встановлення того самого поля service.spec.externalTrafficPolicy у Local змушує вузли без точок доступу Service вилучати себе зі списку вузлів, які отримують трафік від балансувальника навантаження, навмисно не проходячи перевірки стану.

Візуально:

Source IP з externalTrafficPolicy

Ви можете перевірити це, встановивши анотацію:

kubectl patch svc loadbalancer -p '{"spec":{"externalTrafficPolicy":"Local"}}'

Ви одразу побачите поле service.spec.healthCheckNodePort, виділене Kubernetes:

kubectl get svc loadbalancer -o yaml | grep -i healthCheckNodePort

Вивід подібний до цього:

  healthCheckNodePort: 32122

Поле service.spec.healthCheckNodePort вказує на порт на кожному вузлі, на якому надається перевірка стану за адресою /healthz. Ви можете перевірити це:

kubectl get pod -o wide -l app=source-ip-app

Вивід подібний до цього:

NAME                            READY     STATUS    RESTARTS   AGE       IP             NODE
source-ip-app-826191075-qehz4   1/1       Running   0          20h       10.180.1.136   kubernetes-node-6jst

Використайте curl, щоб запитати /healthz на різних вузлах:

# Виконайте це локально на вузлі, який ви вибрали
curl localhost:32122/healthz
1 Service Endpoints found

На іншому вузлі ви можете отримати інший результат:

# Виконайте це локально на вузлі, який ви вибрали
curl localhost:32122/healthz
No Service Endpoints Found

Контролер, який працює на панелі управління, відповідає за виділення хмарного балансувальника навантаження. Той самий контролер також створює HTTP-перевірки стану, що вказують на цей порт/шлях на кожному вузлі. Зачекайте приблизно 10 секунд, поки 2 вузли без точок доступу не пройдуть перевірки стану, а потім за допомогою curl запитайте IPv4-адресу балансувальника навантаження:

curl 203.0.113.140

Вивід подібний до цього:

CLIENT VALUES:
client_address=198.51.100.79
...

Міжплатформна підтримка

Лише деякі хмарні провайдери підтримують збереження Source IP-адреси через Service з Type=LoadBalancer. Ваш хмарний провайдер може виконати запит на балансувальник навантаження кількома способами:

  1. З проксі, що завершує зʼєднання клієнта та відкриває нове зʼєднання з вашими вузлами/точками доступу. У таких випадках Source IP-адреса завжди буде адресою хмарного балансувальника, а не адресою клієнта.

  2. За допомогою пересилача пакетів (packet forwarder), завдяки чому запити від клієнта, надіслані на VIP балансувальника, потрапляють на вузол із Source IP-адресою клієнта, а не проміжного проксі.

Балансувальники навантаження першої категорії повинні використовувати узгоджений між балансувальником навантаження та бекендом протокол для передачі справжньої IP-адреси клієнта, як-от заголовки HTTP Forwarded або X-FORWARDED-FOR, або протокол proxy. Балансувальники навантаження другої категорії можуть використовувати описану вище можливість, створюючи HTTP-перевірку стану, що вказує на порт, збережений у полі service.spec.healthCheckNodePort Service.

Очищення

Видаліть Service:

kubectl delete svc -l app=source-ip-app

Видаліть Deployment, ReplicaSet та Pod:

kubectl delete deployment source-ip-app

Що далі

8 - Відкриття доступу до застосунку за межами кластера

9 - Масштабування застосунку

10 - Оновлення застосунку