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

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

Завдання

Цей розділ документації Kubernetes містить сторінки, які показують, як виконувати окремі завдання. Сторінка завдання показує, як виконати конкретну дію, зазвичай надаючи коротку послідовність кроків.

Якщо ви хочете написати сторінку завдання, див. Створення запиту на злиття для документації.

1 - Встановлення інструментів

Встановлення інструментів Kubernetes на ваш компʼютер.

kubectl

Інструмент командного рядка Kubernetes, kubectl, дозволяє вам виконувати команди відносно кластерів Kubernetes. Ви можете використовувати kubectl для розгортання застосунків, огляду та управління ресурсами кластера, а також перегляду логів. Для отримання додаткової інформації, включаючи повний перелік операцій kubectl, див. Документацію з посилань на kubectl.

kubectl можна встановити на різноманітних платформах Linux, macOS та Windows. Знайдіть свою вибрану операційну систему нижче.

kind

kind дозволяє вам запускати Kubernetes на вашому локальному компʼютері. Цей інструмент вимагає встановлення або Docker, або Podman.

На сторінці Швидкий старт з kind показано, що вам потрібно зробити, щоб розпочати роботу з kind.

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

minikube

Подібно до kind, minikube — це інструмент, який дозволяє вам запускати Kubernetes локально. minikube запускає одно- або багатовузловий локальний кластер Kubernetes на вашому персональному компʼютері (включаючи ПК з операційними системами Windows, macOS і Linux), так щоб ви могли випробувати Kubernetes або використовувати його для щоденної розробки.

Якщо ваша основна мета — встановлення інструменту, ви можете скористатися офіційним посібником Швидкий старт.

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

Якщо у вас вже працює minikube, ви можете використовувати його для запуску застосунку-прикладу.

kubeadm

Ви можете використовувати інструмент kubeadm для створення та управління кластерами Kubernetes. Він виконує необхідні дії для запуску мінімально життєздатного та захищеного кластера за допомогою зручного інтерфейсу користувача.

На сторінці Встановлення kubeadm показано, як встановити kubeadm. Після встановлення ви можете використовувати його для створення кластера.

Переглянути посібник з встановлення kubeadm

1.1 - Встановлення та налаштування kubectl у Linux

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

Вам потрібно використовувати версію kubectl, яка має мінорну версію що відрізняється не більше ніж на одиницю від мінорної версії вашого кластера. Наприклад, клієнт v1.31 може співпрацювати з панелями управління v1.30, v1.31 та v1.32. Використання останньої сумісної версії kubectl допомагає уникнути непередбачуваних проблем.

Встановлення kubectl у Linux

Існують наступні методи встановлення kubectl у Linux:

Встановлення бінарного файлу kubectl за допомогою curl у Linux

  1. Завантажте останній випуск за допомогою команди:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl"
       
  2. Перевірте бінарний файл (опційно)

    Завантажте файл хеш-суми kubectl:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl.sha256"
       

    Перевірте бінарний файл kubectl за допомогою файлу хеш-суми:

    echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check
    

    Якщо результат валідний, виведе:

    kubectl: OK
    

    Якщо перевірка не пройшла, sha256 поверне ненульовий статус і виведе повідомлення подібне до:

    kubectl: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  3. Встановіть kubectl

    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
    
  4. Перевірте, щоб переконатися, що встановлена вами версія є актуальною:

    kubectl version --client
    

    Або скористайтеся цим для детального перегляду версії:

    kubectl version --client --output=yaml
    

Встановлення за допомогою стандартного пакетного менеджера

  1. Оновіть індекс пакунків apt та встановіть пакунки, необхідні для використання репозиторію apt Kubernetes:

    sudo apt-get update
    # apt-transport-https може бути макетним пакетом; якщо так, ви можете пропустити цей пакет
    sudo apt-get install -y apt-transport-https ca-certificates curl gnupg
    
  2. Завантажте публічний ключ підпису для репозиторіїв пакунків Kubernetes. Той самий ключ підпису використовується для всіх репозиторіїв, тому ви можете проігнорувати версію в URL:

    # Якщо тека `/etc/apt/keyrings` не існує, її слід створити перед запуском curl, прочитайте примітку нижче.
    # sudo mkdir -p -m 755 /etc/apt/keyrings
    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
    sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg # дозволяє непривілейованим програмам APT читати цей ключ
    
  1. Додайте відповідний репозиторій Kubernetes apt. Якщо ви хочете використовувати версію Kubernetes, відмінну від v1.31, замініть v1.31 на потрібну мінорну версію в команді нижче:

    # Це перезапише будь-яку існуючу конфігурацію в /etc/apt/sources.list.d/kubernetes.list
    echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
    sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list   # допомагає правильно працювати з такими інструментами, як command-not-found
    
  1. Оновіть індекс пакунків apt, а потім встановіть kubectl:

    sudo apt-get update
    sudo apt-get install -y kubectl
    

  1. Додайте репозиторій Kubernetes yum. Якщо ви хочете використовувати версію Kubernetes, відмінну від v1.31, замініть v1.31 на потрібну мінорну версію в команді нижче.

    # Це перезапише будь-яку існуючу конфігурацію у /etc/yum.repos.d/kubernetes.repo
    cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/repodata/repomd.xml.key
    EOF
    
  1. Встановіть kubectl за допомогою yum:

    sudo yum install -y kubectl
    

  1. Додайте репозиторій Kubernetes zypper. Якщо ви хочете використовувати версію Kubernetes, відмінну від v1.31, замініть v1.31 на потрібну мінорну версію в команді нижче.

    # Це перезапише будь-яку існуючу конфігурацію у /etc/zypp/repos.d/kubernetes.repo
    cat <<EOF | sudo tee /etc/zypp/repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/repodata/repomd.xml.key
    EOF
    
  1. Оновіть zypper і підтвердіть додавання нового репозиторію:

    sudo zypper update
    

    Коли зʼявиться таке повідомлення, натисніть 't' або 'a':

    New repository or package signing key received:
    
    Repository:       Kubernetes
    Key Fingerprint:  1111 2222 3333 4444 5555 6666 7777 8888 9999 AAAA
    Key Name:         isv:kubernetes OBS Project <isv:kubernetes@build.opensuse.org>
    Key Algorithm:    RSA 2048
    Key Created:      Thu 25 Aug 2022 01:21:11 PM -03
    Key Expires:      Sat 02 Nov 2024 01:21:11 PM -03 (expires in 85 days)
    Rpm Name:         gpg-pubkey-9a296436-6307a177
    
    Note: Signing data enables the recipient to verify that no modifications occurred after the data
    were signed. Accepting data with no, wrong or unknown signature can lead to a corrupted system
    and in extreme cases even to a system compromise.
    
    Note: A GPG pubkey is clearly identified by its fingerprint. Do not rely on the key's name. If
    you are not sure whether the presented key is authentic, ask the repository provider or check
    their web site. Many providers maintain a web page showing the fingerprints of the GPG keys they
    are using.
    
    Do you want to reject the key, trust temporarily, or trust always? [r/t/a/?] (r): a
    
  2. Встановіть kubectl, використовуючи zypper:

    sudo zypper install -y kubectl
    

Встановлення за допомогою іншого пакетного менеджера

Якщо ви користуєтеся Ubuntu або іншим дистрибутивом Linux, який підтримує менеджер пакунків snap, kubectl доступний як застосунок snap.

snap install kubectl --classic
kubectl version --client

Якщо ви користуєтеся Linux і використовуєте пакетний менеджер Homebrew, kubectl доступний для встановлення.

brew install kubectl
kubectl version --client

Перевірка конфігурації Verify

Щоб kubectl знайшов та отримав доступ до кластера Kubernetes, вам потрібен файл kubeconfig, який створюється автоматично при створенні кластера за допомогою kube-up.sh або успішного розгортання кластера Minikube. Типово конфігурація kubectl знаходиться в ~/.kube/config.

Перевірте, що kubectl належним чином налаштований, отримавши стан кластера:

kubectl cluster-info

Якщо ви бачите у відповідь URL, kubectl налаштований на доступ до вашого кластера.

Якщо ви бачите повідомлення, подібне до наведеного нижче, kubectl не налаштований належним чином або не може приєднатися до кластера Kubernetes.

The connection to the server <server-name:port> was refused - did you specify the right host or port?

Наприклад, якщо ви плануєте запустити кластер Kubernetes на своєму ноутбуці (локально), вам спочатку потрібно встановити інструмент, такий як Minikube, а потім повторно виконати вказані вище команди.

Якщо kubectl cluster-info повертає у відповідь URL, але ви не можете отримати доступ до свого кластера, щоб перевірити, чи він налаштований належним чином, використовуйте:

kubectl cluster-info dump

Усунення несправностей повідомлення про помилку 'No Auth Provider Found'

У Kubernetes 1.26, kubectl видалив вбудовану автентифікацію для Kubernetes-кластерів керованих хмарними провайдерами. Ці провайдери випустили втулок для kubectl для надання хмарно-специфічної автентифікації. Для інструкцій див. документацію відповідного провайдера:

(Також можуть бути інші причини для показу цього повідомлення про помилку, не повʼязані з цією зміною.)

Необовʼязкові налаштування та втулки kubectl

Увімкнення функціонала автодоповнення оболонки

kubectl надає підтримку автодоповнення для оболонок Bash, Zsh, Fish та PowerShell, що може зекономити вам багато часу на набір тексту.

Нижче наведені процедури для налаштування автодоповнення для оболонок Bash, Fish та Zsh.

Вступ

Сценарій автодоповнення kubectl для Bash може бути створений за допомогою команди kubectl completion bash. Підключення цього сценарію у вашій оболонці дозволить вам мати автодоповнення для kubectl.

Однак сценарій залежить від bash-completion, що означає, що спочатку вам потрібно встановити цей скрипт (ви можете перевірити, чи вже встановлено bash-completion, виконавши команду type _init_completion).

Встановлення bash-completion

bash-completion надається багатьма менеджерами пакунків (див. тут). Ви можете встановити його за допомогою apt-get install bash-completion або yum install bash-completion тощо.

Вищевказані команди створюють /usr/share/bash-completion/bash_completion, який є основним сценарієм bash-completion. Залежно від вашого менеджера пакунків, вам доведеться вручну додати цей файл у ваш ~/.bashrc файл.

Щоб дізнатися, перезавантажте вашу оболонку і виконайте type _init_completion. Якщо команда виконується успішно, значить, ви вже його встановили, інакше додайте наступне до вашого ~/.bashrc файлу:

source /usr/share/bash-completion/bash_completion

Перезавантажте вашу оболонку і перевірте, чи bash-completion правильно встановлено, ввівши type _init_completion.

Увімкнення автодоповнення kubectl

Bash

Тепер вам потрібно переконатися, що сценарій автодоповнення kubectl підключений у всіх ваших сеансах оболонки. Є два способи, якими це можна зробити:


echo 'source <(kubectl completion bash)' >>~/.bashrc


kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
sudo chmod a+r /etc/bash_completion.d/kubectl

Якщо у вас є аліас для kubectl, ви можете розширити автодоповнення оболонки, щоб воно працювало з ним:

echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -o default -F __start_kubectl k' >>~/.bashrc

Обидва підходи еквівалентні. Після перезавантаження вашої оболонки автодоповнення kubectl повинно працювати. Щоб увімкнути автодоповнення bash у поточному сеансі оболонки, переініціалізуйте файл ~/.bashrc:

source ~/.bashrc

Сценарій автозавершення kubectl для Fish може бути створений за допомогою команди kubectl completion fish. Підключення цього сценарію автозавершення у вашій оболонці вмикає автодоповнення для kubectl.

Щоб зробити це у всіх сеансах вашої оболонки, додайте наступний рядок до вашого файлу ~/.config/fish/config.fish:

kubectl completion fish | source

Після перезавантаження вашої оболонки, автодоповнення kubectl повинно працювати.

Сценарій автозавершення kubectl для Zsh може бути створений за допомогою команди kubectl completion zsh. Підключення цього сценарію автозавершення у вашій оболонці дозволяє ввімкнути автодоповнення для kubectl.

Щоб зробити це у всіх сеансах вашої оболонки, додайте наступне до вашого файлу ~/.zshrc:

source <(kubectl completion zsh)

Якщо у вас є аліас для kubectl, автодоповнення kubectl автоматично працюватиме з ним.

Після перезавантаження вашої оболонки автодоповнення kubectl повинно працювати.

Якщо ви отримуєте помилку типу 2: command not found: compdef, то додайте наступне до початку вашого файлу ~/.zshrc:

autoload -Uz compinit
compinit

Встановлення втулка kubectl convert

Втулок для командного рядка Kubernetes kubectl, який дозволяє конвертувати маніфести між різними версіями API. Це може бути особливо корисно для міграції маніфестів до версій API, які все ще є актуальними, на новіші випуски Kubernetes. Для отримання додаткової інформації відвідайте перехід на актуальні API.

  1. Завантажте останній випуск за допомогою наступної команди:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl-convert"
       
  2. Перевірте бінарний файл (опціонально)

    Завантажте файл контрольної суми для kubectl-convert:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl-convert.sha256"
       

    Перевірте бінарний файл kubectl-convert за допомогою файлу контрольної суми:

    echo "$(cat kubectl-convert.sha256) kubectl-convert" | sha256sum --check
    

    Якщо перевірка пройшла успішно, вивід буде таким:

    kubectl-convert: OK
    

    Якщо перевірка не вдалася, sha256 виходить з ненульовим статусом і виводить подібне повідомлення:

    kubectl-convert: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    
  3. Встановіть kubectl-convert

    sudo install -o root -g root -m 0755 kubectl-convert /usr/local/bin/kubectl-convert
    
  4. Перевірте, чи втулок успішно встановлено

    kubectl convert --help
    

    Якщо ви не бачите помилку, це означає, що втулок успішно встановлено.

  5. Після встановлення втулка, вилучіть файли встановлення:

    rm kubectl-convert kubectl-convert.sha256
    

Що далі

1.2 - Встановлення та налаштування kubectl у macOS

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

Вам потрібно використовувати версію kubectl, яка має мінорну версію що відрізняється не більше ніж на одиницю від мінорної версії вашого кластера. Наприклад, клієнт v1.31 може співпрацювати з панелями управління v1.30, v1.31 та v1.32. Використання останньої сумісної версії kubectl допомагає уникнути непередбачуваних проблем.

Встановлення kubectl у macOS

Існують наступні методи встановлення kubectl у macOS:

Встановлення бінарника kubectl з curl у macOS

  1. Завантажте останнє видання:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
       
  2. Перевірте бінарний файл (опціонально)

    Завантажте файл контрольної суми для kubectl:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl.sha256"
       

    Перевірте бінарний файл kubectl за допомогою файлу контрольної суми:

    echo "$(cat kubectl.sha256)  kubectl" | shasum -a 256 --check
    

    Якщо перевірка пройшла успішно, вивід буде таким:

    kubectl: OK
    

    Якщо перевірка не вдалася, shasum виходить з ненульовим статусом і виводить подібне повідомлення:

    kubectl: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  3. Зробіть бінарний файл kubectl виконуваним.

    chmod +x ./kubectl
    
  4. Перемістіть бінарний файл kubectl до розташування файлу на вашій системі PATH.

    sudo mv ./kubectl /usr/local/bin/kubectl
    sudo chown root: /usr/local/bin/kubectl
    
  5. Перевірте, що встановлена версія kubectl актуальна:

    kubectl version --client
    

    Або використовуйте це для детального перегляду версії:

    kubectl version --client --output=yaml
    
  6. Після встановлення та перевірки kubectl видаліть файл контрольної суми:

    rm kubectl.sha256
    

Встановлення за допомогою Homebrew у macOS

Якщо ви користуєтеся macOS і пакетним менеджером Homebrew, ви можете встановити kubectl за допомогою Homebrew.

  1. Виконайте команду встановлення:

    brew install kubectl
    

    або

    brew install kubernetes-cli
    
  2. Перевірте, що встановлена версія актуальна:

    kubectl version --client
    

Встановлення за допомогою Macports у macOS

Якщо ви користуєтеся macOS і пакетним менеджером Macports, ви можете встановити kubectl за допомогою Macports.

  1. Виконайте команду встановлення:

    sudo port selfupdate
    sudo port install kubectl
    
  2. Перевірте, що встановлена версія актуальна:

    kubectl version --client
    

Перевірка конфігурації kubectl

Щоб kubectl знайшов та отримав доступ до кластера Kubernetes, вам потрібен файл kubeconfig, який створюється автоматично при створенні кластера за допомогою kube-up.sh або успішного розгортання кластера Minikube. Типово конфігурація kubectl знаходиться в ~/.kube/config.

Перевірте, що kubectl належним чином налаштований, отримавши стан кластера:

kubectl cluster-info

Якщо ви бачите у відповідь URL, kubectl налаштований на доступ до вашого кластера.

Якщо ви бачите повідомлення, подібне до наведеного нижче, kubectl не налаштований належним чином або не може приєднатися до кластера Kubernetes.

The connection to the server <server-name:port> was refused - did you specify the right host or port?

Наприклад, якщо ви плануєте запустити кластер Kubernetes на своєму ноутбуці (локально), вам спочатку потрібно встановити інструмент, такий як Minikube, а потім повторно виконати вказані вище команди.

Якщо kubectl cluster-info повертає у відповідь URL, але ви не можете отримати доступ до свого кластера, щоб перевірити, чи він налаштований належним чином, використовуйте:

kubectl cluster-info dump

Усунення несправностей повідомлення про помилку 'No Auth Provider Found'

У Kubernetes 1.26, kubectl видалив вбудовану автентифікацію для Kubernetes-кластерів керованих хмарними провайдерами. Ці провайдери випустили втулок для kubectl для надання хмарно-специфічної автентифікації. Для інструкцій див. документацію відповідного провайдера:

(Також можуть бути інші причини для показу цього повідомлення про помилку, не повʼязані з цією зміною.)

Опціональне налаштування kubectl та втулка

Увімкнення автопідстановки оболонки

kubectl надає підтримку автодоповнення для оболонок Bash, Zsh, Fish та PowerShell, що може значно зекономити ваш час при введенні команд.

Нижче подані процедури для налаштування автодоповнення для оболонок Bash, Fish та Zsh.

Вступ

Сценарій автодоповнення для Bash kubectl можна згенерувати за допомогою команди kubectl completion bash. Підключення цього сценарію в вашій оболонці дозволяє використовувати автодоповнення для kubectl.

Проте, сценарій автодоповнення для kubectl залежить від bash-completion, який потрібно встановити заздалегідь.

Оновлення Bash

Інструкції тут передбачають, що ви використовуєте Bash 4.1+. Ви можете перевірити версію свого Bash, виконавши:

echo $BASH_VERSION

Якщо вона є занадто старою, ви можете встановити/оновити її за допомогою Homebrew:

brew install bash

Перезавантажте вашу оболонку і перевірте, що використовується бажана версія:

echo $BASH_VERSION $SHELL

Зазвичай Homebrew встановлює його в /usr/local/bin/bash.

Встановлення bash-completion

Ви можете перевірити, чи вже встановлена bash-completion v2, використавши команду type _init_completion. Якщо ні, ви можете встановити її за допомогою Homebrew:

brew install bash-completion@2

Як зазначено в виводі цієї команди, додайте наступне до вашого файлу ~/.bash_profile:

brew_etc="$(brew --prefix)/etc" && [[ -r "${brew_etc}/profile.d/bash_completion.sh" ]] && . "${brew_etc}/profile.d/bash_completion.sh"

Перезавантажте вашу оболонку і перевірте, що bash-completion v2 встановлена коректно за допомогою type _init_completion.

Активація автодоповнення для kubectl

Тепер вам потрібно забезпечити, щоб сценарій автодоповнення для kubectl підключався в усіх ваших сеансах оболонки. Існують кілька способів досягнення цього:

  • Підключіть сценарій автодоповнення до вашого файлу ~/.bash_profile:

    echo 'source <(kubectl completion bash)' >>~/.bash_profile
    
  • Додайте сценарій автодоповнення до теки /usr/local/etc/bash_completion.d:

    kubectl completion bash >/usr/local/etc/bash_completion.d/kubectl
    
  • Якщо у вас є аліас для kubectl, ви можете розширити автодоповнення оболонки, щоб воно працювало з цим аліасом:

    echo 'alias k=kubectl' >>~/.bash_profile
    echo 'complete -o default -F __start_kubectl k' >>~/.bash_profile
    
  • Якщо ви встановили kubectl за допомогою Homebrew (як пояснено тут), то сценарій автодоповнення для kubectl вже повинен знаходитися у /usr/local/etc/bash_completion.d/kubectl. У цьому випадку вам нічого робити не потрібно.

У будь-якому випадку, після перезавантаження оболонки, автодоповнення для kubectl повинно працювати.

Сценарій автозавершення kubectl для Fish може бути створений за допомогою команди kubectl completion fish. Підключення цього сценарію автозавершення у вашій оболонці вмикає автодоповнення для kubectl.

Щоб зробити це у всіх сеансах вашої оболонки, додайте наступний рядок до вашого файлу ~/.config/fish/config.fish:

kubectl completion fish | source

Після перезавантаження вашої оболонки, автодоповнення kubectl повинно працювати.

Сценарій автозавершення kubectl для Zsh може бути створений за допомогою команди kubectl completion zsh. Підключення цього сценарію автозавершення у вашій оболонці дозволяє ввімкнути автодоповнення для kubectl.

Щоб зробити це у всіх сеансах вашої оболонки, додайте наступне до вашого файлу ~/.zshrc:

source <(kubectl completion zsh)

Якщо у вас є аліас для kubectl, автодоповнення kubectl автоматично працюватиме з ним.

Після перезавантаження вашої оболонки автодоповнення kubectl повинно працювати.

Якщо ви отримуєте помилку типу 2: command not found: compdef, то додайте наступне до початку вашого файлу ~/.zshrc:

autoload -Uz compinit
compinit

Встановлення втулка kubectl convert

Втулок для командного рядка Kubernetes kubectl, який дозволяє конвертувати маніфести між різними версіями API. Це може бути особливо корисно для міграції маніфестів до версій API, які все ще є актуальними, на новіші випуски Kubernetes. Для отримання додаткової інформації відвідайте перехід на актуальні API.

  1. Завантажте останній випуск команди:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert"
       
  2. Перевірте двійковий файл (опціонально):

    Завантажте файл контрольної суми для kubectl-convert:

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl-convert.sha256"
       

    
       curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl-convert.sha256"
       

    Перевірте двійковий файл kubectl-convert за допомогою файлу контрольної суми:

    echo "$(cat kubectl-convert.sha256)  kubectl-convert" | shasum -a 256 --check
    

    Якщо перевірка пройшла успішно, вивід буде таким:

    kubectl-convert: OK
    

    Якщо перевірка не вдалася, shasum виходить з ненульовим статусом і виводить подібне повідомлення:

    kubectl-convert: FAILED
    shasum: WARNING: 1 computed checksum did NOT match
    
  3. Зробіть двійковий файл kubectl-convert виконуваним.

    chmod +x ./kubectl-convert
    
  4. Перемістіть двійковий файл kubectl-convert до розташування файлу у вашій системі PATH.

    sudo mv ./kubectl-convert /usr/local/bin/kubectl-convert
    sudo chown root: /usr/local/bin/kubectl-convert
    
  5. Перевірте, що втулок було встановлено:

    kubectl convert --help
    

    Якщо ви не бачите жодних помилок, втулок було встановлено успішно.

  6. Після встановлення та перевірки kubectl-convert видаліть файл контрольної суми:

    rm kubectl-convert kubectl-convert.sha256
    

Видалення kubectl

Залежно від того, як ви встановили kubectl, використовуйте один з наступних методів.

Видалення kubectl за допомогою командного рядка

  1. Знайдіть бінарний файл kubectl у вашій системі:

    which kubectl
    
  2. Видаліть бінарний файл kubectl:

    sudo rm <path>
    

    Замініть <path> на шлях до бінарного файлу kubectl з попереднього кроку. Наприклад, sudo rm /usr/local/bin/kubectl.

Видалення kubectl за допомогою Homebrew

Якщо ви встановили kubectl за допомогою Homebrew, виконайте наступну команду:

brew remove kubectl

Що далі

1.3 - Встановлення та налаштування kubectl у Windows

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

Вам потрібно використовувати версію kubectl, яка має мінорну версію що відрізняється не більше ніж на одиницю від мінорної версії вашого кластера. Наприклад, клієнт v1.31 може співпрацювати з панелями управління v1.30, v1.31 та v1.32. Використання останньої сумісної версії kubectl допомагає уникнути непередбачуваних проблем.

Встановлення kubectl у Windows

Існують наступні методи встановлення kubectl у Windows:

Встановлення бінарника kubectl у Windows (за допомогою прямого завантаження або за допомогою curl)

  1. У вас є два варіанти встановлення kubectl на вашому пристрої з Windows

    • Безпосереднє завантаження:

      Завантажте останню версію 1.31 патчу безпосередньо для вашої архітектури, відвідавши сторінку випуску Kubernetes. Переконайтеся, що вибрано правильний двійковий файл для вашої архітектури (наприклад, amd64, arm64 тощо).

    • Використовуючи curl

      Або, якщо у вас встановлено curl, використовуйте цю команду:

      curl.exe -LO "https://dl.k8s.io/release/v1.31.0/bin/windows/amd64/kubectl.exe"
      
  2. Перевірте бінарний файл (опціонально)

    Завантажте файл контрольної суми для kubectl:

    curl.exe -LO "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubectl.exe.sha256"
    

    Перевірте бінарний файл kubectl за допомогою файлу контрольної суми:

    • Використовуючи командний рядок для ручного порівняння виводу CertUtil з завантаженим файлом контрольної суми:

      CertUtil -hashfile kubectl.exe SHA256
      type kubectl.exe.sha256
      
    • Використовуючи PowerShell для автоматизації перевірки за допомогою оператора -eq для отримання результату True або False:

      Compare-String -eq (Get-FileHash kubectl.exe -Algorithm SHA256).Hash (Get-Content kubectl.exe.sha256)
      
  3. Додайте на початок чи в кінець змінної середовища PATH шлях до теки з kubectl.

  4. Перевірте, що версія kubectl збігається з завантаженою:

    kubectl version --client
    

    Або використайте це для детального перегляду версії:

    kubectl version --client --output=yaml
    

Встановлення на Windows за допомогою Chocolatey, Scoop або winget

  1. Для встановлення kubectl у Windows ви можете використовувати пакетний менеджер Chocolatey, командний інсталятор Scoop, або менеджер пакунків winget.

    choco install kubernetes-cli
    

    scoop install kubectl
    

    winget install -e --id Kubernetes.kubectl
    
  2. Перевірте, що встановлена версія оновлена:

    kubectl version --client
    
  3. Перейдіть до вашого домашнього каталогу:

    # Якщо ви використовуєте cmd.exe, виконайте: cd %USERPROFILE%
    cd ~
    
  4. Створіть каталог .kube:

    mkdir .kube
    
  5. Перейдіть до каталогу .kube, що ви щойно створили:

    cd .kube
    
  6. Налаштуйте kubectl для використання віддаленого кластера Kubernetes:

    New-Item config -type file
    

Перевірка конфігурації kubectl

Щоб kubectl знайшов та отримав доступ до кластера Kubernetes, вам потрібен файл kubeconfig, який створюється автоматично при створенні кластера за допомогою kube-up.sh або успішного розгортання кластера Minikube. Типово конфігурація kubectl знаходиться в ~/.kube/config.

Перевірте, що kubectl належним чином налаштований, отримавши стан кластера:

kubectl cluster-info

Якщо ви бачите у відповідь URL, kubectl налаштований на доступ до вашого кластера.

Якщо ви бачите повідомлення, подібне до наведеного нижче, kubectl не налаштований належним чином або не може приєднатися до кластера Kubernetes.

The connection to the server <server-name:port> was refused - did you specify the right host or port?

Наприклад, якщо ви плануєте запустити кластер Kubernetes на своєму ноутбуці (локально), вам спочатку потрібно встановити інструмент, такий як Minikube, а потім повторно виконати вказані вище команди.

Якщо kubectl cluster-info повертає у відповідь URL, але ви не можете отримати доступ до свого кластера, щоб перевірити, чи він налаштований належним чином, використовуйте:

kubectl cluster-info dump

Усунення несправностей повідомлення про помилку 'No Auth Provider Found'

У Kubernetes 1.26, kubectl видалив вбудовану автентифікацію для Kubernetes-кластерів керованих хмарними провайдерами. Ці провайдери випустили втулок для kubectl для надання хмарно-специфічної автентифікації. Для інструкцій див. документацію відповідного провайдера:

(Також можуть бути інші причини для показу цього повідомлення про помилку, не повʼязані з цією зміною.)

Опціональні конфігурації та втулки kubectl

Увімкнення автопідстановки оболонки

kubectl надає підтримку автодоповнення для оболонок Bash, Zsh, Fish та PowerShell, що може значно зекономити ваш час при введенні команд.

Нижче подані процедури для налаштування автодоповнення для PowerShell.

Скрипт автодоповнення для kubectl в PowerShell можна згенерувати командою kubectl completion powershell.

Для того, щоб це працювало у всіх сесіях вашої оболонки, додайте наступний рядок до вашого файлу $PROFILE:

kubectl completion powershell | Out-String | Invoke-Expression

Ця команда буде генерувати скрипт автодоповнення при кожному запуску PowerShell. Ви також можете додати згенерований скрипт безпосередньо до вашого файлу $PROFILE.

Щоб додати згенерований скрипт до вашого файлу $PROFILE, виконайте наступний рядок у вашому PowerShell:

kubectl completion powershell >> $PROFILE

Після перезавантаження вашої оболонки автодоповнення для kubectl має працювати.

Встановлення втулка kubectl convert

Втулок для командного рядка Kubernetes kubectl, який дозволяє конвертувати маніфести між різними версіями API. Це може бути особливо корисно для міграції маніфестів до версій API, які все ще є актуальними, на новіші випуски Kubernetes. Для отримання додаткової інформації відвідайте перехід на актуальні API.

  1. Завантажте останній випуск команди:

    curl.exe -LO "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubectl-convert.exe"
    
  2. Перевірте двійковий файл (опціонально).

    Завантажте файл контрольної суми для kubectl-convert:

    curl.exe -LO "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubectl-convert.exe.sha256"
    

    Перевірте бінарний файл kubectl-convert за допомогою файлу контрольної суми:

    • Використовуючи командний рядок для ручного порівняння виводу CertUtil з завантаженим файлом контрольної суми:

      CertUtil -hashfile kubectl-convert.exe SHA256
      type kubectl-convert.exe.sha256
      
    • Використовуючи PowerShell для автоматизації перевірки за допомогою оператора -eq для отримання результату True або False:

      Compare-String -eq (Get-FileHash kubectl-convert.exe -Algorithm SHA256).Hash (Get-Content kubectl-convert.exe.sha256)
      
  3. Додайте на початок чи в кінець змінної середовища PATH шлях до теки з kubectl-convert.

  4. Перевірте, що версія kubectl-convert збігається з завантаженою:

    kubectl-convert version
    

    Або використайте це для детального перегляду версії:

    kubectl-convert version --output=yaml
    
  5. Після встановлення втулка, вилучіть інсталяційні файли:

    Remove-Item kubectl-convert.exe
    Remove-Item kubectl-convert.exe.sha256
    

Що далі

2 - Адміністрування кластера

Дізнайтеся про загальні завдання адміністрування кластера.

2.1 - Адміністрування з kubeadm

Якщо у вас немає кластера, зверніться до сторінки Запуск кластерів з kubeadm.

Завдання в цьому розділі призначені для адміністрування наявного кластера:

2.1.1 - Додавання робочих вузлів Linux

Ця сторінка пояснює, як додати робочі вузли Linux до кластера, створеного за допомогою kubeadm.

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

Додавання робочих вузлів Linux

Щоб додати нові робочі вузли Linux до кластера, виконайте наступне для кожної машини:

  1. Підʼєднатесь до машини за допомогою SSH або іншого методу.
  2. Запустіть команду, яка була виведена kubeadm init. Наприклад:
sudo kubeadm join \
  --token <token> <control-plane-host>:<control-plane-port> \
  --discovery-token-ca-cert-hash sha256:<hash>

Додаткова інформація для kubeadm join

Якщо у вас немає токена, ви можете отримати його, запустивши наступну команду на вузлі панелі управління:

# Запустіть це на вузлі панелі управління
sudo kubeadm token list

Вивід буде приблизно таким:

TOKEN                    TTL  EXPIRES              USAGES           DESCRIPTION            EXTRA GROUPS
8ewj1p.9r9hcjoqgajrj4gi  23h  2018-06-12T02:51:28Z authentication,  The default bootstrap  system:
                                                   signing          token generated by     bootstrappers:
                                                                    'kubeadm init'.        kubeadm:
                                                                                           default-node-token

Стандартно токени для приєднання вузлів мають термін дії 24 години. Якщо ви додаєте вузол до кластера після того, як поточний токен закінчився, ви можете створити новий токен, виконавши наступну команду на вузлі панелі управління:

# Запустіть це на вузлі панелі управління
sudo kubeadm token create

Вивід буде приблизно таким:

5didvk.d09sbcov8ph2amjw

Якщо у вас немає значення --discovery-token-ca-cert-hash, ви можете отримати його, виконавши наступні команди на вузлі панелі управління:

# Запустіть це на вузлі панелі управління
sudo cat /etc/kubernetes/pki/ca.crt | \
  openssl x509 -pubkey  | \
  openssl rsa -pubin -outform der 2>/dev/null | \
  openssl dgst -sha256 -hex | \
  sed 's/^.* //'

Вивід буде приблизно таким:

8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78

Вивід команди kubeadm join має виглядати приблизно так:

[preflight] Running pre-flight checks

... (вивід процесу приєднання) ...

Node join complete:
* Запит на підписання сертифікату надіслано до панелі управління, отримано відповідь.
* Kubelet проінформований про нові деталі безпечного зʼєднання.

Запустіть 'kubectl get nodes' на панелі управління, щоб побачити приєднання цього вузла.

Через кілька секунд ви повинні побачити цей вузол у виводі команди kubectl get nodes. (наприклад, запустіть kubectl на вузлі панелі управління).

Що далі

2.1.2 - Додавання робочих вузлів Windows

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.18 [beta]

Ця сторінка пояснює, як додати робочі вузли Windows до кластера kubeadm.

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

  • Запущений екземпляр Windows Server 2022 (або новіший) з адміністративним доступом.
  • Запущений кластер kubeadm, створений за допомогою kubeadm init та з дотриманням кроків з документа Створення кластера з kubeadm.

Додавання робочих вузлів Windows

Виконайте наступні кроки для кожної машини:

  1. Відкрийте сесію PowerShell на машині.
  2. Переконайтеся, що ви є адміністратором або привілейованим користувачем.

Потім виконайте наведені нижче кроки.

Встановлення containerd

Щоб встановити containerd, спочатку виконайте наступну команду:

curl.exe -LO https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/hostprocess/Install-Containerd.ps1

Потім виконайте наступну команду, але спочатку замініть CONTAINERD_VERSION на нещодавній реліз з репозиторію containerd. Версія не повинна містити префікс v. Наприклад, використовуйте 1.7.22 замість v1.7.22:

.\Install-Containerd.ps1 -ContainerDVersion CONTAINERD_VERSION
  • Налаштуйте будь-які інші параметри для Install-Containerd.ps1, такі як netAdapterName, за необхідності.
  • Встановіть skipHypervisorSupportCheck, якщо ваша машина не підтримує Hyper-V і не може розміщувати контейнери ізольовані Hyper-V.
  • Якщо ви змінюєте необовʼязкові параметри CNIBinPath та/або CNIConfigPath у Install-Containerd.ps1, вам потрібно буде налаштувати встановлений втулок CNI Windows з відповідними значеннями.

Встановлення kubeadm і kubelet

Виконайте наступні команди для установки kubeadm і kubelet:

curl.exe -LO https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/hostprocess/PrepareNode.ps1
.\PrepareNode.ps1 -KubernetesVersion v1.31
  • Налаштуйте параметр KubernetesVersion у PrepareNode.ps1 за необхідності.

Запуск kubeadm join

Виконайте команду, з виводу kubeadm init. Наприклад:

kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>

Додаткова інформація про kubeadm join

Якщо у вас немає токена, ви можете отримати його, виконавши наступну команду на вузлі панелі управління:

# Виконайте це на вузлі панелі управління
sudo kubeadm token list

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

TOKEN                    TTL  EXPIRES              USAGES           DESCRIPTION            EXTRA GROUPS
8ewj1p.9r9hcjoqgajrj4gi  23h  2018-06-12T02:51:28Z authentication,  The default bootstrap  system:
                                                   signing          token generated by     bootstrappers:
                                                                    'kubeadm init'.        kubeadm:
                                                                                           default-node-token

Стандартно, токени приєднання вузлів діють 24 години. Якщо ви приєднуєте вузол до кластера після того, як токен закінчився, ви можете створити новий токен, виконавши наступну команду на вузлі панелі управління:

# Виконайте це на вузлі панелі управління
sudo kubeadm token create

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

5didvk.d09sbcov8ph2amjw

Якщо ви не маєте значення --discovery-token-ca-cert-hash, ви можете отримати його, виконавши наступні команди на вузлі панелі управління:

sudo cat /etc/kubernetes/pki/ca.crt | \
  openssl x509 -pubkey  | \
  openssl rsa -pubin -outform der 2>/dev/null | \
  openssl dgst -sha256 -hex | \
  sed 's/^.* //'

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

8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78

Вивід команди kubeadm join має виглядати приблизно так:

[preflight] Running pre-flight checks

... (вивід журналу процесу приєднання) ...

Приєднання вузла завершено:
* Запит на підпис сертифіката надіслано до панелі управління та отримано відповідь.
* Kubelet поінформований про нові деталі захищеного з'єднання.

Запустіть 'kubectl get nodes' на панелі управління, щоб побачити цей вузол.

Через кілька секунд ви повинні помітити цей вузол у виводі kubectl get nodes. (наприклад, виконайте kubectl на вузлі панелі управління).

Налаштування мережі

Налаштування CNI в кластерах, що містять як Linux, так і вузли Windows, вимагає більше кроків, ніж просто запуск kubectl apply з файлом маніфесту. Крім того, втулок CNI, що працює на вузлах панелі управління, повинен бути підготовлений для підтримки втулка CNI, що працює на робочих вузлах Windows.

Зараз лише кілька втулків CNI підтримують Windows. Нижче наведені інструкції для їх налаштування:

Встановлення kubectl для Windows (необовʼязково)

Дивіться Встановлення та налаштування kubectl у Windows.

Що далі

2.1.3 - Оновлення кластерів з kubeadm

Ця сторінка пояснює, як оновити кластер Kubernetes, створений за допомогою kubeadm, з версії 1.30.x до версії 1.31.x і з версії 1.31.x до 1.31.y (де y > x). Пропуск МІНОРНИХ версій при оновленні не підтримується. Для отримання додаткових відомостей відвідайте Політику версій зміни.

Щоб переглянути інформацію про оновлення кластерів, створених за допомогою старіших версій kubeadm, зверніться до наступних сторінок:

Процес оновлення загалом виглядає наступним чином:

  1. Оновлення первинного вузла панелі управління.
  2. Оновлення додаткових вузлів панелі управління.
  3. Оновлення вузлів робочого навантаження.

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

  • Переконайтеся, що ви уважно прочитали примітки до випуску.
  • Кластер повинен використовувати статичні вузли керування та контейнери etcd або зовнішній etcd.
  • Переконайтеся, що ви зробили резервне копіювання важливих компонентів, таких як стан на рівні застосунків, збережений у базі даних. kubeadm upgrade не торкнеться вашого робочого навантаження, лише компонентів, внутрішніх для Kubernetes, але резервне копіювання завжди є найкращою практикою.
  • Своп має бути вимкнено.

Додаткова інформація

  • Наведені нижче інструкції описують, коли потрібно вивести з експлуатації кожний вузол під час процесу оновлення. Якщо ви виконуєте оновлення для мінорного номера версії для будь-якого kubelet, ви обовʼязково спочатку повинні вивести вузол (або вузли) з експлуатації, які ви оновлюєте. У випадку вузлів панелі управління, на них можуть працювати контейнери CoreDNS або інші критичні робочі навантаження. Для отримання додаткової інформації дивіться Виведення вузлів з експлуатації.
  • Проєкт Kubernetes рекомендує щоб версії kubelet і kubeadm збігались. Замість цього ви можете використовувати версію kubelet, яка є старішою, ніж kubeadm, за умови, що вона знаходиться в межах підтримуваних версій. Для отримання додаткових відомостей, будь ласка, відвідайте Відхилення kubeadm від kubelet.
  • Всі контейнери перезавантажуються після оновлення, оскільки змінюється значення хешу специфікації контейнера.
  • Щоб перевірити, що служба kubelet успішно перезапустилась після оновлення kubelet, ви можете виконати systemctl status kubelet або переглянути логи служби за допомогою journalctl -xeu kubelet.
  • kubeadm upgrade підтримує параметр --config із типом API UpgradeConfiguration, який можна використовувати для налаштування процесу оновлення.
  • kubeadm upgrade не підтримує переналаштування наявного кластера. Замість цього виконайте кроки, описані в Переналаштування кластера kubeadm.

Що треба враховувати при оновленні etcd

Оскільки статичний Pod kube-apiserver працює постійно (навіть якщо ви вивели вузол з експлуатації), під час виконання оновлення kubeadm, яке включає оновлення etcd, запити до сервера зупиняться, поки новий статичний Pod etcd не перезапуститься. Як обхідний механізм, можна активно зупинити процес kube-apiserver на кілька секунд перед запуском команди kubeadm upgrade apply. Це дозволяє завершити запити, що вже відправлені, і закрити наявні зʼєднання, що знижує наслідки перерви роботи etcd. Це можна зробити на вузлах панелі управління таким чином:

killall -s SIGTERM kube-apiserver # виклик належного припинення роботи kube-apiserver
sleep 20 # зачекайте трохи, щоб завершити запити, які вже були відправлені
kubeadm upgrade ... # виконати команду оновлення kubeadm

Зміна репозиторію пакунків

Якщо ви використовуєте репозиторії пакунків, що керуються спільнотою (pkgs.k8s.io), вам потрібно увімкнути репозиторій пакунків для бажаної мінорної версії Kubernetes. Як це зробити можна дізнатись з документа Зміна репозиторію пакунків Kubernetes.

Визначення версії, на яку потрібно оновитися

Знайдіть останнє патч-видання для Kubernetes 1.31 за допомогою менеджера пакунків ОС:

# Знайдіть останню версію 1.31 у списку.
# Вона має виглядати як 1.31.x-*, де x — останній патч.
sudo apt update
sudo apt-cache madison kubeadm

# Знайдіть останню версію 1.31 у списку.
# Вона має виглядати як 1.31.x-*, де x — останній патч.
sudo yum list --showduplicates kubeadm --disableexcludes=kubernetes

Оновлення вузлів панелі управління

Процедуру оновлення на вузлах панелі управління слід виконувати по одному вузлу за раз. Виберіть перший вузол панелі управління, який ви хочете оновити. Він повинен мати файл /etc/kubernetes/admin.conf.

Here's the translation:

Виклик "kubeadm upgrade"

Для першого вузла панелі управління

  1. Оновіть kubeadm:

    # замініть x на останню версію патча
    sudo apt-mark unhold kubeadm && \
    sudo apt-get update && sudo apt-get install -y kubeadm='1.31.x-*' && \
    sudo apt-mark hold kubeadm
    

    # замініть x на останню версію патча
    sudo yum install -y kubeadm-'1.31.x-*' --disableexcludes=kubernetes
    
  2. Перевірте, що завантаження працює і має очікувану версію:

    kubeadm version
    
  3. Перевірте план оновлення:

    sudo kubeadm upgrade plan
    

    Ця команда перевіряє можливість оновлення вашого кластера та отримує версії, на які ви можете оновитися. Також вона показує таблицю стану версій компонентів.

  4. Виберіть версію для оновлення та запустіть відповідну команду. Наприклад:

    # замініть x на версію патча, яку ви вибрали для цього оновлення
    sudo kubeadm upgrade apply v1.31.x
    

    Після завершення команди ви маєте побачити:

    [upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.31.x". Enjoy!
    
    [upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.
    
  5. Вручну оновіть втулок постачальник мережевого інтерфейсу контейнера (CNI).

    Ваш постачальник мережевого інтерфейсу контейнера (CNI) може мати власні інструкції щодо оновлення. Перевірте надбудови для знаходження вашого постачальника CNI та перегляньте, чи потрібні додаткові кроки оновлення.

    Цей крок не потрібен на додаткових вузлах панелі управління, якщо постачальник CNI працює як DaemonSet.

Для інших вузлів панелі управління

Те саме, що для першого вузла керування, але використовуйте:

sudo kubeadm upgrade node

замість:

sudo kubeadm upgrade apply

Також виклик kubeadm upgrade plan та оновлення постачальника мережевого інтерфейсу контейнера (CNI) вже не потрібні.

Виведення вузла з експлуатації

Готуємо вузол для обслуговування, відмітивши його як непридатний для планування та вивівши з нього робочі навантаження:

# замініть <node-to-drain> іменем вашого вузла, який ви хочете вивести з експлуатації
kubectl drain <node-to-drain> --ignore-daemonsets

Оновлення kubelet та kubectl

  1. Оновіть kubelet та kubectl:

    # замініть x у 1.31.x-* на останню патч-версію
    sudo apt-mark unhold kubelet kubectl && \
    sudo apt-get update && sudo apt-get install -y kubelet='1.31.x-*' kubectl='1.31.x-*' && \
    sudo apt-mark hold kubelet kubectl
    

    # замініть x у 1.31.x-* на останню патч-версію
    sudo yum install -y kubelet-'1.31.x-*' kubectl-'1.31.x-*' --disableexcludes=kubernetes
    
  2. Перезапустіть kubelet:

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

Повернення вузла до експлуатації

Відновіть роботу вузла, позначивши його як доступний для планування:

# замініть <node-to-uncordon> на імʼя вашого вузла
kubectl uncordon <node-to-uncordon>

Оновлення вузлів робочих навантажень

Процедуру оновлення на робочих вузлах слід виконувати один за одним або декільком вузлами одночасно, не посягаючи на мінімально необхідні можливості для виконання вашого навантаження.

Наступні сторінки показують, як оновити робочі вузли у Linux та Windows:

Перевірка стану кластера

Після оновлення kubelet на всіх вузлах перевірте доступність всіх вузлів, виконавши наступну команду з будь-якого місця, де кubectl має доступу до кластера:

kubectl get nodes

У стовпці STATUS повинно бути вказано Ready для всіх ваших вузлів, а номер версії повинен бути оновлений.

Відновлення після несправності

Якщо kubeadm upgrade виявляється несправним і не відновлює роботу, наприклад через неочікуване вимкнення під час виконання, ви можете виконати kubeadm upgrade ще раз. Ця команда є ідемпотентною і, зрештою, переконується, що фактичний стан відповідає заданому вами стану.

Для відновлення з несправного стану ви також можете запустити sudo kubeadm upgrade apply --force без зміни версії, яку використовує ваш кластер.

Під час оновлення kubeadm записує наступні резервні теки у /etc/kubernetes/tmp:

  • kubeadm-backup-etcd-<дата>-<час>
  • kubeadm-backup-manifests-<дата>-<час>

kubeadm-backup-etcd містить резервну копію даних локального etcd для цього вузла панелі управління. У разі невдачі оновлення etcd і якщо автоматичне відновлення не працює, вміст цієї теки може бути відновлений вручну в /var/lib/etcd. У разі використання зовнішнього etcd ця тека резервного копіювання буде порожньою.

kubeadm-backup-manifests містить резервну копію файлів маніфестів статичних Podʼів для цього вузла панелі управління. У разі невдачі оновлення і якщо автоматичне відновлення не працює, вміст цієї теки може бути відновлений вручну в /etc/kubernetes/manifests. Якщо з будь-якої причини немає різниці між попереднім та файлом маніфесту після оновлення для певного компонента, резервна копія файлу для нього не буде записана.

Як це працює

kubeadm upgrade apply робить наступне:

  • Перевіряє, що ваш кластер можна оновити:
    • Сервер API доступний
    • Всі вузли знаходяться у стані Ready
    • Панель управління працює належним чином
  • Застосовує політику різниці версій.
  • Переконується, що образи панелі управління доступні або доступні для отримання на машині.
  • Генерує заміни та/або використовує зміни підготовлені користувачем, якщо компонентні конфігурації вимагають оновлення версії.
  • Оновлює компоненти панелі управління або відкочується, якщо будь-який з них не може бути запущений.
  • Застосовує нові маніфести CoreDNS і kube-proxy і переконується, що створені всі необхідні правила RBAC.
  • Створює нові сертифікати та файли ключів API-сервера і робить резервні копії старих файлів, якщо вони мають закінчитися за 180 днів.

kubeadm upgrade node робить наступне на додаткових вузлах панелі управління:

  • Витягує ClusterConfiguration kubeadm з кластера.
  • Опційно робить резервні копії сертифіката kube-apiserver.
  • Оновлює маніфести статичних Podʼів для компонентів панелі управління.
  • Оновлює конфігурацію kubelet для цього вузла.

kubeadm upgrade node робить наступне на вузлах робочих навантажень:

  • Витягує ClusterConfiguration kubeadm з кластера.
  • Оновлює конфігурацію kubelet для цього вузла.

2.1.4 - Оновлення вузлів Linux

Ця сторінка пояснює, як оновити вузли робочих навантажень Linux, створені за допомогою kubeadm.

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

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

Для перевірки версії введіть kubectl version.

Зміна репозиторію пакунків

Якщо ви використовуєте репозиторії пакунків (pkgs.k8s.io), вам потрібно увімкнути репозиторій пакунків для потрібного мінорного релізу Kubernetes. Це пояснено в документі Зміна репозиторію пакунків Kubernetes.

Оновлення робочих вузлів

Оновлення kubeadm

Оновіть kubeadm:

# замініть x у 1.31.x-* на останню версію патча
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.31.x-*' && \
sudo apt-mark hold kubeadm

# замініть x у 1.31.x-* на останню версію патча
sudo yum install -y kubeadm-'1.31.x-*' --disableexcludes=kubernetes

Виклик "kubeadm upgrade"

Для робочих вузлів це оновлює локальну конфігурацію kubelet:

sudo kubeadm upgrade node

Виведіть вузол з експлуатації

Підготуйте вузол до обслуговування, позначивши його як недоступний для планування та виселивши завдання:

# виконайте цю команду на вузлі панелі управління
# замініть <node-to-drain> імʼям вузла, який ви виводите з експлуатації
kubectl drain <node-to-drain> --ignore-daemonsets

Оновлення kubelet та kubectl

  1. Оновіть kubelet та kubectl:

    # замініть x у 1.31.x-* на останню версію патча
    sudo apt-mark unhold kubelet kubectl && \
    sudo apt-get update && sudo apt-get install -y kubelet='1.31.x-*' kubectl='1.31.x-*' && \
    sudo apt-mark hold kubelet kubectl
    

    # замініть x у 1.31.x-* на останню версію патча
    sudo yum install -y kubelet-'1.31.x-*' kubectl-'1.31.x-*' --disableexcludes=kubernetes
    
  2. Перезавантажте kubelet:

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet
    

Відновіть роботу вузла

Поверніть вузол в роботу, позначивши його як придатний для планування:

# виконайте цю команду на вузлі панелі управління
# замініть <node-to-uncordon> імʼям вашого вузла
kubectl uncordon <node-to-uncordon>

Що далі

2.1.5 - Оновлення вузлів Windows

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.18 [beta]

Ця сторінка пояснює, як оновити вузол Windows, створений за допомогою kubeadm.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.17. Для перевірки версії введіть kubectl version.

Оновлення робочих вузлів

Оновлення kubeadm

  1. З вузла Windows оновіть kubeadm:

    # замініть 1.31.0 на вашу бажану версію
    curl.exe -Lo <path-to-kubeadm.exe>  "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubeadm.exe"
    

Виведіть вузол з експлуатації

  1. З машини з доступом до API Kubernetes підготуйте вузол до обслуговування, позначивши його як недоступний для планування та виселивши завдання:

    # замініть <node-to-drain> імʼям вашого вузла, який ви виводите з експлуатації
    kubectl drain <node-to-drain> --ignore-daemonsets
    

    Ви повинні побачити подібний вивід:

    node/ip-172-31-85-18 cordoned
    node/ip-172-31-85-18 drained
    

Оновлення конфігурації kubelet

  1. З вузла Windows викличте наступну команду, щоб синхронізувати нову конфігурацію kubelet:

    kubeadm upgrade node
    

Оновлення kubelet та kube-proxy

  1. З вузла Windows оновіть та перезапустіть kubelet:

    stop-service kubelet
    curl.exe -Lo <path-to-kubelet.exe> "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kubelet.exe"
    restart-service kubelet
    
  2. З вузла Windows оновіть та перезапустіть kube-proxy.

    stop-service kube-proxy
    curl.exe -Lo <path-to-kube-proxy.exe> "https://dl.k8s.io/v1.31.0/bin/windows/amd64/kube-proxy.exe"
    restart-service kube-proxy
    

Відновіть роботу вузла

  1. З машини з доступом до API Kubernetes, поверніть вузол в роботу, позначивши його як придатний для планування:

    # замініть <node-to-drain> імʼям вашого вузла
    kubectl uncordon <node-to-drain>
    

Що далі

2.1.6 - Налаштування драйвера cgroup

Ця сторінка пояснює, як налаштувати драйвер cgroup kubelet, щоб він відповідав драйверу cgroup контейнера для кластерів kubeadm.

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

Вам слід ознайомитися з вимогами до контейнерних середовищ Kubernetes.

Налаштування драйвера cgroup середовища виконання контейнерів

Сторінка Середовища виконання контейнерів пояснює, що для налаштувань на основі kubeadm рекомендується використовувати драйвер systemd замість типового драйвера cgroupfs kubelet, оскільки kubeadm керує kubelet як сервісом systemd.

На сторінці також наведено деталі щодо того, як налаштувати різні контейнерні середовища зі стандартним використанням драйвера systemd.

Налаштування драйвера cgroup для kubelet

kubeadm дозволяє передавати структуру KubeletConfiguration під час ініціалізації за допомогою kubeadm init. Ця структура KubeletConfiguration може включати поле cgroupDriver, яке контролює драйвер cgroup для kubelet.

Ось мінімальний приклад, який явним чином вказує значення поля cgroupDriver:

# kubeadm-config.yaml
kind: ClusterConfiguration
apiVersion: kubeadm.k8s.io/v1beta4
kubernetesVersion: v1.21.0
---
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd

Такий файл конфігурації можна передати команді kubeadm:

kubeadm init --config kubeadm-config.yaml

Використання драйвера cgroupfs

Для використання cgroupfs і запобігання модифікації драйвера cgroup в KubeletConfiguration під час оновлення kubeadm в поточних налаштуваннях, вам потрібно явно вказати його значення. Це стосується випадку, коли ви не хочете, щоб майбутні версії kubeadm стандартно застосовували драйвер systemd.

Дивіться нижче розділ "Зміна ConfigMap у kubelet" для отримання деталей щодо явного вказання значення.

Якщо ви хочете налаштувати середовище виконання контейнерів на використання драйвера cgroupfs, вам слід звернутися до документації вашого середовища виконання контейнерів.

Міграція на використання драйвера systemd

Щоб змінити драйвер cgroup поточного кластера kubeadm з cgroupfs на systemd на місці, потрібно виконати подібну процедуру до оновлення kubelet. Це повинно включати обидва зазначені нижче кроки.

Зміна ConfigMap у kubelet

  • Викличте kubectl edit cm kubelet-config -n kube-system.

  • Змініть наявне значення cgroupDriver або додайте нове поле, яке виглядає наступним чином:

    cgroupDriver: systemd
    

    Це поле повинно бути присутнє у розділі kubelet: в ConfigMap.

Оновлення драйвера cgroup на всіх вузлах

Для кожного вузла в кластері:

  • Відключіть вузол за допомогою kubectl drain <імʼя-вузла> --ignore-daemonsets
  • Зупиніть kubelet за допомогою systemctl stop kubelet
  • Зупиніть середовище виконання контейнерів
  • Змініть драйвер cgroup середовища виконання контейнерів на systemd
  • Встановіть cgroupDriver: systemd у /var/lib/kubelet/config.yaml
  • Запустіть середовище виконання контейнерів
  • Запустіть kubelet за допомогою systemctl start kubelet
  • Увімкніть вузол за допомогою kubectl uncordon <імʼя-вузла>

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

Після завершення процесу переконайтеся, що всі вузли та робочі навантаження є справними.

2.1.7 - Управління сертифікатами з kubeadm

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.15 [stable]

Клієнтські сертифікати, що генеруються kubeadm, закінчуються через 1 рік. Ця сторінка пояснює, як управляти поновленням сертифікатів за допомогою kubeadm. Вона також охоплює інші завдання, повʼязані з управлінням сертифікатами kubeadm.

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

Ви повинні бути знайомі з сертифікатами PKI та вимогами Kubernetes.

Використання власних сертифікатів

Типово, kubeadm генерує всі необхідні сертифікати для роботи кластера. Ви можете перевизначити цю поведінку, надавши власні сертифікати.

Для цього вам потрібно помістити їх у ту теку, яка вказується за допомогою прапорця --cert-dir або поля certificatesDir конфігурації кластера ClusterConfiguration kubeadm. Типово це /etc/kubernetes/pki.

Якщо певна пара сертифікатів і приватний ключ існують до запуску kubeadm init, kubeadm не перезаписує їх. Це означає, що ви можете, наприклад, скопіювати наявний ЦС (Центр сертифікації — Certificate authority) в /etc/kubernetes/pki/ca.crt та /etc/kubernetes/pki/ca.key, і kubeadm використовуватиме цей ЦС для підпису решти сертифікатів.

Режим зовнішнього ЦС

Також можливо надати лише файл ca.crt і не файл ca.key (це доступно лише для файлу кореневого ЦС, а не інших пар сертифікатів). Якщо всі інші сертифікати та файли kubeconfig на місці, kubeadm розпізнає цю умову та активує режим "Зовнішній ЦС". kubeadm буде продовжувати без ключа ЦС на диску.

Замість цього, запустіть контролер-менеджер самостійно з параметром --controllers=csrsigner та вкажіть на сертифікат та ключ ЦС.

Існують різні способи підготовки облікових даних компонента при використанні режиму зовнішнього ЦС.

Ручна підготовка облікових даних компонента

Сертифікати PKI та вимоги містять інформацію про те, як підготувати всі необхідні облікові дані для компонентів, які вимагаються kubeadm, вручну.

Підготовка облікових даних компонента шляхом підпису CSR, що генеруються kubeadm

kubeadm може генерувати файли CSR, які ви можете підписати вручну за допомогою інструментів, таких як openssl, та вашого зовнішнього ЦС. Ці файли CSR будуть включати всі вказівки для облікових даних, які вимагаються компонентами, розгорнутими kubeadm.

Автоматизована підготовка облікових даних компонента за допомогою фаз kubeadm

З іншого боку, можливо використовувати команди фаз kubeadm для автоматизації цього процесу.

  • Перейдіть на хост, який ви хочете підготувати як вузол панелі управління kubeadm з зовнішнім ЦС.
  • Скопіюйте зовнішні файли ЦС ca.crt та ca.key, які ви маєте, до /etc/kubernetes/pki на вузлі.
  • Підготуйте тимчасовий файл конфігурації kubeadm під назвою config.yaml, який можна використовувати з kubeadm init. Переконайтеся, що цей файл містить будь-яку відповідну інформацію на рівні кластера або хосту, яка може бути включена в сертифікати, таку як, ClusterConfiguration.controlPlaneEndpoint, ClusterConfiguration.certSANs та InitConfiguration.APIEndpoint.
  • На тому ж самому хості виконайте команди kubeadm init phase kubeconfig all --config config.yaml та kubeadm init phase certs all --config config.yaml. Це згенерує всі необхідні файли kubeconfig та сертифікати у теці /etc/kubernetes/ та її підтеці pki.
  • Перевірте згенеровані файли. Видаліть /etc/kubernetes/pki/ca.key, видаліть або перемістіть в безпечне місце файл /etc/kubernetes/super-admin.conf.
  • На вузлах, де буде викликано kubeadm join, також видаліть /etc/kubernetes/kubelet.conf. Цей файл потрібний лише на першому вузлі, де буде викликано kubeadm init.
  • Зауважте, що деякі файли, такі як pki/sa.*, pki/front-proxy-ca.* та pki/etc/ca.*, спільно використовуються між вузлами панелі управління, Ви можете згенерувати їх один раз та розподілити їх вручну на вузли, де буде викликано kubeadm join, або ви можете використовувати функціональність --upload-certs kubeadm init та --certificate-key kubeadm join для автоматизації цього розподілу.

Після того, як облікові дані будуть підготовлені на всіх вузлах, викличте kubeadm init та kubeadm join для цих вузлів, щоб приєднати їх до кластера. kubeadm використовуватиме наявні файли kubeconfig та сертифікати у теці /etc/kubernetes/ та її підтеці pki.

Перевірка закінчення терміну дії сертифікатів

Ви можете використовувати підкоманду check-expiration, щоб перевірити термін дії сертифікатів:

kubeadm certs check-expiration

Вивід подібний до наступного:

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Dec 30, 2020 23:36 UTC   364d                                    no
apiserver                  Dec 30, 2020 23:36 UTC   364d            ca                      no
apiserver-etcd-client      Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
apiserver-kubelet-client   Dec 30, 2020 23:36 UTC   364d            ca                      no
controller-manager.conf    Dec 30, 2020 23:36 UTC   364d                                    no
etcd-healthcheck-client    Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-peer                  Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
etcd-server                Dec 30, 2020 23:36 UTC   364d            etcd-ca                 no
front-proxy-client         Dec 30, 2020 23:36 UTC   364d            front-proxy-ca          no
scheduler.conf             Dec 30, 2020 23:36 UTC   364d                                    no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Dec 28, 2029 23:36 UTC   9y              no
etcd-ca                 Dec 28, 2029 23:36 UTC   9y              no
front-proxy-ca          Dec 28, 2029 23:36 UTC   9y              no

Команда показує час закінчення та залишковий час для сертифікатів клієнта у теці /etc/kubernetes/pki та для сертифіката клієнта, вбудованого у файли kubeconfig, що використовуються kubeadm (admin.conf, controller-manager.conf та scheduler.conf).

Крім того, kubeadm повідомляє користувача, якщо керування сертифікатом відбувається ззовні; у цьому випадку користувачу слід самостійно забезпечити керування поновленням сертифікатів вручну/за допомогою інших інструментів.

Автоматичне поновлення сертифікатів

kubeadm поновлює всі сертифікати під час оновлення панелі управління.

Ця функція призначена для вирішення найпростіших випадків використання; якщо у вас немає конкретних вимог до поновлення сертифікатів і ви регулярно виконуєте оновлення версії Kubernetes (частіше ніж 1 раз на рік між кожним оновленням), kubeadm буде піклуватися про те, щоб ваш кластер був завжди актуальним і досить безпечним.

Якщо у вас є складніші вимоги до поновлення сертифікатів, ви можете відмовитися від стандартної поведінки, передавши --certificate-renewal=false до kubeadm upgrade apply або до kubeadm upgrade node.

Ручне поновлення сертифікатів

Ви можете в будь-який момент вручну оновити свої сертифікати за допомогою команди kubeadm certs renew з відповідними параметрами командного рядка.

Ця команда виконує поновлення за допомогою сертифіката та ключа ЦС (або front-proxy-CA), збережених у /etc/kubernetes/pki.

Після виконання команди вам слід перезапустити Podʼи панелі управління. Це необхідно, оскільки динамічне перезавантаження сертифікатів наразі не підтримується для всіх компонентів та сертифікатів. Статичні Podʼи керуються локальним kubelet і не API-сервером, тому kubectl не може бути використаний для їх видалення та перезапуску. Щоб перезапустити статичний Pod, ви можете тимчасово видалити файл його маніфеста з /etc/kubernetes/manifests/ і зачекати 20 секунд (див. значення fileCheckFrequency у KubeletConfiguration struct). kubelet завершить роботу Pod, якщо він більше не знаходиться в теці маніфестів. Потім ви можете повернути файл назад і після ще одного періоду fileCheckFrequency kubelet знову створить Pod, і поновлення сертифікатів для компонента буде завершено.

kubeadm certs renew може оновити будь-який конкретний сертифікат або, за допомогою підкоманди all, він може оновити всі з них, як показано нижче:

kubeadm certs renew all

Поновлення сертифікатів за допомогою API сертифікатів Kubernetes

У цьому розділі надаються додаткові відомості про те, як виконати ручне поновлення сертифікатів за допомогою API сертифікатів Kubernetes.

Налаштування підписувача

Kubernetes Certificate Authority не працює зразу. Ви можете налаштувати зовнішнього підписувача, такого як cert-manager, або можете використовувати вбудованого підписувача.

Вбудований підписувач є частиною kube-controller-manager.

Для активації вбудованого підписувача вам необхідно передати прапорці --cluster-signing-cert-file та --cluster-signing-key-file.

Якщо ви створюєте новий кластер, ви можете використовувати файл конфігурації kubeadm:

apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
controllerManager:
  extraArgs:
  - name: "cluster-signing-cert-file"
    value: "/etc/kubernetes/pki/ca.crt"
  - name: "cluster-signing-key-file"
    value: "/etc/kubernetes/pki/ca.key"

Створення запитів на підпис сертифікатів (CSR)

Дивіться Створення CertificateSigningRequest для створення CSRs за допомогою API Kubernetes.

Поновлення сертифікатів зовнішнім ЦС

У цьому розділі надаються додаткові відомості про те, як виконати ручне поновлення сертифікатів за допомогою зовнішнього ЦС.

Для кращої інтеграції з зовнішніми ЦС, kubeadm також може створювати запити на підпис сертифікатів (CSR). Запит на підпис сертифіката є запитом до ЦС на підписаний сертифікат для клієнта. За термінологією kubeadm, будь-який сертифікат, який зазвичай підписується ЦС на диску, може бути створений у вигляді CSR. Однак ЦС не може бути створено як CSR.

Поновлення за допомогою запитів на підпис сертифікатів (CSR)

Поновлення сертифікатів можливе шляхом генерації нових CSR і підпису їх зовнішнім ЦС. Для отримання докладнішої інформації щодо роботи з CSR, створеними kubeadm, див. розділ Підпис запитів на підпис сертифікатів (CSR), згенерованих kubeadm.

Оновлення Certificate authority (ЦС)

Kubeadm не підтримує автоматичне оновлення або заміну сертифікатів ЦС зразу.

Для отримання додаткової інформації про ручне оновлення або заміну ЦС дивіться ручне оновлення сертифікатів ЦС.

Ввімкнення підписаних службових сертифікатів kubelet

Типово службовий сертифікат kubelet, розгорнутий за допомогою kubeadm, є самопідписним. Це означає, що зʼєднання зовнішніх служб, наприклад, сервера метрик з kubelet, не може бути захищено TLS.

Щоб налаштувати kubelet в новому кластері kubeadm для отримання належно підписаних службових сертифікатів, ви повинні передати наступну мінімальну конфігурацію до kubeadm init:

apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
serverTLSBootstrap: true

Якщо ви вже створили кластер, вам слід адаптувати його, виконавши наступне:

  • Знайдіть і відредагуйте ConfigMap kubelet-config-1.31 в просторі імен kube-system. У ConfigMap ключ kubelet має документ KubeletConfiguration як своє значення. Відредагуйте документ KubeletConfiguration, щоб встановити serverTLSBootstrap: true.
  • На кожному вузлі додайте поле serverTLSBootstrap: true у /var/lib/kubelet/config.yaml і перезапустіть kubelet за допомогою systemctl restart kubelet.

Поле serverTLSBootstrap: true дозволить ініціювати завантаження службових сертифікатів kubelet, запитуючи їх з API certificates.k8s.io. Одне з відомих обмежень поля serverTLSBootstrap: true — CSRs (запити на підпис сертифікатів) для цих сертифікатів не можуть бути автоматично затверджені типовим підписувачем в kube-controller-manager — kubernetes.io/kubelet-serving. Це потребує дій користувача або стороннього контролера.

Ці CSRs можна переглянути за допомогою:

kubectl get csr
NAME        AGE     SIGNERNAME                        REQUESTOR                      CONDITION
csr-9wvgt   112s    kubernetes.io/kubelet-serving     system:node:worker-1           Pending
csr-lz97v   1m58s   kubernetes.io/kubelet-serving     system:node:control-plane-1    Pending

Щоб затвердити їх, ви можете виконати наступне:

kubectl certificate approve <CSR-name>

Типово ці службові сертифікати закінчуються через рік. Kubeadm встановлює поле rotateCertificates в true у KubeletConfiguration, що означає, що близько до закінчення буде створено новий набір CSRs для службових сертифікатів і їх слід затвердити, щоб завершити оновлення. Для отримання додаткової інформації дивіться Оновлення сертифікатів.

Якщо ви шукаєте рішення для автоматичного затвердження цих CSRs, рекомендується звернутися до свого постачальника хмарних послуг і дізнатись, чи він має підписувача CSR, який перевіряє ідентифікацію вузла за допомогою окремого механізму.

Ви можете використовувати власні контролери сторонніх постачальників:

Такий контролер не є безпечним механізмом, якщо він перевіряє лише CommonName в CSR, але також перевіряє запитані IP-адреси та доменні імена. Це запобігло б зловмиснику, який має доступ до сертифіката клієнта kubelet, створювати CSRs, запитуючи службові сертифікати для будь-якої IP-адреси або доменного імені.

Генерація файлів kubeconfig для додаткових користувачів

Під час створення кластера, kubeadm підписує сертифікат у admin.conf, щоб мати Subject: O = system:masters, CN = kubernetes-admin. system:masters є групою суперкористувачів, яка обходить рівень авторизації (наприклад, RBAC). Не рекомендується ділитися admin.conf з додатковими користувачами!

Замість цього, ви можете використовувати команду kubeadm kubeconfig user для генерації файлів kubeconfig для додаткових користувачів. Команда приймає змішаний набір параметрів командного рядка та опцій конфігурації kubeadm. Згенерований kubeconfig буде записаний до stdout і може бути перенаправлений у файл за допомогою kubeadm kubeconfig user ... > somefile.conf.

Приклад конфігураційного файлу, який можна використовувати з --config:

# example.yaml
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
# Буде використано як цільовий "кластер" у kubeconfig
clusterName: "kubernetes"
# Буде використано як "сервер" (IP або DNS-імʼя) цього кластера в kubeconfig
controlPlaneEndpoint: "some-dns-address:6443"
# Ключ і сертифікат ЦС кластера будуть завантажені з цієї локальної теки
certificatesDir: "/etc/kubernetes/pki"

Переконайтеся, що ці параметри відповідають бажаним параметрам цільового кластера. Щоб переглянути параметри поточного кластера, скористайтеся:

kubectl get cm kubeadm-config -n kube-system -o=jsonpath="{.data.ClusterConfiguration}"

Наступний приклад згенерує файл kubeconfig з обліковими даними, дійсними протягом 24 годин, для нового користувача johndoe, який належить до групи appdevs:

kubeadm kubeconfig user --config example.yaml --org appdevs --client-name johndoe --validity-period 24h

Наступний приклад згенерує файл kubeconfig з обліковими даними адміністратора, дійсними протягом 1 тижня:

kubeadm kubeconfig user --config example.yaml --client-name admin --validity-period 168h

Підписування запитів на підпис сертифікатів (CSR), згенерованих kubeadm

Ви можете створювати запити на підпис сертифікатів за допомогою kubeadm certs generate-csr. Виклик цієї команди згенерує пари файлів .csr / .key для звичайних сертифікатів. Для сертифікатів, вбудованих у файли kubeconfig, команда згенерує пару файлів .csr / .conf, де ключ вже вбудований у файл .conf.

Файл CSR містить всю необхідну інформацію для ЦС для підпису сертифіката. kubeadm використовує чітко визначену специфікацію для всіх своїх сертифікатів і CSR.

Типовою текою для сертифікатів є /etc/kubernetes/pki, тоді як типова тека для файлів kubeconfig є /etc/kubernetes. Ці стандартні значення можна змінити за допомогою прапорців --cert-dir та --kubeconfig-dir, відповідно.

Для передачі власних параметрів команді kubeadm certs generate-csr використовуйте прапорець --config, який приймає файл конфігурації kubeadm, так само як і команди, такі як kubeadm init. Будь-яка специфікація, така як додаткові SAN та власні IP-адреси, повинна зберігатися в тому ж файлі конфігурації та використовуватися для всіх відповідних команд kubeadm, передаючи його як --config.

Підготовка файлів ЦС та сервісного облікового запису

На головному вузлі панелі управління, де буде виконано команду kubeadm init, виконайте наступні команди:

sudo kubeadm init phase certs ca
sudo kubeadm init phase certs etcd-ca
sudo kubeadm init phase certs front-proxy-ca
sudo kubeadm init phase certs sa

Це заповнить теки /etc/kubernetes/pki та /etc/kubernetes/pki/etcd усіма самопідписними файлами ЦС (сертифікати та ключі) та сервісним обліковим записом (публічні та приватні ключі), які необхідні kubeadm для вузла панелі управління.

Для другорядних вузлів панелі управління (kubeadm join --control-plane) нема потреби викликати вищезазначені команди. Залежно від того, як ви налаштували Високодоступний кластер, вам або потрібно вручну скопіювати ті ж самі файли з головного вузла панелі управління, або використати автоматизовану функціональність --upload-certs від kubeadm init.

Генерація CSR

Команда kubeadm certs generate-csr генерує CSR для всіх відомих сертифікатів, якими керує kubeadm. Після завершення команди вам потрібно вручну видалити файли .csr, .conf або .key, які вам не потрібні.

Врахування kubelet.conf

Цей розділ стосується як вузлів панелі управління, так і робочих вузлів.

Якщо ви видалили файл ca.key з вузлів панелі управління (Режим зовнішнього ЦС), активний kube-controller-manager у цьому кластері не зможе підписати клієнтські сертифікати kubelet. Якщо у вашій конфігурації не існує зовнішнього методу для підписання цих сертифікатів (наприклад, зовнішній підписувач), ви могли б вручну підписати kubelet.conf.csr, як пояснено в цьому посібнику.

Зверніть увагу, що це також означає, що автоматичне оновлення клієнтського сертифіката kubelet буде відключено. Таким чином, близько до закінчення терміну дії сертифіката, вам потрібно буде генерувати новий kubelet.conf.csr, підписувати сертифікат, вбудовувати його в kubelet.conf і перезапускати kubelet.

Якщо це не стосується вашої конфігурації, ви можете пропустити обробку kubelet.conf.csr на другорядних вузлах панелі управління та на робочих вузлах (всі вузли, що викликають kubeadm join ...). Це тому, що активний kube-controller-manager буде відповідальний за підписання нових клієнтських сертифікатів kubelet.

Вузли панелі управління

Виконайте наступну команду на головному (kubeadm init) та вторинних (kubeadm join --control-plane) вузлах панелі управління, щоб згенерувати всі файли CSR:

sudo kubeadm certs generate-csr

Якщо має використовуватися зовнішній etcd, дотримуйтесь керівництва Зовнішній etcd з kubeadm, щоб зрозуміти, які файли CSR потрібні на вузлах kubeadm та etcd. Інші файли .csr та .key у теці /etc/kubernetes/pki/etcd можна видалити.

Виходячи з пояснення у розділі Врахування kubelet.conf, збережіть або видаліть файли kubelet.conf та kubelet.conf.csr.

Робочі вузли

Згідно з поясненням у розділі Врахування kubelet.conf, за необхідності викличте:

sudo kubeadm certs generate-csr

та залиште лише файли kubelet.conf та kubelet.conf.csr. Альтернативно, повністю пропустіть кроки для робочих вузлів.

Підписання CSR для всіх сертифікатів

Повторіть цей крок для всіх вузлів, що мають файли CSR.

Запишіть наступний скрипт у теку /etc/kubernetes, перейдіть до цієї теки та виконайте скрипт. Скрипт згенерує сертифікати для всіх файлів CSR, які присутні в дереві /etc/kubernetes.

#!/bin/bash

# Встановіть термін дії сертифіката в днях
DAYS=365

# Обробіть всі файли CSR, крім тих, що призначені для front-proxy і etcd
find ./ -name "*.csr" | grep -v "pki/etcd" | grep -v "front-proxy" | while read -r FILE;
do
    echo "* Обробка ${FILE} ..."
    FILE=${FILE%.*} # Відкинути розширення
    if [ -f "./pki/ca.srl" ]; then
        SERIAL_FLAG="-CAserial ./pki/ca.srl"
    else
        SERIAL_FLAG="-CAcreateserial"
    fi
    openssl x509 -req -days "${DAYS}" -CA ./pki/ca.crt -CAkey ./pki/ca.key ${SERIAL_FLAG} \
        -in "${FILE}.csr" -out "${FILE}.crt"
    sleep 2
done

# Обробіть всі CSR для etcd
find ./pki/etcd -name "*.csr" | while read -r FILE;
do
    echo "* Обробка ${FILE} ..."
    FILE=${FILE%.*} # Відкинути розширення
    if [ -f "./pki/etcd/ca.srl" ]; then
        SERIAL_FLAG=-CAserial ./pki/etcd/ca.srl
    else
        SERIAL_FLAG=-CAcreateserial
    fi
    openssl x509 -req -days "${DAYS}" -CA ./pki/etcd/ca.crt -CAkey ./pki/etcd/ca.key ${SERIAL_FLAG} \
        -in "${FILE}.csr" -out "${FILE}.crt"
done

# Обробіть CSR для front-proxy
echo "* Обробка ./pki/front-proxy-client.csr ..."
openssl x509 -req -days "${DAYS}" -CA ./pki/front-proxy-ca.crt -CAkey ./pki/front-proxy-ca.key -CAcreateserial \
    -in ./pki/front-proxy-client.csr -out ./pki/front-proxy-client.crt

Вбудовування сертифікатів у файли kubeconfig

Повторіть цей крок для всіх вузлів, що мають файли CSR.

Запишіть наступний скрипт у теку /etc/kubernetes, перейдіть до цієї теки та виконайте скрипт. Скрипт візьме файли .crt, які були підписані для файлів kubeconfig з CSR на попередньому кроці, та вбудує їх у файли kubeconfig.

#!/bin/bash

CLUSTER=kubernetes
find ./ -name "*.conf" | while read -r FILE;
do
    echo "* Обробка ${FILE} ..."
    KUBECONFIG="${FILE}" kubectl config set-cluster "${CLUSTER}" --certificate-authority ./pki/ca.crt --embed-certs
    USER=$(KUBECONFIG="${FILE}" kubectl config view -o jsonpath='{.users[0].name}')
    KUBECONFIG="${FILE}" kubectl config set-credentials "${USER}" --client-certificate "${FILE}.crt" --embed-certs
done

Виконання очищення

Виконайте цей крок на всіх вузлах, які мають файли CSR.

Запишіть наступний скрипт у теці /etc/kubernetes, перейдіть до цієї теки та виконайте скрипт.

#!/bin/bash

# Очищення файлів CSR
rm -f ./*.csr ./pki/*.csr ./pki/etcd/*.csr # Очистка всіх файлів CSR

# Очищення файлів CRT, які вже були вбудовані у файли kubeconfig
rm -f ./*.crt

За бажанням, перемістіть файли .srl на наступний вузол, який буде оброблено.

За бажанням, якщо використовується зовнішній ЦС, видаліть файл /etc/kubernetes/pki/ca.key, як пояснено у розділі Вузол зовнішнього ЦС.

Ініціалізація вузла kubeadm

Як тільки файли CSR підписані і необхідні сертифікати розміщені на хостах, які ви хочете використовувати як вузли, ви можете використовувати команди kubeadm init та kubeadm join для створення Kubernetes кластера з цих вузлів. Під час init та join, kubeadm використовує існуючі сертифікати, ключі шифрування та файли kubeconfig, які він знаходить у дереві /etc/kubernetes у локальній файловій системі хоста.

2.1.8 - Переконфігурація кластера за допомогою kubeadm

kubeadm не підтримує автоматизованих способів переконфігурації компонентів, що були розгорнуті на керованих вузлах. Один зі способів автоматизації цього — використання власного оператора.

Для зміни конфігурації компонентів вам потрібно вручну редагувати повʼязані обʼєкти кластера та файли на диску.

Цей посібник показує правильну послідовність кроків, які потрібно виконати для досягнення переконфігурації кластера kubeadm.

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

  • Вам потрібен кластер, що був розгорнутий за допомогою kubeadm.
  • У вас мають бути адміністративні облікові дані (/etc/kubernetes/admin.conf) та мережеве зʼєднання з робочим kube-apiserver у кластері з хосту, на якому встановлено kubectl.
  • Мати текстовий редактор встановлений на всіх хостах.

Переконфігурація кластера

kubeadm записує набір параметрів конфігурації компонентів на рівні кластера у ConfigMaps та в інших обʼєктах. Ці обʼєкти потрібно редагувати вручну. Команда kubectl edit може бути використана для цього.

Команда kubectl edit відкриє текстовий редактор, в якому ви можете редагувати та зберегти обʼєкт безпосередньо.

Ви можете використовувати змінні середовища KUBECONFIG та KUBE_EDITOR для вказівки розташування файлу kubeconfig, який використовується kubectl, та обраного текстового редактора.

Наприклад:

KUBECONFIG=/etc/kubernetes/admin.conf KUBE_EDITOR=nano kubectl edit <параметри>

Застосування змін у конфігурації кластера

Оновлення ClusterConfiguration

Під час створення кластера та його оновлення, kubeadm записує ClusterConfiguration у ConfigMap, з назвою kubeadm-config у просторі імен kube-system.

Щоб змінити певну опцію у ClusterConfiguration, ви можете редагувати ConfigMap за допомогою цієї команди:

kubectl edit cm -n kube-system kubeadm-config

Конфігурація знаходиться в ключі data.ClusterConfiguration.

Віддзеркалення змін ClusterConfiguration на вузлах панелі управління

kubeadm керує компонентами панелі управління як статичними маніфестами Pod, які розташовані в теці /etc/kubernetes/manifests. Будь-які зміни у ClusterConfiguration в ключах apiServer, controllerManager, scheduler або etcd повинні віддзеркалюватись у відповідних файлах у теці маніфестів на вузлі панелі управління.

Такі зміни можуть включати:

  • extraArgs — потребує оновлення списку прапорців, які передаються контейнеру компонента
  • extraVolumes — потребує оновлення точок монтування для контейнера компонента
  • *SANs — потребує написання нових сертифікатів з Subject Alternative Names.

Перед продовженням цих змін переконайтеся, що ви зробили резервну копію теки /etc/kubernetes/.

Для написання нових сертифікатів ви можете використовувати:

kubeadm init phase certs <component-name> --config <config-file>

Для написання нових файлів маніфестів у теці /etc/kubernetes/manifests ви можете використовувати:

# Для компонентів панелі управління Kubernetes
kubeadm init phase control-plane <component-name> --config <config-file>
# Для локального etcd
kubeadm init phase etcd local --config <config-file>

Зміст <config-file> повинен відповідати оновленням в ClusterConfiguration. Значення <component-name> повинно бути імʼям компонента панелі управління Kubernetes (apiserver, controller-manager або scheduler).

Застосування змін конфігурації kubelet

Оновлення KubeletConfiguration

Під час створення кластера та оновлення, kubeadm записує KubeletConfiguration у ConfigMap з назвою kubelet-config в просторі імен kube-system.

Ви можете редагувати цей ConfigMap за допомогою такої команди:

kubectl edit cm -n kube-system kubelet-config

Конфігурація розташована в ключі data.kubelet.

Віддзеркалення змін в kubelet

Щоб віддзеркалити зміни на вузлах kubeadm, вам потрібно виконати наступне:

  • Увійдіть на вузол kubeadm.
  • Виконайте команду kubeadm upgrade node phase kubelet-config, щоб завантажити свіжий вміст kubelet-config ConfigMap в локальний файл /var/lib/kubelet/config.yaml.
  • Відредагуйте файл /var/lib/kubelet/kubeadm-flags.env, щоб застосувати додаткову конфігурацію за допомогою прапорців.
  • Перезапустіть службу kubelet за допомогою systemctl restart kubelet.

Застосування змін у конфігурації kube-proxy

Оновлення KubeProxyConfiguration

Під час створення кластера та оновлення, kubeadm записує KubeProxyConfiguration у ConfigMap в просторі імен kube-system з назвою kube-proxy.

Цей ConfigMap використовується DaemonSet kube-proxy в просторі імен kube-system.

Щоб змінити певну опцію в KubeProxyConfiguration, ви можете відредагувати ConfigMap за допомогою цієї команди:

kubectl edit cm -n kube-system kube-proxy

Конфігурація знаходиться в ключі data.config.conf.

Віддзеркалення змін у kube-proxy

Після оновлення ConfigMap kube-proxy, ви можете перезапустити всі Podʼи kube-proxy:

Отримайте імена Podʼів:

kubectl get po -n kube-system | grep kube-proxy

Видаліть Pod за допомогою:

kubectl delete po -n kube-system <імʼя-поду>

Створюватимуться нові Podʼи, які використовують оновлений ConfigMap.

Застосування змін конфігурації CoreDNS

Оновлення розгортання CoreDNS та сервісу

kubeadm розгортає CoreDNS як Deployment з назвою coredns та Service з назвою kube-dns, обидва у просторі імен kube-system.

Для оновлення будь-яких налаштувань CoreDNS ви можете редагувати обʼєкти Deployment та Service:

kubectl edit deployment -n kube-system coredns
kubectl edit service -n kube-system kube-dns

Віддзеркалення змін у CoreDNS

Після застосування змін у CoreDNS ви можете видалити його Podʼи:

Отримайте назви Podʼів:

kubectl get po -n kube-system | grep coredns

Видаліть Pod за допомогою:

kubectl delete po -n kube-system <імʼя-пода>

Нові Podʼи з оновленою конфігурацією CoreDNS будуть створені.

Збереження переконфігурації

Під час виконання команди kubeadm upgrade на керованому вузлі kubeadm може перезаписати конфігурацію, яка була застосована після створення кластера (переконфігурація).

Збереження переконфігурації обʼєкту Node

kubeadm записує Labels, Taints, сокенти CRI та іншу інформацію в обʼєкті Node для конкретного вузла Kubernetes. Для зміни будь-якого змісту цього обʼєкта Node ви можете використовувати:

kubectl edit no <імʼя-вузла>

Під час виконання kubeadm upgrade вміст такого Node може бути перезаписаний. Якщо ви бажаєте зберегти свої зміни в обʼєкті Node після оновлення, ви можете підготувати команду патча для kubectl і застосувати її до обʼєкта Node:

kubectl patch no <імʼя-вузла> --patch-file <файл-патча>

Збереження переконфігурації компонента панелі управління

Основним джерелом конфігурації панелі управління є обʼєкт ClusterConfiguration, збережений у кластері. Для розширення конфігурації статичних маніфестів Podʼів можна використовувати патчі.

Ці файли патчів повинні залишатися як файли на вузлах панелі управління, щоб забезпечити можливість їх використання командою kubeadm upgrade ... --patches <directory>.

Якщо переконфігурування виконується для ClusterConfiguration і статичних маніфестів Podʼів на диску, то набір патчів для конкретного вузла повинен бути відповідно оновлений.

Збереження переконфігурації kubelet

Будь-які зміни в KubeletConfiguration, збережені у /var/lib/kubelet/config.yaml, будуть перезаписані під час виконання kubeadm upgrade, завантажуючи вміст конфігурації kubelet-config ConfigMap для всього кластера. Для збереження конфігурації kubelet для Node потрібно або вручну змінити файл /var/lib/kubelet/config.yaml після оновлення, або файл /var/lib/kubelet/kubeadm-flags.env може містити прапорці. Прапорці kubelet перевизначають відповідні параметри KubeletConfiguration, але слід зауважити, що деякі з прапорців є застарілими.

Після зміни /var/lib/kubelet/config.yaml або /var/lib/kubelet/kubeadm-flags.env потрібен перезапуск kubelet.

Що далі

2.1.9 - Зміна репозиторія пакунків Kubernetes

Ця сторінка пояснює, як увімкнути репозиторій пакунків для бажаного мінорного випуску Kubernetes під час оновлення кластера. Це потрібно лише для користувачів репозиторіїв пакунків, що підтримуються спільнотою та розміщені на pkgs.k8s.io. На відміну від застарілих репозиторіїв пакунків, репозиторії пакунків, що підтримуються спільнотою, структуровані таким чином, що для кожної мінорної версії Kubernetes є окремий репозиторій пакунків.

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

У цьому документі припускається, що ви вже використовуєте репозиторії пакунків, які підтримуються спільнотою (pkgs.k8s.io). Якщо це не так, настійно рекомендується перейти на репозиторії пакунків, які підтримуються спільнотою, як описано в офіційному оголошенні.

Перевірка використання репозиторіїв пакунків Kubernetes

Якщо ви не впевнені, чи ви використовуєте репозиторії пакунків, які підтримуються спільнотою, чи застарілі репозиторії пакунків, виконайте наступні кроки для перевірки:

Виведіть вміст файлу, який визначає apt-репозиторій Kubernetes:

# У вашій системі цей файл конфігурації може мати іншу назву
pager /etc/apt/sources.list.d/kubernetes.list

Якщо ви бачите рядок, схожий на:

deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /

Ви використовуєте репозиторії пакунків Kubernetes, і цей посібник стосується вас. Інакше настійно рекомендується перейти на репозиторії пакунків Kubernetes, які підтримуються спільнотою, як описано в офіційному оголошенні.

Виведіть вміст файлу, який визначає yum-репозиторій Kubernetes:

# У вашій системі цей файл конфігурації може мати іншу назву
cat /etc/yum.repos.d/kubernetes.repo

Якщо ви бачите baseurl, схожий на baseurl в наведеному нижче виводі:

[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl

Ви використовуєте репозиторії пакунків Kubernetes, і цей посібник стосується вас. Інакше настійно рекомендується перейти на репозиторії пакунків Kubernetes, які підтримуються спільнотою, як описано в офіційному оголошенні.

Виведіть вміст файлу, який визначає zypper-репозиторій Kubernetes:

# У вашій системі цей файл конфігурації може мати іншу назву
cat /etc/zypp/repos.d/kubernetes.repo

Якщо ви бачите baseurl, схожий на baseurl в наведеному нижче виводі:

[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl

Ви використовуєте репозиторії пакунків Kubernetes, і цей посібник стосується вас. Інакше настійно рекомендується перейти на репозиторії пакунків Kubernetes, які підтримуються спільнотою, як описано в офіційному оголошенні.

Перехід на інший репозиторій пакунків Kubernetes

Цей крок слід виконати при оновленні з одного мінорного випуску Kubernetes на інший для отримання доступу до пакунків бажаної мінорної версії Kubernetes.

  1. Відкрийте файл, який визначає apt-репозиторій Kubernetes за допомогою текстового редактора на ваш вибір:

    nano /etc/apt/sources.list.d/kubernetes.list
    

    Ви повинні побачити один рядок з URL, що містить вашу поточну мінорну версію Kubernetes. Наприклад, якщо ви використовуєте v1.30, ви повинні побачити це:

    deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /
    
  2. Змініть версію в URL на наступний доступний мінорний випуск, наприклад:

    deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /
    
  3. Збережіть файл і вийдіть з текстового редактора. Продовжуйте дотримуватися відповідних інструкцій щодо оновлення.

  1. Відкрийте файл, який визначає yum-репозиторій Kubernetes за допомогою текстового редактора на ваш вибір:

    nano /etc/yum.repos.d/kubernetes.repo
    

    Ви повинні побачити файл з двома URL, що містять вашу поточну мінорну версію Kubernetes. Наприклад, якщо ви використовуєте v1.30, ви повинні побачити це:

    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.30/rpm/repodata/repomd.xml.key
    exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
    
  2. Змініть версію в цих URL на наступний доступний мінорний випуск, наприклад:

    [kubernetes]
    name=Kubernetes
    baseurl=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/
    enabled=1
    gpgcheck=1
    gpgkey=https://pkgs.k8s.io/core:/stable:/v1.31/rpm/repodata/repomd.xml.key
    exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
    
  3. Збережіть файл і вийдіть з текстового редактора. Продовжуйте дотримуватися відповідних інструкцій щодо оновлення.

Що далі

2.2 - Міграція з dockershim

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

Після оголошення про застарівання dockershim в Kubernetes 1.20, виникли питання, як це вплине на різні робочі навантаження та розгортання самого Kubernetes. Наш Dockershim Removal FAQ допоможе вам краще зрозуміти проблему.

Dockershim був видалений з Kubernetes з випуском v1.24. Якщо ви використовуєте Docker Engine через dockershim як своє оточення виконання контейнерів і хочете оновитись до v1.24, рекомендується або мігрувати на інше оточення виконання, або знайти альтернативний спосіб отримання підтримки Docker Engine. Ознайомтеся з розділом оточення виконання контейнерів, щоб дізнатися про ваші варіанти.

Версія Kubernetes з dockershim (1.23) вийшла з стану підтримки, а v1.24 скоро вийде зі стану підтримки. Переконайтеся, що повідомляєте про проблеми з міграцією, щоб проблеми могли бути вчасно виправлені, і ваш кластер був готовий до видалення dockershim. Після виходу v1.24 зі стану підтримки вам доведеться звертатися до вашого постачальника Kubernetes за підтримкою або оновлювати кілька версій одночасно, якщо є критичні проблеми, які впливають на ваш кластер.

Ваш кластер може мати більше одного типу вузлів, хоча це не є загальною конфігурацією.

Ці завдання допоможуть вам здійснити міграцію:

Що далі

2.2.1 - Заміна середовища виконання контейнерів на вузлі з Docker Engine на containerd

Це завдання визначає кроки, необхідні для оновлення вашого середовища виконання контейнерів на containerd з Docker. Воно буде корисним для операторів кластерів, які працюють з Kubernetes 1.23 або старішими версіями. Воно також охоплює приклад сценарію міграції з dockershim на containerd. З цієї сторінки можна вибрати альтернативні середовища виконання контейнерів.

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

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

Виведення вузла з експлуатації

kubectl drain <node-to-drain> --ignore-daemonsets

Замініть <node-to-drain> на імʼя вузла, який ви збираєтеся виводити з експлуатації.

Зупиніть службу Docker

systemctl stop kubelet
systemctl disable docker.service --now

Встановлення Containerd

Дотримуйтесь настанов посібника для отримання детальних кроків з встановлення containerd.

  1. Встановіть пакунок containerd.io з офіційних репозиторіїв Docker. Інструкції щодо налаштування репозиторію Docker для вашого конкретного дистрибутиву Linux і встановлення пакунка containerd.io можна знайти у Починаючи з containerd.

  2. Налаштуйте containerd:

    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml
    
  3. Перезапустіть containerd:

    sudo systemctl restart containerd
    

Розпочніть сеанс PowerShell, встановіть значення $Version на бажану версію (наприклад, $Version="1.4.3"), а потім виконайте наступні команди:

  1. Завантажте containerd:

    curl.exe -L https://github.com/containerd/containerd/releases/download/v$Version/containerd-$Version-windows-amd64.tar.gz -o containerd-windows-amd64.tar.gz
    tar.exe xvf .\containerd-windows-amd64.tar.gz
    
  2. Розпакуйте та налаштуйте:

    Copy-Item -Path ".\bin\" -Destination "$Env:ProgramFiles\containerd" -Recurse -Force
    cd $Env:ProgramFiles\containerd\
    .\containerd.exe config default | Out-File config.toml -Encoding ascii
    
    # Перегляньте конфігурацію. Залежно від налаштувань можливо, ви захочете внести корективи:
    # - образ sandbox_image (образ pause Kubernetes)
    # - розташування cni bin_dir та conf_dir
    Get-Content config.toml
    
    # (Необовʼязково, але дуже рекомендується) Виключіть containerd зі сканування Windows Defender
    Add-MpPreference -ExclusionProcess "$Env:ProgramFiles\containerd\containerd.exe"
    
  3. Запустіть containerd:

    .\containerd.exe --register-service
    Start-Service containerd
    

Налаштування kubelet для використання containerd як його середовища виконання контейнерів

Відредагуйте файл /var/lib/kubelet/kubeadm-flags.env та додайте середовище виконання контейнерів до прапорців; --container-runtime-endpoint=unix:///run/containerd/containerd.sock.

Користувачі, які використовують kubeadm, повинні знати, що інструмент kubeadm зберігає сокет CRI для кожного хосту як анотацію в обʼєкті Node для цього хосту. Щоб змінити його, ви можете виконати наступну команду на машині, на якій є файл kubeadm /etc/kubernetes/admin.conf.

kubectl edit no <імʼя-вузла>

Це запустить текстовий редактор, де ви можете редагувати обʼєкт Node. Для вибору текстового редактора ви можете встановити змінну середовища KUBE_EDITOR.

  • Змініть значення kubeadm.alpha.kubernetes.io/cri-socket з /var/run/dockershim.sock на шлях сокета CRI за вашим вибором (наприклад, unix:///run/containerd/containerd.sock).

    Зауважте, що нові шляхи сокета CRI в ідеалі повині мати префікс unix://.

  • Збережіть зміни в текстовому редакторі, що оновить обʼєкт Node.

Перезапустіть kubelet

systemctl start kubelet

Перевірте, що вузол справний

Запустіть kubectl get nodes -o wide, і containerd зʼявиться як середовище виконання для вузла, який ми щойно змінили.

Видаліть Docker Engine

Якщо вузол виглядає справним, видаліть Docker.

sudo yum remove docker-ce docker-ce-cli

sudo apt-get purge docker-ce docker-ce-cli

sudo dnf remove docker-ce docker-ce-cli

sudo apt-get purge docker-ce docker-ce-cli

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

Введення вузла в експлуатацію

kubectl uncordon <node-to-uncordon>

Замініть <node-to-uncordon> на імʼя вузла, який ви раніше вивели з експлуатації.

2.2.2 - Міграція вузлів Docker Engine з dockershim на cri-dockerd

Ця сторінка показує вам, як перевести ваші вузли з Docker Engine на використання cri-dockerd замість dockershim. Ви повинні дотримуватися цих кроків у таких сценаріях:

  • Ви хочете перейти від dockershim і все ще використовувати Docker Engine для запуску контейнерів у Kubernetes.
  • Ви хочете оновити до Kubernetes v1.31 і ваш наявний кластер покладається на dockershim, у такому випадку вам необхідно перейти з dockershim, де cri-dockerd є одним з варіантів.

Щоб дізнатися більше про видалення dockershim, прочитайте сторінку ЧаПи.

Що таке cri-dockerd?

У Kubernetes 1.23 та раніше ви могли використовувати Docker Engine з Kubernetes, покладаючись на вбудований компонент Kubernetes, що називався dockershim. Компонент dockershim було вилучено у випуску Kubernetes 1.24; проте доступний сторонній замінник, cri-dockerd. Адаптер cri-dockerd дозволяє використовувати Docker Engine через інтерфейс середовища виконання контейнерів.

Якщо ви хочете мігрувати на cri-dockerd, щоб продовжувати використовувати Docker Engine як своє середовище виконання контейнерів, вам слід виконати наступне для кожного вузла:

  1. Встановіть cri-dockerd.
  2. Відключіть та вимкніть вузол.
  3. Налаштуйте kubelet для використання cri-dockerd.
  4. Перезапустіть kubelet.
  5. Перевірте, що вузол справний.

Спочатку протестуйте міграцію на не критичних вузлах.

Ви повинні виконати наступні кроки для кожного вузла, який ви хочете перевести на cri-dockerd.

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

Відключіть та вимкніть вузол

  1. Відключіть вузол, щоб зупинити нові запуски капсул на ньому:

    kubectl cordon <NODE_NAME>
    

    Замініть <NODE_NAME> на імʼя вузла.

  2. Вимкніть вузол, щоб безпечно виселити працюючі Podʼи:

    kubectl drain <NODE_NAME> \
        --ignore-daemonsets
    

Налаштуйте kubelet для використання cri-dockerd

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

  1. Відкрийте /var/lib/kubelet/kubeadm-flags.env на кожному ураженому вузлі.
  2. Змініть прапорець --container-runtime-endpoint на unix:///var/run/cri-dockerd.sock.
  3. Змініть прапорець --container-runtime на remote (недоступно в Kubernetes v1.27 та пізніше).

Інструмент kubeadm зберігає сокет вузла як анотацію обʼєкта Node в панелі управління. Щоб змінити цей сокет для кожного ураженого вузла:

  1. Відредагуйте YAML-представлення обʼєкта Node:

    KUBECONFIG=/шлях/до/admin.conf kubectl edit no <NODE_NAME>
    

    Замініть наступне:

    • /шлях/до/admin.conf: шлях до файлу конфігурації kubectl, admin.conf.
    • <NODE_NAME>: імʼя вузла, яке ви хочете змінити.
  2. Змініть kubeadm.alpha.kubernetes.io/cri-socket з /var/run/dockershim.sock на unix:///var/run/cri-dockerd.sock.

  3. Збережіть зміни. Обʼєкт Node оновлюється при збереженні.

Перезапустіть kubelet

systemctl restart kubelet

Перевірте, що вузол справний

Щоб перевірити, чи використовує вузол точку доступу cri-dockerd, слідувати інструкціям Дізнайтеся, яке середовище виконання контейнерів використовується. Прапорець --container-runtime-endpoint для kubelet повинен бути unix:///var/run/cri-dockerd.sock.

Введення вузла в експлуатацію

Введіть вузол в експлуатацію, щоб Podʼи могли запускатися на ньому:

kubectl uncordon <NODE_NAME>

Що далі

2.2.3 - Перевірте, яке середовище виконання контейнерів використовується на вузлі

На цій сторінці наведено кроки для визначення того, яке середовище виконання контейнерів використовують вузли у вашому кластері.

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

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

Встановіть та налаштуйте kubectl. Див. розділ Встановлення інструментів для отримання деталей.

Визначте середовище виконання контейнерів, яке використовується на вузлі

Використовуйте kubectl, щоб отримати та показати інформацію про вузли:

kubectl get nodes -o wide

Вивід подібний до такого. Стовпець CONTAINER-RUNTIME виводить інформацію про середовище та його версію.

Для Docker Engine вивід схожий на цей:

NAME         STATUS   VERSION    CONTAINER-RUNTIME
node-1       Ready    v1.16.15   docker://19.3.1
node-2       Ready    v1.16.15   docker://19.3.1
node-3       Ready    v1.16.15   docker://19.3.1

Якщо ваше середовище показується як Docker Engine, це все одно не означає, що вас точно торкнеться видалення dockershim у Kubernetes v1.24. Перевірте точку доступу середовища, щоб побачити, чи використовуєте ви dockershim. Якщо ви не використовуєте dockershim, вас це не стосується.

Для containerd вивід схожий на цей:

NAME         STATUS   VERSION   CONTAINER-RUNTIME
node-1       Ready    v1.19.6   containerd://1.4.1
node-2       Ready    v1.19.6   containerd://1.4.1
node-3       Ready    v1.19.6   containerd://1.4.1

Дізнайтеся більше інформації про середовища виконання контейнерів на сторінці Середовища виконання контейнерів.

Дізнайтеся, яку точку доступу середовища виконання контейнерів ви використовуєте

Середовище виконання контейнерів спілкується з kubelet через Unix-сокет, використовуючи протокол CRI, який базується на фреймворку gRPC. Kubelet діє як клієнт, а середовище — як сервер. У деяких випадках може бути корисно знати, який сокет використовується на ваших вузлах. Наприклад, з видаленням dockershim у Kubernetes v1.24 і пізніше ви, можливо, захочете знати, чи використовуєте ви Docker Engine з dockershim.

Ви можете перевірити, який сокет ви використовуєте, перевіривши конфігурацію kubelet на своїх вузлах.

  1. Прочитайте початкові команди для процесу kubelet:

    tr \\0 ' ' < /proc/"$(pgrep kubelet)"/cmdline
    

    Якщо у вас немає tr або pgrep, перевірте командний рядок для процесу kubelet вручну.

  2. У виведенні шукайте прапорець --container-runtime та прапорець --container-runtime-endpoint.

    • Якщо ваші вузли використовують Kubernetes v1.23 та старіший, і ці прапори відсутні або прапорець --container-runtime не є remote, ви використовуєте сокет dockershim з Docker Engine. Параметр командного рядка --container-runtime не доступний у Kubernetes v1.27 та пізніше.
    • Якщо прапорець --container-runtime-endpoint присутній, перевірте імʼя сокета, щоб дізнатися, яке середовище ви використовуєте. Наприклад, unix:///run/containerd/containerd.sock — це точка доступу containerd.

Якщо ви хочете змінити середовище виконання контейнерів на вузлі з Docker Engine на containerd, ви можете дізнатися більше інформації про міграцію з Docker Engine на containerd, або, якщо ви хочете продовжити використовувати Docker Engine у Kubernetes v1.24 та пізніше, мігруйте на сумісний з CRI адаптер, наприклад cri-dockerd.

2.2.4 - Виправлення помилок, повʼязаних з втулками CNI

Щоб уникнути помилок, повʼязаних з втулками CNI, переконайтеся, що ви використовуєте або оновлюєте середовище виконання контейнерів, яке було протестовано й працює коректно з вашою версією Kubernetes.

Про помилки "Incompatible CNI versions" та "Failed to destroy network for sandbox"

Проблеми з Service існують для налаштування та демонтажу мережі CNI для Pod в containerd v1.6.0-v1.6.3, коли втулки CNI не були оновлені та/або версія конфігурації CNI не вказана в файлах конфігурації CNI. Команда containerd повідомляє, що "ці проблеми вирішені в containerd v1.6.4."

З containerd v1.6.0-v1.6.3, якщо ви не оновите втулки CNI та/або не вкажете версію конфігурації CNI, ви можете стикнутися з наступними умовами помилок "Incompatible CNI versions" або "Failed to destroy network for sandbox".

Помилка "Incompatible CNI versions"

Якщо версія вашого втулка CNI не відповідає версії втулка в конфігурації, тому що версія конфігурації є новішою, ніж версія втулка, лог containerd, швидше за все, покаже повідомлення про помилку при запуску Pod подібнe до:

incompatible CNI versions; config is \"1.0.0\", plugin supports [\"0.1.0\" \"0.2.0\" \"0.3.0\" \"0.3.1\" \"0.4.0\"]"

Щоб виправити цю проблему, оновіть свої втулки CNI та файли конфігурації CNI.

Помилка "Failed to destroy network for sandbox"

Якщо версія втулка відсутня в конфігурації втулка CNI, Pod може запускатися. Однак припинення Podʼа призводить до помилки, подібної до:

ERROR[2022-04-26T00:43:24.518165483Z] StopPodSandbox for "b" failed
error="failed to destroy network for sandbox \"bbc85f891eaf060c5a879e27bba9b6b06450210161dfdecfbb2732959fb6500a\": invalid version \"\": the version is empty"

Ця помилка залишає Pod у стані "not-ready" з приєднаним простором імен мережі. Щоб відновити роботу після цієї проблеми, відредагуйте файл конфігурації CNI, щоб додати відсутню інформацію про версію. Наступна спроба зупинити Pod повинна бути успішною.

Оновлення ваших втулків CNI та файлів конфігурації CNI

Якщо ви використовуєте containerd v1.6.0-v1.6.3 та зіткнулися з помилками "Incompatible CNI versions" або "Failed to destroy network for sandbox", розгляньте можливість оновлення ваших втулків CNI та редагування файлів конфігурації CNI.

Ось огляд типових кроків для кожного вузла:

  1. Безпечно виведіть та введіть вузол в експлуатацію.

  2. Після зупинки ваших служб середовища виконання контейнерів та kubelet виконайте наступні операції оновлення:

    • Якщо ви використовуєте втулки CNI, оновіть їх до останньої версії.
    • Якщо ви використовуєте не-CNI втулки, замініть їх втулками CNI. Використовуйте останню версію втулків.
    • Оновіть файл конфігурації втулка, щоб вказати або відповідати версії специфікації CNI, яку підтримує втулок, як показано у наступному розділі "Приклад файлу конфігурації containerd".
    • Для containerd переконайтеся, що ви встановили останню версію (v1.0.0 або пізніше) втулка CNI loopback.
    • Оновіть компоненти вузла (наприклад, kubelet) до Kubernetes v1.24.
    • Оновіть або встановіть найновішу версію середовища виконання контейнерів.
  3. Поверніть вузол у ваш кластер, перезапустивши ваше середовище виконання контейнерів та kubelet. Увімкніть вузол (kubectl uncordon <імʼя_вузла>).

Приклад файлу конфігурації containerd

Наведений нижче приклад показує конфігурацію для середовища виконання контейнерів containerd версії v1.6.x, яке підтримує останню версію специфікації CNI (v1.0.0).

Будь ласка, перегляньте документацію від вашого постачальника втулків та мереж для подальших інструкцій з налаштування вашої системи.

У Kubernetes, середовище виконання контейнерів containerd додає типовий інтерфейс loopback, lo, до Podʼів. Середовище виконання контейнерів containerd налаштовує інтерфейс loopback через втулок CNI, loopback. Втулок loopback розповсюджується як частина пакунків релізу containerd, які мають позначення cni. containerd v1.6.0 та пізніше включає сумісний з CNI v1.0.0 втулок loopback, а також інші типові втулки CNI. Налаштування втулка loopback виконується внутрішньо за допомогою контейнерного середовища containerd і встановлюється для використання CNI v1.0.0. Це також означає, що версія втулка loopback повинна бути v1.0.0 або пізніше при запуску цієї новішої версії containerd.

Наступна команда bash генерує приклад конфігурації CNI. Тут значення 1.0.0 для версії конфігурації призначено для поля cniVersion для використання при запуску containerd втулком bridge CNI.

cat << EOF | tee /etc/cni/net.d/10-containerd-net.conflist
{
 "cniVersion": "1.0.0",
 "name": "containerd-net",
 "plugins": [
   {
     "type": "bridge",
     "bridge": "cni0",
     "isGateway": true,
     "ipMasq": true,
     "promiscMode": true,
     "ipam": {
       "type": "host-local",
       "ranges": [
         [{
           "subnet": "10.88.0.0/16"
         }],
         [{
           "subnet": "2001:db8:4860::/64"
         }]
       ],
       "routes": [
         { "dst": "0.0.0.0/0" },
         { "dst": "::/0" }
       ]
     }
   },
   {
     "type": "portmap",
     "capabilities": {"portMappings": true},
     "externalSetMarkChain": "KUBE-MARK-MASQ"
   }
 ]
}
EOF

Оновіть діапазони IP-адрес у прикладі відповідно до вашого випадку використання та плану адресації мережі.

2.2.5 - Перевірка впливу видалення dockershim на ваш кластер

Компонент dockershim Kubernetes дозволяє використовувати Docker як середовище виконання контейнерів Kubernetes. Вбудований компонент dockershim Kubernetes був видалений у випуску v1.24.

Ця сторінка пояснює, як ваш кластер може використовувати Docker як середовище виконання контейнерів, надає деталі щодо ролі, яку відіграє dockershim при використанні, і показує кроки, які можна виконати, щоб перевірити, чи може видалення dockershim впливати на робочі навантаження.

Пошук залежностей вашого застосунку від Docker

Якщо ви використовуєте Docker для побудови контейнерів вашого застосунку, ви все ще можете запускати ці контейнери на будь-якому середовищі виконання контейнерів. Це використання Docker не вважається залежністю від Docker як середовища виконання контейнерів.

Коли використовується альтернативне середовище виконання контейнерів, виконання команд Docker може або не працювати, або призводити до неочікуваного виводу. Ось як ви можете зʼясувати, чи є у вас залежність від Docker:

  1. Переконайтеся, що привілейовані Podʼи не виконують команди Docker (наприклад, docker ps), перезапускають службу Docker (команди типу systemctl restart docker.service) або змінюють файли Docker, такі як /etc/docker/daemon.json.
  2. Перевірте наявність приватних реєстрів або налаштувань дзеркал образів у файлі конфігурації Docker (наприклад, /etc/docker/daemon.json). Зазвичай їх потрібно повторно налаштувати для іншого середовища виконання контейнерів.
  3. Перевірте, що скрипти та застосунки, які працюють на вузлах поза вашою інфраструктурою Kubernetes, не виконують команди Docker. Це може бути:
    • SSH на вузли для усунення несправностей;
    • Сценарії запуску вузлів;
    • Встановлені безпосередньо на вузлах агенти безпеки та моніторингу.
  4. Інструменти сторонніх розробників, які виконують вищезгадані привілейовані операції. Див. Міграція телеметрії та агентів безпеки з dockershim для отримання додаткової інформації.
  5. Переконайтеся, що немає непрямих залежностей від поведінки dockershim. Це крайній випадок і ймовірно не вплине на ваш застосунок. Деякі інструменти можуть бути налаштовані реагувати на специфічну поведінку Docker, наприклад, видаляти сповіщення про певні метрики або шукати певне повідомлення у лозі як частину інструкцій щодо усунення несправностей. Якщо у вас налаштовано такі інструменти, перевірте поведінку на тестовому кластері перед міграцією.

Пояснення залежності від Docker

Середовище виконання контейнерів — це програмне забезпечення, яке може виконувати контейнери, які складаються з Podʼів Kubernetes. Kubernetes відповідає за оркестрування та планування Podʼів; на кожному вузлі kubelet використовує інтерфейс середовища виконання контейнерів як абстракцію, щоб ви могли використовувати будь-яке сумісне середовище виконання контейнерів.

У своїх перших випусках Kubernetes пропонував сумісність з одним середовищем виконання контейнерів — Docker. Пізніше, в історії проєкту Kubernetes, оператори кластерів хотіли б використовувати додаткові середовища виконання контейнерів. CRI було розроблено для того, щоб дозволити такий вид гнучкості — і kubelet почав підтримувати CRI. Однак, оскільки Docker існував до того, як була винайдена специфікація CRI, проєкт Kubernetes створив адаптер dockershim. Адаптер dockershim дозволяє kubelet взаємодіяти з Docker так, ніби Docker був середовищем виконання контейнерів, сумісним з CRI.

Ви можете прочитати про це в блозі Інтеграція Kubernetes Containerd стає загально доступною.

Dockershim vs. CRI з Containerd

Перехід до Containerd як середовища виконання контейнерів прибирає посередника. Всі ті самі контейнери можуть бути запущені середовищем виконання контейнерів, такими як Containerd, як і раніше. Але тепер, оскільки контейнери розміщуються безпосередньо з середовищем виконання контейнерів, вони не є видимими для Docker. Отже, будь-який інструментарій Docker або стильний інтерфейс користувача, який ви могли використовувати раніше для перевірки цих контейнерів, більше не доступний.

Ви не можете отримати інформацію про контейнери за допомогою команд docker ps або docker inspect. Оскільки ви не можете отримувати перелік контейнерів, ви не можете отримати логи, зупинити контейнери або виконати щось всередині контейнера за допомогою docker exec.

Ви все ще можете отримувати образи або будувати їх за допомогою команди docker build. Але образи, побудовані або отримані Docker, не будуть видимі для середовища виконання контейнерів та Kubernetes. Їх потрібно буде розмістити у якомусь реєстрі, щоб їх можна було використовувати в Kubernetes.

Відомі проблеми

Деякі метрики файлової системи відсутні та формат метрик відрізняється

Точка доступу Kubelet /metrics/cadvisor надає метрики Prometheus, як описано в Метрики для системних компонентів Kubernetes. Якщо ви встановите збирач метрик, який залежить від цієї точки доступу, ви можете побачити такі проблеми:

  • Формат метрик на вузлі Docker - це k8s_<container-name>_<pod-name>_<namespace>_<pod-uid>_<restart-count>, але формат у іншому середовищі відрізняється. Наприклад, на вузлі containerd він має вигляд <container-id>.

  • Деякі метрики файлової системи відсутні, як описано нижче:

    container_fs_inodes_free
    container_fs_inodes_total
    container_fs_io_current
    container_fs_io_time_seconds_total
    container_fs_io_time_weighted_seconds_total
    container_fs_limit_bytes
    container_fs_read_seconds_total
    container_fs_reads_merged_total
    container_fs_sector_reads_total
    container_fs_sector_writes_total
    container_fs_usage_bytes
    container_fs_write_seconds_total
    container_fs_writes_merged_total
    

Обхідний шлях

Ви можете помʼякшити цю проблему, використовуючи cAdvisor як автономний DaemonSet.

  1. Знайдіть останній реліз cAdvisor із шаблоном імені vX.Y.Z-containerd-cri (наприклад, v0.42.0-containerd-cri).
  2. Дотримуйтесь кроків у DaemonSet Kubernetes cAdvisor, щоб створити DaemonSet.
  3. Drf;Вкажіть збирачу метрик використовувати точку доступу /metrics cAdvisor, яка надає повний набір метрик контейнера в форматі Prometheus.

Альтернативи:

  • Використовуйте альтернативне рішення для збору метрик сторонніх розробників.
  • Збирайте метрики з Kubelet summary API, який доступний через /stats/summary.

Що далі

2.2.6 - Міграція агентів телеметрії та безпеки з dockershim

Підтримка Kubernetes прямої інтеграції з Docker Engine є застарілою та була видалена. Більшість застосунків не мають прямої залежності від середовища виконання контейнерів. Однак, є ще багато агентів телеметрії та моніторингу, які мають залежність від Docker для збору метаданих, логів та метрик контейнерів. Цей документ збирає інформацію про те, як виявити ці залежності, а також посилання на те, як перенести ці агенти для використання загальних інструментів або альтернативних середовищ виконання.

Агенти телеметрії та безпеки

У кластері Kubernetes є кілька різних способів запуску агентів телеметрії чи безпеки. Деякі агенти мають пряму залежність від Docker Engine, коли вони працюють як DaemonSets або безпосередньо на вузлах.

Чому деякі агенти телеметрії взаємодіють з Docker Engine?

Історично Kubernetes був спеціально створений для роботи з Docker Engine. Kubernetes займався мережею та плануванням, покладаючись на Docker Engine для запуску та виконання контейнерів (у Podʼах) на вузлі. Деяка інформація, яка стосується телеметрії, наприклад, імʼя Podʼа, доступна лише з компонентів Kubernetes. Інші дані, такі як метрики контейнерів, не є обовʼязком середовища виконання контейнера. Ранні агенти телеметрії мали потребу у запиті середовища виконання контейнера та Kubernetes для передачі точної картини. З часом Kubernetes набув можливості підтримки кількох середовищ виконання контейнерів, і зараз підтримує будь-яке середовище виконання, яке сумісне з інтерфейсом середовища виконання контейнерів.

Деякі агенти телеметрії покладаються тільки на інструменти Docker Engine. Наприклад, агент може виконувати команду, таку як docker ps чи docker top для отримання переліку контейнерів та процесів або docker logs для отримання поточних логів. Якщо вузли у вашому проточному кластері використовують Docker Engine, і ви переходите на інше середовище виконання контейнерів, ці команди більше не працюватимуть.

Виявлення DaemonSets, що залежать від Docker Engine

Якщо Pod хоче викликати dockerd, що працює на вузлі, він повинен або:

  • змонтувати файлову систему, що містить привілейований сокет Docker, як том; або
  • безпосередньо змонтувати конкретний шлях привілейованого сокета Docker, також як том.

Наприклад: на образах COS Docker відкриває свій Unix сокет у /var/run/docker.sock. Це означає, що специфікація Pod буде містити монтування тому hostPath з /var/run/docker.sock.

Нижче наведено приклад сценарію оболонки для пошуку Podʼів, які мають монтування, які безпосередньо зіставляються з сокетом Docker. Цей сценарій виводить простір імен та імʼя Podʼа. Ви можете видалити grep '/var/run/docker.sock', щоб переглянути інші монтування.

kubectl get pods --all-namespaces \
-o=jsonpath='{range .items[*]}{"\n"}{.metadata.namespace}{":\t"}{.metadata.name}{":\t"}{range .spec.volumes[*]}{.hostPath.path}{", "}{end}{end}' \
| sort \
| grep '/var/run/docker.sock'

Виявлення залежності від Docker агентів вузлів

Якщо вузли кластера налаштовані та встановлюють додаткові заходи безпеки та агенти телеметрії на вузлі, перевірте у вендора агента, чи має він будь-які залежності від Docker.

Вендори агентів телеметрії та безпеки

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

Ми зберігаємо робочу версію інструкцій щодо перенесення для різних вендорів агентів телеметрії та безпеки у документі Google. Будь ласка, звʼяжіться з вендором, щоб отримати актуальні інструкції щодо міграції з dockershim.

Міграція з dockershim

Aqua

Ніяких змін не потрібно: все має працювати без перешкод при перемиканні середовища виконання.

Datadog

Як перенести: Застарівання Docker у Kubernetes Pod, який має доступ до Docker Engine, може мати назву, яка містить будь-що з:

  • datadog-agent
  • datadog
  • dd-agent

Dynatrace

Як перенести: Міграція з Docker до загальних метрик контейнера в Dynatrace

Оголошення підтримки Containerd: Автоматична повна видимість у стеку в середовищах Kubernetes на основі containerd

Оголошення підтримки CRI-O: Автоматична повна видимість у ваших контейнерах Kubernetes з CRI-O (бета)

Pod, який має доступ до Docker, може мати назву, яка містить:

  • dynatrace-oneagent

Falco

Як перенести: Міграція Falco з dockershim. Falco підтримує будь-яке середовище виконання, сумісне з CRI (стандартно використовується containerd); документація пояснює всі деталі. Pod, який має доступ до Docker, може мати назву, яка містить:

  • falco

Prisma Cloud Compute

Перевірте документацію для Prisma Cloud, в розділі "Встановлення Prisma Cloud в кластер CRI (не Docker)". Pod, який має доступ до Docker, може мати назву, подібну до:

  • twistlock-defender-ds

SignalFx (Splunk)

Смарт-агент SignalFx (застарілий) використовує кілька різних моніторів для Kubernetes, включаючи kubernetes-cluster, kubelet-stats/kubelet-metrics та docker-container-stats. Монітор kubelet-stats раніше був застарілим вендором на користь kubelet-metrics. Монітор docker-container-stats є одним з тих, що стосуються видалення dockershim. Не використовуйте монітор docker-container-stats з середовищами виконання контейнерів, відмінними від Docker Engine.

Як перейти з агента, залежного від dockershim:

  1. Видаліть docker-container-stats зі списку налаштованих моніторів. Зверніть увагу, що залишення цього монітора увімкненим з не-dockershim середовищем виконання призведе до неправильних метрик при встановленні docker на вузлі та відсутності метрик, коли docker не встановлено.
  2. Увімкніть та налаштуйте монітор kubelet-metrics.

Pod, який має доступ до Docker, може мати назву, подібну до:

  • signalfx-agent

Yahoo Kubectl Flame

Flame не підтримує середовищ виконання контейнера, відмінні від Docker. Див. https://github.com/yahoo/kubectl-flame/issues/51

2.3 - Генерація сертифікатів вручну

При використанні автентифікації сертифіката клієнта ви можете генерувати сертифікати вручну за допомогою easyrsa, openssl або cfssl.

easyrsa

З easyrsa ви можете вручну генерувати сертифікати для вашого кластера.

  1. Завантажте, розпакуйте та ініціалізуйте патчену версію easyrsa3.

    curl -LO https://dl.k8s.io/easy-rsa/easy-rsa.tar.gz
    tar xzf easy-rsa.tar.gz
    cd easy-rsa-master/easyrsa3
    ./easyrsa init-pki
    
  2. Згенеруйте новий центр сертифікації (CA). --batch встановлює автоматичний режим; --req-cn вказує загальну назву (CN, Common Name) для нового кореневого сертифіката CA.

    ./easyrsa --batch "--req-cn=${MASTER_IP}@`date +%s`" build-ca nopass
    
  3. Згенеруйте сертифікат та ключ сервера.

    Аргумент --subject-alt-name встановлює можливі IP-адреси та імена DNS, за якими буде доступний сервер API. MASTER_CLUSTER_IP зазвичай є першою IP-адресою з діапазону служб CIDR, який вказаний як аргумент --service-cluster-ip-range для сервера API та компонента керування контролером. Аргумент --days використовується для встановлення кількості днів чинності сертифіката. У прикладі нижче також припускається, що ви використовуєте cluster.local як типове імʼя домену DNS.

    ./easyrsa --subject-alt-name="IP:${MASTER_IP},"\
    "IP:${MASTER_CLUSTER_IP},"\
    "DNS:kubernetes,"\
    "DNS:kubernetes.default,"\
    "DNS:kubernetes.default.svc,"\
    "DNS:kubernetes.default.svc.cluster,"\
    "DNS:kubernetes.default.svc.cluster.local" \
    --days=10000 \
    build-server-full server nopass
    
  4. Скопіюйте pki/ca.crt, pki/issued/server.crt та pki/private/server.key у вашу теку.

  5. Заповніть та додайте наступні параметри до параметрів запуску сервера API:

    --client-ca-file=/yourdirectory/ca.crt
    --tls-cert-file=/yourdirectory/server.crt
    --tls-private-key-file=/yourdirectory/server.key
    

openssl

Ви також можете вручну генерувати сертифікати за допомогою openssl.

  1. Згенеруйте 2048-бітний ca.key :

    openssl genrsa -out ca.key 2048
    
  2. Згенеруйте ca.crt для ca.key (використовуйте -days для встановлення кількості днів чинності сертифіката):

    openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt
    
  3. Згенеруйте 2048-бітний server.key:

    openssl genrsa -out server.key 2048
    
  4. Створіть конфігураційний файл для генерування Certificate Signing Request (CSR).

    Переконайтеся, що ви встановили власні значення для параметрів в кутових дужках, наприклад <MASTER_IP>, замінивши їх на власні значення перед збереженням файлу, наприклад з назвою csr.conf. Зауважте, що значення <MASTER_CLUSTER_IP> є IP-адресою API-сервера, як про це йдеться в попередньому розділі. Приклад конфігураційного файлу нижче використовує cluster.local як типове імʼя домену DNS.

    [ req ]
    default_bits = 2048
    prompt = no
    default_md = sha256
    req_extensions = req_ext
    distinguished_name = dn
    
    [ dn ]
    C = <country>
    ST = <state>
    L = <city>
    O = <organization>
    OU = <organization unit>
    CN = <MASTER_IP>
    
    [ req_ext ]
    subjectAltName = @alt_names
    
    [ alt_names ]
    DNS.1 = kubernetes
    DNS.2 = kubernetes.default
    DNS.3 = kubernetes.default.svc
    DNS.4 = kubernetes.default.svc.cluster
    DNS.5 = kubernetes.default.svc.cluster.local
    IP.1 = <MASTER_IP>
    IP.2 = <MASTER_CLUSTER_IP>
    
    [ v3_ext ]
    authorityKeyIdentifier=keyid,issuer:always
    basicConstraints=CA:FALSE
    keyUsage=keyEncipherment,dataEncipherment
    extendedKeyUsage=serverAuth,clientAuth
    subjectAltName=@alt_names
    
  5. Згенеруйте Certificate Signing Request (CSR) на основі конфігураційного файлу:

    openssl req -new -key server.key -out server.csr -config csr.conf
    
  6. Згенеруйте сертифікат сервера, використовуючи ca.key, ca.crt та server.csr:

    openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
        -CAcreateserial -out server.crt -days 10000 \
        -extensions v3_ext -extfile csr.conf -sha256
    
  7. Перегляньте запит на підписання сертифіката:

    openssl req  -noout -text -in ./server.csr
    
  8. Перегляньте сертифікат:

    openssl x509 -noout -text -in ./server.crt
    

Нарешті, додайте ті ж самі параметри до параметрів запуску сервера API.

cfssl

Ви також можете вручну генерувати сертифікати за допомогою cfssl.

  1. Завантажте, розпакуйте та підготуйте інструменти як показано нижче

    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_linux_amd64 -o cfssl
    chmod +x cfssl
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_linux_amd64 -o cfssljson
    chmod +x cfssljson
    curl -L https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl-certinfo_1.5.0_linux_amd64 -o cfssl-certinfo
    chmod +x cfssl-certinfo
    
  2. Створіть теку для зберігання артифактів та ініціалізації cfssl:

    mkdir cert
    cd cert
    ../cfssl print-defaults config > config.json
    ../cfssl print-defaults csr > csr.json
    
  3. Створіть конфігураційний файл JSON для генерації файлу CA, наприклад, ca-config.json:

    {
      "signing": {
        "default": {
          "expiry": "8760h"
        },
        "profiles": {
          "kubernetes": {
            "usages": [
              "signing",
              "key encipherment",
              "server auth",
              "client auth"
            ],
            "expiry": "8760h"
          }
        }
      }
    }
    
  4. Створіть конфігураційний файл JSON для генерації Certificate Signing Request (CSR), наприклад, ca-csr.json. Переконайтеся, що ви встановили власні значення для параметрів в кутових дужках.

    {
      "CN": "kubernetes",
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names":[{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  5. Згенеруйте сертифікат CA (ca.pem) та ключ CA (ca-key.pem):

    ../cfssl gencert -initca ca-csr.json | ../cfssljson -bare ca
    
  6. Створіть конфігураційний файл JSON для генерації ключів та сертифікатів для сервера API, наприклад, server-csr.json. Переконайтеся, що ви встановили власні значення для параметрів в кутових дужках. <MASTER_CLUSTER_IP> є IP-адресою кластера, як про це йдеться в попередньому розділі. В прикладі нижче також припускається, що ви використовуєте cluster.local як типове імʼя домену DNS.

    {
      "CN": "kubernetes",
      "hosts": [
        "127.0.0.1",
        "<MASTER_IP>",
        "<MASTER_CLUSTER_IP>",
        "kubernetes",
        "kubernetes.default",
        "kubernetes.default.svc",
        "kubernetes.default.svc.cluster",
        "kubernetes.default.svc.cluster.local"
      ],
      "key": {
        "algo": "rsa",
        "size": 2048
      },
      "names": [{
        "C": "<country>",
        "ST": "<state>",
        "L": "<city>",
        "O": "<organization>",
        "OU": "<organization unit>"
      }]
    }
    
  7. Згенеруйте ключ та сертифікат для сервера API, які типово зберігаються у файлах server-key.pem та server.pem, відповідно:

    ../cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
         --config=ca-config.json -profile=kubernetes \
         server-csr.json | ../cfssljson -bare server
    

Розповсюдження самопідписних сертифікатів CA

Клієнтський вузол може не визнавати самопідписні сертифікати CA. Для оточення, що не є операційним, або для операційного оточення, що працює за корпоративним фаєрволом, ви можете розповсюджувати самопідписні сертифікати CA на всіх клієнтів та оновлювати перелік довірених сертифікатів.

На кожному клієнті виконайте наступні кроки:

sudo cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....
done.

API сертифікатів

Ви можете використовувати certificates.k8s.io API для надання сертифікатів x509 для використання для автентифікації, як про це йдеться на сторінці Керування TLS в кластері.

2.4 - Керування ресурсами памʼяті, CPU та API

2.4.1 - Налаштування типових запитів та обмежень памʼяті для простору імен

Визначте типове обмеження ресурсів памʼяті для простору імен, щоб кожний новий Контейнер у цьому просторі імен мав налаштоване обмеження ресурсів памʼяті.

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

Кластер Kubernetes може бути розділений на простори імен. Якщо у вас є простір імен, в якому вже є типове обмеження памʼяті limit, і ви спробуєте створити Pod з контейнером, який не вказує своє власне обмеження памʼяті, то панель управління назначає типове обмеження памʼяті цьому контейнеру.

Kubernetes назначає типовий запит памʼяті за певних умов, які будуть пояснені пізніше в цій темі.

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

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

У вас має бути доступ до створення просторів імен у вашому кластері.

Кожен вузол у вашому кластері повинен мати принаймні 2 ГіБ памʼяті.

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були ізольовані від решти вашого кластера.

kubectl create namespace default-mem-example

Створення LimitRange та Pod

Ось маніфест для прикладу LimitRange. Маніфест вказує типовий запит памʼяті та типове обмеження памʼяті.

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
  - default:
      memory: 512Mi
    defaultRequest:
      memory: 256Mi
    type: Container

Створіть LimitRange у просторі імен default-mem-example:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults.yaml --namespace=default-mem-example

Тепер, якщо ви створите Pod у просторі імен default-mem-example, і будь-який контейнер у цьому Podʼі не вказує свої власні значення для запиту та обмеження памʼяті, то панель управління застосовує типові значення: запит памʼяті 256MiB та обмеження памʼяті 512MiB.

Ось приклад маніфесту для Pod, який має один контейнер. Контейнер не вказує запиту та обмеження памʼяті.

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo
spec:
  containers:
  - name: default-mem-demo-ctr
    image: nginx

Створіть цей Pod.

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod.yaml --namespace=default-mem-example

Перегляньте інформацію про цей Pod:

kubectl get pod memory-defaults-pod --namespace=default-mem-example

Вивід має показати, що контейнер Podʼа має обмеження на запит памʼяті 256MiB та обмеження памʼяті 512MiB. Ці значення були назначені через типові обмеження памʼяті, вказані в LimitRange.

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-mem-demo-ctr
  resources:
    limits:
      memory: 512Mi
    requests:
      memory: 256Mi

Видаліть свій Pod:

kubectl delete pod memory-defaults-pod --namespace=default-mem-example

Що якщо ви вказуєте обмеження контейнера, але не його запит?

Ось маніфест для Podʼа з одним контейнером. Контейнер вказує обмеження памʼяті, але не запит:

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-2
spec:
  containers:
  - name: default-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1Gi"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-2.yaml --namespace=default-mem-example

Перегляньте детальну інформацію про Pod:

kubectl get pod default-mem-demo-2 --output=yaml --namespace=default-mem-example

Вивід показує, що запит памʼяті контейнера встановлено таким чином, щоб відповідати його обмеженню памʼяті. Зверніть увагу, що контейнеру не було назначено типового значення запиту памʼяті 256Mi.

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

Що якщо ви вказуєте запит контейнера, але не його обмеження?

Ось маніфест для Podʼа з одним контейнером. Контейнер вказує запит памʼяті, але не обмеження:

apiVersion: v1
kind: Pod
metadata:
  name: default-mem-demo-3
spec:
  containers:
  - name: default-mem-demo-3-ctr
    image: nginx
    resources:
      requests:
        memory: "128Mi"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-defaults-pod-3.yaml --namespace=default-mem-example

Перегляньте специфікацію Podʼа:

kubectl get pod default-mem-demo-3 --output=yaml --namespace=default-mem-example

Вивід показує, що запит памʼяті контейнера встановлено на значення, вказане в маніфесті контейнера. Контейнер обмежений використовувати не більше 512MiB памʼяті, що відповідає типовому обмеженню памʼяті для простору імен.

resources:
  limits:
    memory: 512Mi
  requests:
    memory: 128Mi

Мотивація для типових обмежень та запитів памʼяті

Якщо у вашому просторі імен налаштовано квоту ресурсів памʼяті, корисно мати типове значення для обмеження памʼяті. Ось три обмеження, які накладає квота ресурсів на простір імен:

  • Для кожного Podʼа, який працює у просторі імен, Pod та кожен з його контейнерів повинні мати обмеження памʼяті. (Якщо ви вказуєте обмеження памʼяті для кожного контейнера у Podʼі, Kubernetes може вивести типове обмеження памʼяті на рівні Podʼа, додавши обмеження для його контейнерів).
  • Обмеження памʼяті застосовує резервування ресурсів на вузлі, де запускається відповідний Pod. Загальна кількість памʼяті, зарезервована для всіх Podʼів у просторі імен, не повинна перевищувати вказаного обмеження.
  • Загальна кількість памʼяті, що фактично використовується всіма Podʼами у просторі імен, також не повинна перевищувати вказаного обмеження.

Коли ви додаєте обмеження (LimitRange):

Якщо будь-який Pod у цьому просторі імен, що містить контейнер, не вказує своє власне обмеження памʼяті, панель управління застосовує типове обмеження памʼяті для цього контейнера, і Podʼу може бути дозволено запуститися в просторі імен, який обмежено квотою ресурсів памʼяті.

Очищення

Видаліть простір імен:

kubectl delete namespace default-mem-example

Що далі

Для адміністраторів кластера

Для розробників додатків

2.4.2 - Налаштування типових запитів та обмежень CPU для простору імен

Визначте типове обмеження ресурсів CPU для простору імен так, щоб усі нові Podʼи у цьому просторі імен мали налаштоване обмеження ресурсів CPU.

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

Кластер Kubernetes може бути розділений на простори імен. Якщо ви створюєте Pod у просторі імен, який має типове обмеження CPU limit, і будь-який контейнер у цьому Podʼі не вказує своє власне обмеження CPU, то панель управління назначає типове обмеження CPU цьому контейнеру.

Kubernetes назначає типовий запит CPU request, але лише за певних умов, які будуть пояснені пізніше на цій сторінці.

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

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

Вам потрібно мати доступ для створення просторів імен у вашому кластері.

Якщо ви ще не знайомі з тим, що означає 1.0 CPU в Kubernetes, прочитайте значення CPU.

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були ізольовані від решти вашого кластера.

kubectl create namespace default-cpu-example

Створення LimitRange та Podʼа

Ось маніфест для прикладу LimitRange. У маніфесті вказано типовий запит CPU та типове обмеження CPU.

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-limit-range
spec:
  limits:
  - default:
      cpu: 1
    defaultRequest:
      cpu: 0.5
    type: Container

Створіть LimitRange у просторі імен default-cpu-example:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults.yaml --namespace=default-cpu-example

Тепер, якщо ви створюєте Pod у просторі імен default-cpu-example, і будь-який контейнер у цьому Podʼі не вказує свої власні значення для запиту та обмеження CPU, то панель управління застосовує типові значення: запит CPU 0.5 та типове обмеження CPU 1.

Ось маніфест для Podʼа з одним контейнером. Контейнер не вказує запит CPU та обмеження.

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx

Створіть Pod.

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod.yaml --namespace=default-cpu-example

Перегляньте специфікацію Podʼа :

kubectl get pod default-cpu-demo --output=yaml --namespace=default-cpu-example

Вивід показує, що єдиний контейнер Podʼа має запит CPU 500m cpu (що ви можете читати як “500 millicpu”), і обмеження CPU 1 cpu. Це типові значення, вказані обмеженням.

containers:
- image: nginx
  imagePullPolicy: Always
  name: default-cpu-demo-ctr
  resources:
    limits:
      cpu: "1"
    requests:
      cpu: 500m

Що якщо ви вказуєте обмеження контейнера, але не його запит?

Ось маніфест для Podʼа з одним контейнером. Контейнер вказує обмеження CPU, але не запит:

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-2
spec:
  containers:
  - name: default-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-2.yaml --namespace=default-cpu-example

Перегляньте специфікацію Podʼа, який ви створили:

kubectl get pod default-cpu-demo-2 --output=yaml --namespace=default-cpu-example

Вивід показує, що запит CPU контейнера встановлено таким чином, щоб відповідати його обмеженню CPU. Зверніть увагу, що контейнеру не було назначено типове значення запиту CPU 0.5 cpu:

resources:
  limits:
    cpu: "1"
  requests:
    cpu: "1"

Що якщо ви вказуєте запит контейнера, але не його обмеження?

Ось приклад маніфесту для Podʼа з одним контейнером. Контейнер вказує запит CPU, але не обмеження:

apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo-3
spec:
  containers:
  - name: default-cpu-demo-3-ctr
    image: nginx
    resources:
      requests:
        cpu: "0.75"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-defaults-pod-3.yaml --namespace=default-cpu-example

Перегляньте специфікацію Podʼа , який ви створили:

kubectl get pod default-cpu-demo-3 --output=yaml --namespace=default-cpu-example

Вивід показує, що запит CPU контейнера встановлено на значення, яке ви вказали при створенні Podʼа (іншими словами: воно відповідає маніфесту). Однак обмеження CPU цього ж контейнера встановлено на 1 cpu, що є типовим обмеженням CPU для цього простору імен.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 750m

Мотивація для типових обмежень та запитів CPU

Якщо ваш простір імен має налаштовану квоту ресурсів CPU, корисно мати типове значення для обмеження CPU. Ось два обмеження, які накладає квота ресурсів CPU на простір імен:

  • Для кожного Podʼа, який працює в просторі імен, кожен з його контейнерів повинен мати обмеження CPU.
  • Обмеження CPU застосовує резервування ресурсів на вузлі, де запускається відповідний Pod. Загальна кількість CPU, яка зарезервована для використання всіма Podʼами в просторі імен, не повинна перевищувати вказане обмеження.

Коли ви додаєте LimitRange:

Якщо будь-який Pod у цьому просторі імен, що містить контейнер, не вказує своє власне обмеження CPU, панель управління застосовує типове обмеження CPU цьому контейнеру, і Pod може отримати дозвіл на запуск у просторі імен, який обмежено квотою ресурсів CPU.

Прибирання

Видаліть ваш простір імен:

kubectl delete namespace default-cpu-example

Що далі

Для адміністраторів кластера

Для розробників додатків

2.4.3 - Налаштування мінімальних та максимальних обмежень памʼяті для простору імен

Визначте діапазон дійсних обмежень ресурсів памʼяті для простору імен, так щоб кожний новий Pod у цьому просторі імен знаходився в межах встановленого вами діапазону.

Ця сторінка показує, як встановити мінімальні та максимальні значення для памʼяті, яку використовують контейнери, що працюють у просторі імен. Мінімальні та максимальні значення памʼяті ви вказуєте у LimitRange обʼєкті. Якщо Pod не відповідає обмеженням, накладеним LimitRange, його неможливо створити в просторі імен.

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

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

У вас повинен бути доступ до створення просторів імен у вашому кластері.

Кожен вузол у вашому кластері повинен мати щонайменше 1 GiB памʼяті для Podʼів.

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте в цьому завданні, були відокремлені від решти вашого кластера.

kubectl create namespace constraints-mem-example

Створення LimitRange та Podʼа

Ось приклад маніфесту для LimitRange:

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

Створіть LimitRange:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints.yaml --namespace=constraints-mem-example

Перегляньте докладну інформацію про LimitRange:

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

Вивід показує мінімальні та максимальні обмеження памʼяті як очікувалося. Але зверніть увагу, що, навіть якщо ви не вказали типові значення в конфігураційному файлі для LimitRange, вони були створені автоматично.

  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

Тепер кожного разу, коли ви визначаєте Pod у просторі імен constraints-mem-example, Kubernetes виконує такі кроки:

  • Якщо будь-який контейнер в цьому Podʼі не вказує свій власний запит памʼяті та обмеження, панель управління надає типовий запит та обмеження памʼяті цьому контейнеру.

  • Перевірте, що кожний контейнер у цьому Podʼі запитує принаймні 500 MiB памʼяті.

  • Перевірте, що кожний контейнер у цьому Podʼі запитує не більше 1024 MiB (1 GiB) памʼяті.

Ось маніфест для Podʼа з одним контейнером. У специфікації Podʼа, єдиний контейнер вказує запит памʼяті 600 MiB та обмеження памʼяті 800 MiB. Ці значення задовольняють мінімальні та максимальні обмеження памʼяті, накладені LimitRange.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod.yaml --namespace=constraints-mem-example

Перевірте, що Pod працює і його контейнер є справним:

kubectl get pod constraints-mem-demo --namespace=constraints-mem-example

Перегляньте докладну інформацію про Pod:

kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example

Вивід показує, що контейнер у цьому Podʼі має запит памʼяті 600 MiB та обмеження памʼяті 800 MiB. Ці значення задовольняють обмеження, накладені LimitRange на цей простір імен:

resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi

Видаліть свій Pod:

kubectl delete pod constraints-mem-demo --namespace=constraints-mem-example

Спроба створення Podʼа, який перевищує максимальне обмеження памʼяті

Ось маніфест для Podʼа з одним контейнером. Контейнер вказує запит памʼяті 800 MiB та обмеження памʼяті 1.5 GiB.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"
      requests:
        memory: "800Mi"

Спробуйте створити Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

Вивід показує, що Pod не було створено, оскільки він визначає контейнер, який запитує більше памʼяті, ніж дозволяється:

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

Спроба створення Podʼа, який не відповідає мінімальному запиту памʼяті

Ось маніфест для Podʼа з одним контейнером. Цей контейнер вказує запит памʼяті 100 MiB та обмеження памʼяті 800 MiB.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"

Спробуйте створити Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-3.yaml --namespace=constraints-mem-example

Вивід показує, що Pod не було створено, оскільки він визначає контейнер який запитує менше памʼяті, ніж вимагається:

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

Створення Podʼа, який не вказує жодного запиту памʼяті чи обмеження

Ось маніфест для Podʼа з одним контейнером. Контейнер не вказує запиту памʼяті, і він не вказує обмеження памʼяті.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

Перегляньте докладну інформацію про Pod:

kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml

Вивід показує, що єдиний контейнер у цьому Podʼі має запит памʼяті 1 GiB та обмеження памʼяті 1 GiB. Як цей контейнер отримав ці значення?

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

Тому що ваш Pod не визначає жодного запиту памʼяті та обмеження для цього контейнера, кластер застосував типовий запит памʼяті та обмеження з LimitRange.

Це означає, що визначення цього Podʼа показує ці значення. Ви можете перевірити це за допомогою kubectl describe:

# Подивіться розділ "Requests:" виводу
kubectl describe pod constraints-mem-demo-4 --namespace=constraints-mem-example

На цей момент ваш Pod може працювати або не працювати. Памʼятайте, що передумовою для цього завдання є те, що ваші вузли мають щонайменше 1 GiB памʼяті. Якщо кожен з ваших вузлів має лише 1 GiB памʼяті, тоді недостатньо виділеної памʼяті на будь-якому вузлі для обслуговування запиту памʼяті 1 GiB. Якщо ви використовуєте вузли з 2 GiB памʼяті, то, ймовірно, у вас достатньо місця для розміщення запиту 1 GiB.

Видаліть свій Pod:

kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example

Застосування мінімальних та максимальних обмежень памʼяті

Максимальні та мінімальні обмеження памʼяті, накладені на простір імен LimitRange, діють тільки під час створення або оновлення Podʼа. Якщо ви змінюєте LimitRange, це не впливає на Podʼи, що були створені раніше.

Причини для мінімальних та максимальних обмежень памʼяті

Як адміністратор кластера, вам може знадобитися накладати обмеження на кількість памʼяті, яку можуть використовувати Podʼи. Наприклад:

  • Кожен вузол у кластері має 2 GiB памʼяті. Ви не хочете приймати будь-який Pod, який запитує більше ніж 2 GiB памʼяті, оскільки жоден вузол у кластері не може підтримати запит.

  • Кластер використовується як виробництвом, так і розробкою вашими відділами. Ви хочете дозволити навантаженням в експлуатації використовувати до 8 GiB памʼяті, але ви хочете обмежити навантаження в розробці до 512 MiB. Ви створюєте окремі простори імен для експлуатації та розробки і застосовуєте обмеження памʼяті для кожного простору імен.

Прибирання

Видаліть свій простір імен:

kubectl delete namespace constraints-mem-example

Що далі

Для адміністраторів кластера

Для розробників додатків

2.4.4 - Налаштування мінімальних та максимальних обмеженнь CPU для простору імен

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

Ця сторінка показує, як встановити мінімальні та максимальні значення ресурсів CPU, що використовуються контейнерами та Podʼами в просторі імен. Ви вказуєте мінімальні та максимальні значення CPU в обʼєкті LimitRange. Якщо Pod не відповідає обмеженням, накладеним LimitRange, його не можна створити у просторі імен.

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

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

Ви повинні мати доступ до створення просторів імен у своєму кластері.

Кожен вузол у вашому кластері повинен мати щонайменше 1,0 CPU, доступний для Podʼів. Див. значення CPU, щоб дізнатися, що означає в Kubernetes "1 CPU".

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були відокремлені від інших частин вашого кластера.

kubectl create namespace constraints-cpu-example

Створення LimitRange та Podʼа

Ось маніфест для прикладу LimitRange:

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
    min:
      cpu: "200m"
    type: Container

Створіть LimitRange:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints.yaml --namespace=constraints-cpu-example

Перегляньте детальну інформацію про LimitRange:

kubectl get limitrange cpu-min-max-demo-lr --output=yaml --namespace=constraints-cpu-example

Вивід показує мінімальні та максимальні обмеження CPU, як очікувалося. Але зверніть увагу, що навіть якщо ви не вказали типових значень у конфігураційному файлі для LimitRange, вони були створені автоматично.

limits:
- default:
    cpu: 800m
  defaultRequest:
    cpu: 800m
  max:
    cpu: 800m
  min:
    cpu: 200m
  type: Container

Тепер, кожного разу, коли ви створюєте Pod у просторі імен constraints-cpu-example (або який-небудь інший клієнт API Kubernetes створює еквівалентний Pod), Kubernetes виконує ці кроки:

  • Якщо який-небудь контейнер у цьому Podʼі не вказує свої власні CPU-запити та обмеження, панель управління призначає контейнеру типове значення для CPU-запиту та обмеження.

  • Перевірте, що кожен контейнер у цьому Podʼі вказує CPU-запит, який більший або дорівнює 200 мілі-CPU.

  • Перевірте, що кожен контейнер у цьому Podʼі вказує обмеження CPU, яке менше або дорівнює 800 мілі-CPU.

Ось маніфест для Podʼа з одним контейнером. Маніфест контейнера вказує CPU-запит у розмірі 500 мілі-CPU та обмеження CPU у розмірі 800 мілі-CPU. Це задовольняє мінімальні та максимальні обмеження CPU, накладені LimitRange на цей простір імен.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
spec:
  containers:
  - name: constraints-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "500m"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod.yaml --namespace=constraints-cpu-example

Перевірте, що Pod працює, а його контейнер є справним:

kubectl get pod constraints-cpu-demo --namespace=constraints-cpu-example

Перегляньте детальну інформацію про Pod:

kubectl get pod constraints-cpu-demo --output=yaml --namespace=constraints-cpu-example

Вивід показує, що єдиний контейнер Podʼа має запит CPU у розмірі 500 мілі-CPU та обмеження CPU 800 мілі-CPU. Це задовольняє обмеження, накладеним LimitRange.

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 500m

Видаліть Pod

kubectl delete pod constraints-cpu-demo --namespace=constraints-cpu-example

Спроба створити Pod, який перевищує максимальне обмеження CPU

Ось маніфест для Podʼа з одним контейнером. Контейнер вказує запит CPU у розмірі 500 мілі-CPU та обмеження CPU у розмірі 1,5 CPU.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-2
spec:
  containers:
  - name: constraints-cpu-demo-2-ctr
    image: nginx
    resources:
      limits:
        cpu: "1.5"
      requests:
        cpu: "500m"

Спробуйте створити Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-2.yaml --namespace=constraints-cpu-example

Вивід показує, що Pod не створено, оскільки визначений контейнер є неприйнятним. Цей контейнер є неприйнятним, оскільки він вказує обмеження CPU, яке занадто велике:

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-2.yaml":
pods "constraints-cpu-demo-2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m.

Спроба створити Pod, який не відповідає мінімальному запиту CPU

Ось маніфест для Podʼа з одним контейнером. Контейнер вказує запит CPU у розмірі 100 мілі-CPU та обмеження CPU у розмірі 800 мілі-CPU.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-3
spec:
  containers:
  - name: constraints-cpu-demo-3-ctr
    image: nginx
    resources:
      limits:
        cpu: "800m"
      requests:
        cpu: "100m"

Спробуйте створити Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-3.yaml --namespace=constraints-cpu-example

Вивід показує, що Pod не створено, оскільки визначений контейнер є неприйнятним. Цей контейнер є неприйнятним, оскільки він вказує запит CPU, який нижче мінімального:

Error from server (Forbidden): error when creating "examples/admin/resource/cpu-constraints-pod-3.yaml":
pods "constraints-cpu-demo-3" is forbidden: minimum cpu usage per Container is 200m, but request is 100m.

Створення Podʼа, який не вказує жодного запиту або обмеження CPU

Ось маніфест для Podʼа з одним контейнером. Контейнер не вказує запит CPU і не вказує обмеження CPU.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo-4
spec:
  containers:
  - name: constraints-cpu-demo-4-ctr
    image: vish/stress

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/cpu-constraints-pod-4.yaml --namespace=constraints-cpu-example

Перегляньте детальну інформацію про Pod:

kubectl get pod constraints-cpu-demo-4 --namespace=constraints-cpu-example --output=yaml

Вивід показує, що у Podʼі єдиний контейнер має запит CPU у розмірі 800 мілі-CPU та обмеження CPU у розмірі 800 мілі-CPU. Як цей контейнер отримав ці значення?

resources:
  limits:
    cpu: 800m
  requests:
    cpu: 800m

Тому що цей контейнер не вказав свій власний запит CPU та обмеження, панель управління застосовує стандартні обмеження та запит CPU з LimitRange для цього простору імен.

На цьому етапі ваш Pod може бути запущеним або не запущеним. Згадайте, що передумовою для цієї задачі є те, що у ваших вузлах повинно бути щонайменше 1 CPU для використання. Якщо в кожному вузлі у вас є лише 1 CPU, то, можливо, немає достатньої кількості CPU на будь-якому вузлі для виконання запиту у розмірі 800 мілі-CPU. Якщо ви використовуєте вузли з 2 CPU, то, ймовірно, у вас достатньо CPU для виконання запиту у розмірі 800 мілі-CPU.

Видаліть ваш Pod:

kubectl delete pod constraints-cpu-demo-4 --namespace=constraints-cpu-example

Застосування мінімальних та максимальних обмежень CPU

Максимальні та мінімальні обмеження CPU, накладені на простір імен за допомогою LimitRange, застосовуються лише при створенні або оновленні Podʼа. Якщо ви зміните LimitRange, це не вплине на Podʼи, які були створені раніше.

Причини для мінімальних та максимальних обмежень CPU

Як адміністратор кластера, ви можете бажати накладати обмеження на ресурси CPU, які можуть використовувати Podʼи. Наприклад:

  • Кожен вузол у кластері має 2 CPU. Ви не хочете приймати жодного Podʼа, який запитує більше, ніж 2 CPU, оскільки жоден вузол у кластері не може підтримати цей запит.

  • Кластер використовується вашими відділами експлуатації та розробки. Ви хочете дозволити навантаженням в експлуатації споживати до 3 CPU, але ви хочете обмежити навантаження в розробці до 1 CPU. Ви створюєте окремі простори імен для експлуатації та розробки та застосовуєте обмеження CPU до кожного простору імен.

Прибирання

Видаліть ваш простір імен:

kubectl delete namespace constraints-cpu-example

Що далі

Для адміністраторів кластера

Для розробників застосунків

2.4.5 - Налаштування квот памʼяті та CPU для простору імен

Визначте загальні обмеження памʼяті та CPU для всіх Podʼів, які працюють у просторі імен.

На цій сторінці показано, як встановити квоти для загальної кількості памʼяті та CPU, які можуть бути використані всіма Podʼами, що працюють у просторі імен. Ви вказуєте квоти в обʼєкті ResourceQuota.

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

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

Вам потрібен доступ до створення просторів імен у вашому кластері.

Кожен вузол у вашому кластері повинен мати принаймні 1 ГБ памʼяті.

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були ізольовані від інших частин вашого кластера.

kubectl create namespace quota-mem-cpu-example

Створення ResourceQuota

Ось маніфест для прикладу ResourceQuota:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-demo
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

Створіть ResourceQuota:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu.yaml --namespace=quota-mem-cpu-example

Перегляньте детальну інформацію про ResourceQuota:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

ResourceQuota накладає такі вимоги на простір імен quota-mem-cpu-example:

  • Для кожного Podʼа у просторі імен кожен контейнер повинен мати запит памʼяті, обмеження памʼяті, запит CPU та обмеження CPU.
  • Загальний запит памʼяті для всіх Podʼів у цьому просторі імен не повинен перевищувати 1 ГБ.
  • Загальне обмеження памʼяті для всіх Podʼів у цьому просторі імен не повинно перевищувати 2 ГБ.
  • Загальний запит CPU для всіх Podʼів у цьому просторі імен не повинен перевищувати 1 CPU.
  • Загальне обмеження CPU для всіх Podʼів у цьому просторі імен не повинно перевищувати 2 CPU.

Дивіться значення CPU, щоб дізнатися, що має на увазі Kubernetes, коли говорить про "1 CPU".

Створення Podʼа

Ось маніфест для прикладу Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo
spec:
  containers:
  - name: quota-mem-cpu-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
        cpu: "800m"
      requests:
        memory: "600Mi"
        cpu: "400m"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod.yaml --namespace=quota-mem-cpu-example

Перевірте, що Pod працює, і його (єдиний) контейнер є справним:

kubectl get pod quota-mem-cpu-demo --namespace=quota-mem-cpu-example

Знову перегляньте детальну інформацію про ResourceQuota:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml

У виводі вказано квоту разом з тим, скільки з квоти було використано. Ви можете побачити, що запити памʼяті та CPU для вашого Podʼа не перевищують квоту.

status:
  hard:
    limits.cpu: "2"
    limits.memory: 2Gi
    requests.cpu: "1"
    requests.memory: 1Gi
  used:
    limits.cpu: 800m
    limits.memory: 800Mi
    requests.cpu: 400m
    requests.memory: 600Mi

Якщо у вас є інструмент jq, ви також можете запитувати (використовуючи JSONPath) лише значення used, і друкувати ці значення з приємним форматуванням. Наприклад:

kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example -o jsonpath='{ .status.used }' | jq .

Спроба створити другий Pod

Ось маніфест для другого Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-demo-2
spec:
  containers:
  - name: quota-mem-cpu-demo-2-ctr
    image: redis
    resources:
      limits:
        memory: "1Gi"
        cpu: "800m"
      requests:
        memory: "700Mi"
        cpu: "400m"

У маніфесті можна побачити, що Pod має запит памʼяті 700 MiB. Зверніть увагу, що сума використаного запиту памʼяті та цього нового запиту памʼяті перевищує квоту запиту памʼяті: 600 MiB + 700 MiB > 1 GiB.

Спробуйте створити Pod:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example

Другий Pod не створюється. У виводі вказано, що створення другого Podʼа призведе до того, що загальний запит памʼяті перевищить квоту запиту памʼяті.

Error from server (Forbidden): error when creating "examples/admin/resource/quota-mem-cpu-pod-2.yaml":
pods "quota-mem-cpu-demo-2" is forbidden: exceeded quota: mem-cpu-demo,
requested: requests.memory=700Mi,used: requests.memory=600Mi, limited: requests.memory=1Gi

Обговорення

Як ви бачили у цьому завданні, ви можете використовувати ResourceQuota для обмеження загального запиту памʼяті для всіх Podʼів, що працюють у просторі імен. Ви також можете обмежити загальні суми для обмеження памʼяті, запиту CPU та обмеження CPU.

Замість керування загальним використанням ресурсів у просторі імен, ви, можливо, захочете обмежити окремі Podʼи або контейнери у цих Podʼах. Щоб досягти такого обмеження, використовуйте LimitRange.

Прибирання

Видаліть ваш простір імен:

kubectl delete namespace quota-mem-cpu-example

Що далі

Для адміністраторів кластера

Для розробників застосунків

2.4.6 - Налаштування квоти Podʼів для простору імен

Обмежте кількість Podʼів, які можна створити у просторі імен.

На цій сторінці показано, як встановити квоту на загальну кількість Podʼів, які можуть працювати в просторі імен. Ви вказуєте квоти в обʼєкті ResourceQuota.

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

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

Вам потрібен доступ до створення просторів імен у вашому кластері.

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були ізольовані від інших частин вашого кластера.

kubectl create namespace quota-pod-example

Створення ResourceQuota

Ось приклад маніфесту для ResourceQuota:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: pod-demo
spec:
  hard:
    pods: "2"

Створіть ResourceQuota:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod.yaml --namespace=quota-pod-example

Перегляньте детальну інформацію про ResourceQuota:

kubectl get resourcequota pod-demo --namespace=quota-pod-example --output=yaml

У виводі показано, що у просторі імен є квота на два Podʼи, і наразі немає Podʼів; іншими словами, жодна частина квоти не використовується.

spec:
  hard:
    pods: "2"
status:
  hard:
    pods: "2"
  used:
    pods: "0"

Ось приклад маніфесту для Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-quota-demo
spec:
  selector:
    matchLabels:
      purpose: quota-demo
  replicas: 3
  template:
    metadata:
      labels:
        purpose: quota-demo
    spec:
      containers:
      - name: pod-quota-demo
        image: nginx

У цьому маніфесті replicas: 3 повідомляє Kubernetes спробувати створити три нові Podʼи, які всі працюватимуть з одним і тим же застосунком.

Створіть Deployment:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-pod-deployment.yaml --namespace=quota-pod-example

Перегляньте детальну інформацію про Deployment:

kubectl get deployment pod-quota-demo --namespace=quota-pod-example --output=yaml

У виводі показано, що навіть якщо Deployment вказує три репліки, було створено лише два Podʼи через раніше визначену вами квоту:

spec:
  ...
  replicas: 3
...
status:
  availableReplicas: 2
...
lastUpdateTime: 2021-04-02T20:57:05Z
    message: 'unable to create pods: pods "pod-quota-demo-1650323038-" is forbidden:
      exceeded quota: pod-demo, requested: pods=1, used: pods=2, limited: pods=2'

Вибір ресурсу

У цьому завданні ви визначили ResourceQuota, яке обмежує загальну кількість Podʼів, але ви також можете обмежити загальну кількість інших видів обʼєктів. Наприклад, ви можете вирішити обмежити кількість CronJobs, які можуть існувати в одному просторі імен.

Прибирання

Видаліть ваш простір імен:

kubectl delete namespace quota-pod-example

Для адміністраторів кластера

Для розробників застосунків

2.5 - Встановлення постачальника мережевої політики

2.5.1 - Використання Antrea для NetworkPolicy

Ця сторінка показує, як встановити та використовувати втулок Antrea CNI в Kubernetes. Щоб дізнатися більше про проєкт Antrea, прочитайте Вступ до Antrea.

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

Вам потрібно мати кластер Kubernetes. Слідуйте початковому керівництву kubeadm для його створення.

Розгортання Antrea за допомогою kubeadm

Слідуйте керівництву Початок роботи для розгортання Antrea за допомогою kubeadm.

Що далі

Після того, як ваш кластер буде запущений, ви можете перейти до Оголошення мережевої політики, щоб спробувати в дії Kubernetes NetworkPolicy.

2.5.2 - Використання Calico для NetworkPolicy

Ця сторінка показує кілька швидких способів створення кластера Calico в Kubernetes.

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

Вирішіть, чи ви хочете розгорнути хмарний або локальний кластер.

Створення кластера Calico з Google Kubernetes Engine (GKE)

Передумова: gcloud.

  1. Щоб запустити кластер GKE з Calico, включіть прапорець --enable-network-policy.

    Синтаксис

    gcloud container clusters create [ІМ'Я_КЛАСТЕРА] --enable-network-policy
    

    Приклад

    gcloud container clusters create my-calico-cluster --enable-network-policy
    
  2. Для перевірки розгортання використовуйте наступну команду.

    kubectl get pods --namespace=kube-system
    

    Podʼи Calico починаються з calico. Перевірте, щоб кожен з них мав статус Running.

Створення локального кластера Calico з kubeadm

Щоб отримати локальний кластер Calico для одного хосту за пʼятнадцять хвилин за допомогою kubeadm, див. Швидкий старт Calico.

Що далі

Після того, як ваш кластер буде запущений, ви можете перейти до Оголошення мережевої політики, щоб спробувати в дії Kubernetes NetworkPolicy.

2.5.3 - Використання Cilium для NetworkPolicy

Ця сторінка показує, як використовувати Cilium для NetworkPolicy.

Щоб ознайомитися з основною інформацією про Cilium, прочитайте Вступ до Cilium.

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

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

Для перевірки версії введіть kubectl version.

Розгортання Cilium на Minikube для базового тестування

Щоб легко ознайомитися з Cilium, ви можете слідувати Початковому керівництву Cilium Kubernetes для виконання базової інсталяції Cilium як DaemonSet у Minikube.

Щоб запустити Minikube, мінімально необхідна версія >= v1.5.2, виконайте з наступними аргументами:

minikube version
minikube version: v1.5.2
minikube start --network-plugin=cni

Для Minikube ви можете встановити Cilium за допомогою його CLI інструменту. Спочатку завантажте останню версію CLI за допомогою наступної команди:

curl -LO https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz

Потім розпакуйте завантажений файл у вашу теку /usr/local/bin за допомогою наступної команди:

sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz

Після виконання вищевказаних команд, ви тепер можете встановити Cilium за допомогою наступної команди:

cilium install

Cilium автоматично визначить конфігурацію кластера та створить і встановить відповідні компоненти для успішної інсталяції. Компоненти включають:

  • Центр сертифікації (CA) у Secret cilium-ca та сертифікати для Hubble (шар спостереження Cilium).
  • Сервісні облікові записи.
  • Кластерні ролі.
  • ConfigMap.
  • Agent DaemonSet та Operator Deployment.

Після інсталяції ви можете переглянути загальний статус розгортання Cilium за допомогою команди cilium status. Дивіться очікуваний вивід команди status тут.

Решта Початкового керівництва пояснює, як застосувати політики безпеки як L3/L4 (тобто IP-адреса + порт), так і L7 (наприклад, HTTP) за допомогою прикладної програми.

Розгортання Cilium для використання в операційному середовищі

Для докладних інструкцій з розгортання Cilium для операційного використання, дивіться: Керівництво з інсталяції Cilium Kubernetes. Ця документація включає докладні вимоги, інструкції та приклади файлів DaemonSet для продуктивного використання.

Розуміння компонентів Cilium

Розгортання кластера з Cilium додає Podʼи до простору імен kube-system. Щоб побачити цей список Podʼів, виконайте:

kubectl get pods --namespace=kube-system -l k8s-app=cilium

Ви побачите список Podʼів, подібний до цього:

NAME           READY   STATUS    RESTARTS   AGE
cilium-kkdhz   1/1     Running   0          3m23s
...

Pod cilium працює на кожному вузлі вашого кластера і забезпечує виконання мережевої політики для трафіку до/від Podʼів на цьому вузлі за допомогою Linux BPF.

Що далі

Після того, як ваш кластер буде запущений, ви можете перейти до Оголошення мережевої політики для випробування Kubernetes NetworkPolicy з Cilium. Якщо у вас є запитання, звʼяжіться з нами за допомогою Каналу Cilium у Slack.

2.5.4 - Використання Kube-router для NetworkPolicy

Ця сторінка показує, як використовувати Kube-router для NetworkPolicy.

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

Вам потрібно мати запущений кластер Kubernetes. Якщо у вас ще немає кластера, ви можете створити його, використовуючи будь-які інсталятори кластерів, такі як Kops, Bootkube, Kubeadm тощо.

Встановлення надбудови Kube-router

Надбудова Kube-router містить контролер мережевих політик, який відстежує сервер API Kubernetes на предмет будь-яких оновлень NetworkPolicy та Podʼів і налаштовує правила iptables та ipsets для дозволу або блокування трафіку відповідно до політик. Будь ласка, слідуйте керівництву спробуйте Kube-router з інсталяторами кластерів для встановлення надбудови Kube-router.

Що далі

Після того, як ви встановили надбудову Kube-router, ви можете перейти до Оголошення мережевої політики для випробування Kubernetes NetworkPolicy.

2.5.5 - Використання Romana для NetworkPolicy

Ця сторінка показує, як використовувати Romana для NetworkPolicy.

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

Виконайте кроки 1, 2 та 3 з початкового керівництва kubeadm.

Встановлення Romana за допомогою kubeadm

Слідуйте керівництву з контейнеризованого встановлення для kubeadm.

Застосування мережевих політик

Для застосування мережевих політик використовуйте одне з наступного:

Що далі

Після встановлення Romana ви можете перейти до Оголошення мережевої політики для випробування Kubernetes NetworkPolicy.

2.5.6 - Використання Weave Net для NetworkPolicy

Ця сторінка показує, як використовувати Weave Net для NetworkPolicy.

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

Вам потрібен Kubernetes кластер. Слідуйте початковому керівництву kubeadm, щоб його налаштувати.

Встановлення надбудови Weave Net

Слідуйте керівництву Інтеграція Kubernetes через надбудову.

Надбудова Weave Net для Kubernetes містить Контролер мережевих політик, який автоматично відстежує всі анотації мережевих політик у Kubernetes у всіх просторах імен і налаштовує правила iptables для дозволу або блокування трафіку відповідно до цих політик.

Тестування встановлення

Перевірте, що Weave працює коректно.

Введіть наступну команду:

kubectl get pods -n kube-system -o wide

Вивід буде схожим на це:

NAME                                    READY     STATUS    RESTARTS   AGE       IP              NODE
weave-net-1t1qg                         2/2       Running   0          9d        192.168.2.10    worknode3
weave-net-231d7                         2/2       Running   1          7d        10.2.0.17       worknodegpu
weave-net-7nmwt                         2/2       Running   3          9d        192.168.2.131   masternode
weave-net-pmw8w                         2/2       Running   0          9d        192.168.2.216   worknode2

На кожному вузлі є Pod Weave, і всі Podʼи Running та 2/2 READY. (2/2 означає, що кожен Pod має weave і weave-npc.)

Що далі

Після встановлення надбудови Weave Net ви можете перейти до Оголошення мережевої політики, щоб спробувати Kubernetes NetworkPolicy. Якщо у вас є запитання, звертайтеся до нас #weave-community у Slack або Weave User Group.

2.6 - Доступ до кластера через API Kubernetes

Ця сторінка описує, як отримати доступ до кластера через API Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Доступ до API Kubernetes

Перший доступ за допомогою kubectl

При першому доступі до API Kubernetes використовуйте інструмент командного рядка Kubernetes, kubectl.

Для отримання доступу до кластера вам потрібно знати його розташування та мати облікові дані для входу. Зазвичай вони встановлюються автоматично, коли ви користуєтесь настановами зі сторінки Початок роботи, або ж ви вже маєте розгорнутий кластер з налаштованим доступом.

Перевірте місце знаходження та облікові дані, про які знає kubectl, за допомогою цієї команди:

kubectl config view

Багато прикладів містять введення в користування kubectl. Повну документацію ви можете знайти в довідці kubectl.

Прямий доступ до REST API

kubectl використовується для знаходження та автентифікації на сервері API. Якщо ви хочете дістатись REST API за допомогою інструментів на кшталт curl або wget, чи вебоглядача, існує кілька способів якими ви можете знайти та автентифікуватись на сервері API.

  1. Запустіть kubectl у режимі проксі (рекомендовано). Цей метод рекомендується, оскільки він використовує збережене розташування сервера API та перевіряє відповідність сервера API за допомогою самопідписного сертифіката. За допомогою цього методу неможлива атака man-in-the-middle (MITM).
  2. Крім того, ви можете вказати знаходження та облікові дані безпосередньо http-клієнту. Це працює з клієнтським кодом, який плутають проксі. Щоб захиститися від атак man in the middle, вам потрібно буде імпортувати кореневий сертифікат у свій вебоглядач.

Використання клієнтських бібліотек Go або Python забезпечує доступ до kubectl у режимі проксі.

Використання kubectl proxy

Наступна команда запускає kubectl у режимі, де він діє як зворотний проксі. Він виконує пошук сервера API та автентифікацію.

kubectl proxy --port=8080 &

Дивіться kubectl proxy для отримання додаткової інформації.

Потім ви можете дослідити API за допомогою curl, wget або вебоглядача, наприклад:

curl http://localhost:8080/api/

Вивід має бути схожий на цей:

{
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

Без використання kubectl proxy

Можна уникнути використання kubectl proxy, передаючи токен автентифікації безпосередньо на сервер API, наприклад:

Використовуючи підхід grep/cut:

# Перевірте всі можливі кластери, оскільки ваш .KUBECONFIG може мати кілька контекстів:
kubectl config view -o jsonpath='{"Cluster name\tServer\n"}{range .clusters[*]}{.name}{"\t"}{.cluster.server}{"\n"}{end}'

# Виберіть назву кластера, з яким ви хочете взаємодіяти, з виводу вище:
export CLUSTER_NAME="some_server_name"

# Вкажіть сервер API, посилаючись на імʼя кластера
APISERVER=$(kubectl config view -o jsonpath="{.clusters[?(@.name==\"$CLUSTER_NAME\")].cluster.server}")

# Створіть секрет для зберігання токена для облікового запису служби
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: default-token
  annotations:
    kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF

# Зачекайте, поки контролер заповнить секрет токеном:
while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
  echo "waiting for token..." >&2
  sleep 1
done

# Отримайте значення токена
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode)

# Дослідіть API скориставшись TOKEN
curl -X GET $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

Вивід має бути схожий на цей:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

У вищенаведеному прикладі використовується прапорець --insecure. Це залишає систему вразливою до атак типу MITM (Man-In-The-Middle). Коли kubectl отримує доступ до кластера, він використовує збережений кореневий сертифікат та сертифікати клієнта для доступу до сервера. (Ці дані встановлені у каталозі ~/.kube). Оскільки сертифікати кластера зазвичай самопідписні, може знадобитися спеціальна конфігурація, щоб ваш HTTP-клієнт використовував кореневий сертифікат.

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

Програмний доступ до API

Kubernetes офіційно підтримує клієнтські бібліотеки для Go, Python, Java, dotnet, JavaScript та Haskell. Існують інші клієнтські бібліотеки, які надаються та підтримуються їхніми авторами, а не командою Kubernetes. Дивіться бібліотеки клієнтів для доступу до API з інших мов програмування та їхнього методу автентифікації.

Go-клієнт

  • Щоб отримати бібліотеку, виконайте наступну команду: go get k8s.io/client-go@kubernetes-<номер-версії-kubernetes>. Дивіться https://github.com/kubernetes/client-go/releases, щоб переглянути підтримувані версії.
  • Напишіть застосунок поверх клієнтів client-go.

Go-клієнт може використовувати той самий файл kubeconfig, як і kubectl CLI, для пошуку та автентифікації на сервері API. Дивіться цей приклад:

package main

import (
  "context"
  "fmt"
  "k8s.io/apimachinery/pkg/apis/meta/v1"
  "k8s.io/client-go/kubernetes"
  "k8s.io/client-go/tools/clientcmd"
)

func main() {
  // використовуємо поточний контекст з kubeconfig
  // path-to-kubeconfig -- наприклад, /root/.kube/config
  config, _ := clientcmd.BuildConfigFromFlags("", "<path-to-kubeconfig>")
  // створює clientset
  clientset, _ := kubernetes.NewForConfig(config)
  // доступ API до списку Podʼів
  pods, _ := clientset.CoreV1().Pods("").List(context.TODO(), v1.ListOptions{})
  fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
}

Якщо застосунок розгорнуто як Pod у кластері, дивіться Доступ до API зсередини Pod.

Python-клієнт

Щоб використовувати Python-клієнт, виконайте наступну команду: pip install kubernetes. Дивіться сторінку бібліотеки Python-клієнта для отримання додаткових варіантів встановлення.

Python-клієнт може використовувати той самий файл kubeconfig, як і kubectl CLI, для пошуку та автентифікації на сервері API. Дивіться цей приклад:

from kubernetes import client, config

config.load_kube_config()

v1=client.CoreV1Api()
print("Listing pods with their IPs:")
ret = v1.list_pod_for_all_namespaces(watch=False)
for i in ret.items:
    print("%s\t%s\t%s" % (i.status.pod_ip, i.metadata.namespace, i.metadata.name))

Java-клієнт

Для встановлення Java-клієнта виконайте наступну команду:

# Зколнуйте код білліотеки java
git clone --recursive https://github.com/kubernetes-client/java

# Встановлення артефактів проєкту, POM й так даіл:
cd java
mvn install

Дивіться https://github.com/kubernetes-client/java/releases, щоб переглянути підтримувані версії.

Java-клієнт може використовувати той самий файл kubeconfig, що і kubectl CLI, для пошуку та автентифікації на сервері API. Дивіться цей приклад:

package io.kubernetes.client.examples;

import io.kubernetes.client.ApiClient;
import io.kubernetes.client.ApiException;
import io.kubernetes.client.Configuration;
import io.kubernetes.client.apis.CoreV1Api;
import io.kubernetes.client.models.V1Pod;
import io.kubernetes.client.models.V1PodList;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import java.io.FileReader;
import java.io.IOException;

/**
 * Простий приклад використання Java API з застосунку поза кластером Kubernetes.
 *
 * Найпростіший спосіб запуску цього: mvn exec:java
 * -Dexec.mainClass="io.kubernetes.client.examples.KubeConfigFileClientExample"
 *
 */
public class KubeConfigFileClientExample {
  public static void main(String[] args) throws IOException, ApiException {

    // шлях до файлуу KubeConfig
    String kubeConfigPath = "~/.kube/config";

    // завантаження конфігурації ззовні кластера, kubeconfig із файлової системи
    ApiClient client =
        ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();

    // встановлення глобального api-client на того, що працює в межах кластера, як описано вище
    Configuration.setDefaultApiClient(client);

    // the CoreV1Api завантажує api-client з глобальної конфігурації.
    CoreV1Api api = new CoreV1Api();

    // виклик клієнта CoreV1Api
    V1PodList list = api.listPodForAllNamespaces(null, null, null, null, null, null, null, null, null);
    System.out.println("Listing all pods: ");
    for (V1Pod item : list.getItems()) {
      System.out.println(item.getMetadata().getName());
    }
  }
}

dotnet-клієнт

Щоб використовувати dotnet-клієнт, виконайте наступну команду: dotnet add package KubernetesClient --version 1.6.1. Дивіться сторінку бібліотеки dotnet-клієнта для отримання додаткових варіантів встановлення. Дивіться https://github.com/kubernetes-client/csharp/releases, щоб переглянути підтримувані версії.

Dotnet-клієнт може використовувати той самий файл kubeconfig, що і kubectl CLI, для пошуку та автентифікації на сервері API. Дивіться цей приклад:

using System;
using k8s;

namespace simple
{
    internal class PodList
    {
        private static void Main(string[] args)
        {
            var config = KubernetesClientConfiguration.BuildDefaultConfig();
            IKubernetes client = new Kubernetes(config);
            Console.WriteLine("Starting Request!");

            var list = client.ListNamespacedPod("default");
            foreach (var item in list.Items)
            {
                Console.WriteLine(item.Metadata.Name);
            }
            if (list.Items.Count == 0)
            {
                Console.WriteLine("Empty!");
            }
        }
    }
}

JavaScript-клієнт

Щоб встановити JavaScript-клієнт, виконайте наступну команду: npm install @kubernetes/client-node. Дивіться сторінку бібліотеки JavaScript-клієнта для отримання додаткових варіантів встановлення. Дивіться https://github.com/kubernetes-client/javascript/releases, щоб переглянути підтримувані версії.

JavaScript-клієнт може використовувати той самий файл kubeconfig, що і kubectl CLI, для пошуку та автентифікації на сервері API. Дивіться цей приклад:

const k8s = require('@kubernetes/client-node');

const kc = new k8s.KubeConfig();
kc.loadFromDefault();

const k8sApi = kc.makeApiClient(k8s.CoreV1Api);

k8sApi.listNamespacedPod('default').then((res) => {
    console.log(res.body);
});

Haskell-клієнт

Дивіться https://github.com/kubernetes-client/haskell/releases, щоб переглянути підтримувані версії.

Haskell-клієнт може використовувати той самий файл kubeconfig, що і kubectl CLI, для пошуку та автентифікації на сервері API. Дивіться цей приклад:

exampleWithKubeConfig :: IO ()
exampleWithKubeConfig = do
    oidcCache <- atomically $ newTVar $ Map.fromList []
    (mgr, kcfg) <- mkKubeClientConfig oidcCache $ KubeConfigFile "/path/to/kubeconfig"
    dispatchMime
            mgr
            kcfg
            (CoreV1.listPodForAllNamespaces (Accept MimeJSON))
        >>= print

Що далі

2.7 - Оголошення розширених ресурсів для вузла

Ця сторінка показує, як вказати розширені ресурси для вузла. Розширені ресурси дозволяють адміністраторам кластера оголошувати ресурси на рівні вузла, які інакше були б невідомі для Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Отримання імен ваших вузлів

kubectl get nodes

Виберіть один з ваших вузлів для цього завдання.

Щоб оголосити новий розширений ресурс на вузлі, відправте HTTP PATCH запит до сервера API Kubernetes. Наприклад, припустимо, що один з ваших вузлів має чотири підключені dongle. Ось приклад запиту PATCH, який оголошує чотири ресурси dongle для вашого вузла.

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "add",
    "path": "/status/capacity/example.com~1dongle",
    "value": "4"
  }
]

Зверніть увагу, що Kubernetes не потрібно знати, що таке dongle або для чого він призначений. Попередній запит PATCH повідомляє Kubernetes, що ваш вузол має чотири речі, які ви називаєте dongle.

Запустіть проксі, щоб відправляти запити на сервер API Kubernetes:

kubectl proxy

У іншому вікні термінала відправте HTTP PATCH запит. Замініть <your-node-name> на імʼя вашого вузла:

curl --header "Content-Type: application/json-patch+json" \
  --request PATCH \
  --data '[{"op": "add", "path": "/status/capacity/example.com~1dongle", "value": "4"}]' \
  http://localhost:8001/api/v1/nodes/<your-node-name>/status

Вивід показує, що вузол має потужність 4 dongle:

"capacity": {
  "cpu": "2",
  "memory": "2049008Ki",
  "example.com/dongle": "4",

Опишіть свій вузол:

kubectl describe node <your-node-name>

Ще раз, вивід показує ресурс dongle:

Capacity:
  cpu: 2
  memory: 2049008Ki
  example.com/dongle: 4

Тепер розробники застосунків можуть створювати Podʼи, які вимагають певну кількість dongle. Див. Призначення розширених ресурсів контейнеру.

Обговорення

Розширені ресурси схожі на памʼять та ресурси CPU. Наприклад, так само як на вузлі є певна кількість памʼяті та CPU для спільного використання всіма компонентами, що працюють на вузлі, він може мати певну кількість dongle для спільного використання всіма компонентами, що працюють на вузлі. І так само як розробники застосунків можуть створювати Podʼи, які вимагають певної кількості памʼяті та CPU, вони можуть створювати Podʼи, які вимагають певну кількість dongle.

Розширені ресурси є непрозорими для Kubernetes; Kubernetes не знає нічого про їх призначення. Kubernetes знає лише, що у вузла є їх певна кількість. Розширені ресурси мають оголошуватись у цілих числах. Наприклад, вузол може оголошувати чотири dongle, але не 4,5 dongle.

Приклад зберігання

Припустимо, що вузол має 800 ГіБ особливого типу дискового простору. Ви можете створити назву для спеціального сховища, скажімо, example.com/special-storage. Потім ви можете оголошувати його в частинах певного розміру, скажімо, 100 ГіБ. У цьому випадку, ваш вузол буде повідомляти, що в ньому є вісім ресурсів типу example.com/special-storage.

Capacity:
 ...
 example.com/special-storage: 8

Якщо ви хочете дозволити довільні запити на спеціальне зберігання, ви можете оголошувати спеціальне сховище частками розміром 1 байт. У цьому випадку ви оголошуєте 800Gi ресурсів типу example.com/special-storage.

Capacity:
 ...
 example.com/special-storage:  800Gi

Потім контейнер може запросити будь-яку кількість байтів спеціального сховища, до 800Gi.

Очищення

Ось запит PATCH, який видаляє оголошення dongle з вузла.

PATCH /api/v1/nodes/<your-node-name>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "remove",
    "path": "/status/capacity/example.com~1dongle",
  }
]

Запустіть проксі, щоб відправляти запити на сервер API Kubernetes:

kubectl proxy

У іншому вікні термінала відправте HTTP PATCH запит. Замініть <your-node-name> на імʼя вашого вузла:

curl --header "Content-Type: application/json-patch+json" \
  --request PATCH \
  --data '[{"op": "remove", "path": "/status/capacity/example.com~1dongle"}]' \
  http://localhost:8001/api/v1/nodes/<your-node-name>/status

Перевірте, що оголошення dongle було видалено:

kubectl describe node <your-node-name> | grep dongle

(ви не повинні бачити жодного виводу)

Що далі

Для розробників застосунків

Для адміністраторів кластера

2.8 - Автоматичне масштабування служби DNS в кластері

Ця сторінка показує, як увімкнути та налаштувати автоматичне масштабування служби DNS у вашому кластері Kubernetes.

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

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

    Для перевірки версії введіть kubectl version.

  • Цей посібник передбачає, що ваші вузли використовують архітектуру процесора AMD64 або Intel 64.

  • Переконайтеся, що DNS Kubernetes увімкнений.

Визначте, чи вже увімкнуто горизонтальне автоматичне масштабування DNS

Перегляньте Deploymentʼи у вашому кластері у просторі імен kube-system:

kubectl get deployment --namespace=kube-system

Вивід схожий на такий:

NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
...
kube-dns-autoscaler    1/1     1            1           ...
...

Якщо ви бачите "kube-dns-autoscaler" у виводі, горизонтальне автоматичне масштабування DNS вже увімкнено, і ви можете перейти до Налаштування параметрів автоматичного масштабування.

Отримайте імʼя вашого Deployment DNS

Перегляньте Deployment DNS у вашому кластері у просторі імен kube-system:

kubectl get deployment -l k8s-app=kube-dns --namespace=kube-system

Вивід схожий на такий:

NAME      READY   UP-TO-DATE   AVAILABLE   AGE
...
coredns   2/2     2            2           ...
...

Якщо ви не бачите Deployment для DNS-служб, ви також можете знайти його за імʼям:

kubectl get deployment --namespace=kube-system

і пошукайте Deployment з назвою coredns або kube-dns.

Ваша ціль масштабування:

Deployment/<імʼя вашого розгортання>

де <імʼя вашого розгортання> — це імʼя вашого Deployment DNS. Наприклад, якщо імʼя вашого Deployment для DNS — coredns, ваша ціль масштабування — Deployment/coredns.

Увімкніть горизонтальне автоматичне масштабування DNS

У цьому розділі ви створюєте нове Deployment. Podʼи в Deployment працюють з контейнером на основі образу cluster-proportional-autoscaler-amd64.

Створіть файл з назвою dns-horizontal-autoscaler.yaml з таким вмістом:

kind: ServiceAccount
apiVersion: v1
metadata:
  name: kube-dns-autoscaler
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:kube-dns-autoscaler
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["list", "watch"]
  - apiGroups: [""]
    resources: ["replicationcontrollers/scale"]
    verbs: ["get", "update"]
  - apiGroups: ["apps"]
    resources: ["deployments/scale", "replicasets/scale"]
    verbs: ["get", "update"]
# Remove the configmaps rule once below issue is fixed:
# kubernetes-incubator/cluster-proportional-autoscaler#16
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "create"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:kube-dns-autoscaler
subjects:
  - kind: ServiceAccount
    name: kube-dns-autoscaler
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-dns-autoscaler
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube-dns-autoscaler
  namespace: kube-system
  labels:
    k8s-app: kube-dns-autoscaler
    kubernetes.io/cluster-service: "true"
spec:
  selector:
    matchLabels:
      k8s-app: kube-dns-autoscaler
  template:
    metadata:
      labels:
        k8s-app: kube-dns-autoscaler
    spec:
      priorityClassName: system-cluster-critical
      securityContext:
        seccompProfile:
          type: RuntimeDefault
        supplementalGroups: [ 65534 ]
        fsGroup: 65534
      nodeSelector:
        kubernetes.io/os: linux
      containers:
      - name: autoscaler
        image: registry.k8s.io/cpa/cluster-proportional-autoscaler:1.8.4
        resources:
            requests:
                cpu: "20m"
                memory: "10Mi"
        command:
          - /cluster-proportional-autoscaler
          - --namespace=kube-system
          - --configmap=kube-dns-autoscaler
          # Should keep target in sync with cluster/addons/dns/kube-dns.yaml.base
          - --target=<SCALE_TARGET>
          # When cluster is using large nodes(with more cores), "coresPerReplica" should dominate.
          # If using small nodes, "nodesPerReplica" should dominate.
          - --default-params={"linear":{"coresPerReplica":256,"nodesPerReplica":16,"preventSinglePointFailure":true,"includeUnschedulableNodes":true}}
          - --logtostderr=true
          - --v=2
      tolerations:
      - key: "CriticalAddonsOnly"
        operator: "Exists"
      serviceAccountName: kube-dns-autoscaler

У файлі замініть <SCALE_TARGET> на вашу ціль масштабування.

Перейдіть в теку, яка містить ваш файл конфігурації, та введіть цю команду для створення Deployment:

kubectl apply -f dns-horizontal-autoscaler.yaml

Вивід успішної команди:

deployment.apps/kube-dns-autoscaler created

Тепер горизонтальне автоматичне масштабування DNS увімкнено.

Налаштування параметрів автоматичного масштабування DNS

Перевірте, що існує ConfigMap kube-dns-autoscaler:

kubectl get configmap --namespace=kube-system

Вивід схожий на такий:

NAME                  DATA      AGE
...
kube-dns-autoscaler   1

     ...
...

Змініть дані в ConfigMap:

kubectl edit configmap kube-dns-autoscaler --namespace=kube-system

Знайдіть цей рядок:

linear: '{"coresPerReplica":256,"min":1,"nodesPerReplica":16}'

Змініть поля відповідно до ваших потреб. Поле "min" вказує на мінімальну кількість резервних DNS. Фактична кількість резервних копій обчислюється за цією формулою:

replicas = max( ceil( cores × 1/coresPerReplica ) , ceil( nodes × 1/nodesPerReplica ) )

Зверніть увагу, що значення як coresPerReplica, так і nodesPerReplica — це числа з комою.

Ідея полягає в тому, що, коли кластер використовує вузли з багатьма ядрами, coresPerReplica домінує. Коли кластер використовує вузли з меншою кількістю ядер, домінує nodesPerReplica.

Існують інші підтримувані шаблони масштабування. Докладні відомості див. у cluster-proportional-autoscaler.

Вимкніть горизонтальне автоматичне масштабування DNS

Існують декілька варіантів налаштування горизонтального автоматичного масштабування DNS. Який варіант використовувати залежить від різних умов.

Опція 1: Зменшити масштаб розгортання kube-dns-autoscaler до 0 резервних копій

Цей варіант працює для всіх ситуацій. Введіть цю команду:

kubectl scale deployment --replicas=0 kube-dns-autoscaler --namespace=kube-system

Вивід:

deployment.apps/kube-dns-autoscaler scaled

Перевірте, що кількість резервних копій дорівнює нулю:

kubectl get rs --namespace=kube-system

Вивід показує 0 в колонках DESIRED та CURRENT:

NAME                                  DESIRED   CURRENT   READY   AGE
...
kube-dns-autoscaler-6b59789fc8        0         0         0       ...
...

Опція 2: Видаліть розгортання kube-dns-autoscaler

Цей варіант працює, якщо kube-dns-autoscaler знаходиться під вашим контролем, що означає, що його ніхто не буде знову створювати:

kubectl delete deployment kube-dns-autoscaler --namespace=kube-system

Вивід:

deployment.apps "kube-dns-autoscaler" deleted

Опція 3: Видаліть файл маніфесту kube-dns-autoscaler з майстер-вузла

Цей варіант працює, якщо kube-dns-autoscaler знаходиться під контролем (застарілий) Addon Manager, і ви маєте права запису на майстер-вузол.

Увійдіть на майстер-вузол та видаліть відповідний файл маніфесту. Загальний шлях для цього kube-dns-autoscaler:

/etc/kubernetes/addons/dns-horizontal-autoscaler/dns-horizontal-autoscaler.yaml

Після видалення файлу маніфесту Addon Manager видалить розгортання kube-dns-autoscaler.

Розуміння того, як працює горизонтальне автоматичне масштабування DNS

  • Застосунок cluster-proportional-autoscaler розгортається окремо від служби DNS.

  • Pod автомасштабування працює клієнтом, який опитує сервер API Kubernetes для отримання кількості вузлів та ядер у кластері.

  • Обчислюється та застосовується бажана кількість резервних копій на основі поточних запланованих вузлів та ядер та заданих параметрів масштабування.

  • Параметри масштабування та точки даних надаються через ConfigMap Podʼа автомасштабування, і він оновлює свою таблицю параметрів щоразу під час періодичного опитування, щоб вона була актуальною з найновішими бажаними параметрами масштабування.

  • Зміни параметрів масштабування дозволені без перебудови або перезапуску Podʼа автомасштабування.

  • Автомасштабування надає інтерфейс контролера для підтримки двох шаблонів керування: linear та ladder.

Що далі

2.9 - Зміна режиму доступу до PersistentVolume на ReadWriteOncePod

Ця сторінка показує, як змінити режим доступу наявного PersistentVolume на ReadWriteOncePod.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.22. Для перевірки версії введіть kubectl version.

Чому слід використовувати ReadWriteOncePod?

До версії Kubernetes v1.22 часто використовувався режим доступу ReadWriteOnce, щоб обмежити доступ до PersistentVolume для робочих навантажень, яким потрібен доступ одноосібного запису до сховища. Однак цей режим доступу мав обмеження: він обмежував доступ до тому одним вузлом, дозволяючи кільком Podʼам на одному вузлі одночасно читати та записувати в один і той же том. Це могло становити ризик для застосунків, які вимагають строгого доступу одноосібного запису для безпеки даних.

Якщо забезпечення доступу одноосібного запису є критичним для ваших робочих навантажень, розгляньте можливість міграції ваших томів на ReadWriteOncePod.

Міграція наявних PersistentVolumes

Якщо у вас є наявні PersistentVolumes, їх можна мігрувати на використання ReadWriteOncePod. Підтримується тільки міграція з ReadWriteOnce на ReadWriteOncePod.

У цьому прикладі вже є ReadWriteOnce "cat-pictures-pvc" PersistentVolumeClaim, який привʼязаний до "cat-pictures-pv" PersistentVolume, і "cat-pictures-writer" Deployment, який використовує цей PersistentVolumeClaim.

І ви можете переглянути PVC перед тим, як робити зміни. Перегляньте маніфест локально або виконайте kubectl get pvc <name-of-pvc> -o yaml. Вивід буде схожий на:

# cat-pictures-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: cat-pictures-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Ось приклад Deployment, який залежить від цього PersistentVolumeClaim:

# cat-pictures-writer-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cat-pictures-writer
spec:
  replicas: 3
  selector:
    matchLabels:
      app: cat-pictures-writer
  template:
    metadata:
      labels:
        app: cat-pictures-writer
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
        volumeMounts:
        - name: cat-pictures
          mountPath: /mnt
      volumes:
      - name: cat-pictures
        persistentVolumeClaim:
          claimName: cat-pictures-pvc
          readOnly: false

На першому етапі вам потрібно відредагувати spec.persistentVolumeReclaimPolicy вашого PersistentVolume і встановити його на Retain. Це забезпечить, що ваш PersistentVolume не буде видалений, коли ви видалите відповідний PersistentVolumeClaim:

kubectl patch pv cat-pictures-pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'

Потім вам потрібно зупинити будь-які робочі навантаження, які використовують PersistentVolumeClaim, повʼязаний з PersistentVolume, який ви хочете мігрувати, а потім видалити PersistentVolumeClaim. Уникайте вносити будь-які інші зміни до PersistentVolumeClaim, такі як зміна розміру тому, поки міграція не буде завершена.

Якщо це зроблено, вам потрібно очистити spec.claimRef.uid вашого PersistentVolume, щоб забезпечити можливість звʼязування PersistentVolumeClaims з ним під час перестворення:

kubectl scale --replicas=0 deployment cat-pictures-writer
kubectl delete pvc cat-pictures-pvc
kubectl patch pv cat-pictures-pv -p '{"spec":{"claimRef":{"uid":""}}}'

Після цього замініть список дійсних режимів доступу до PersistentVolume, щоб був (тільки) ReadWriteOncePod:

kubectl patch pv cat-pictures-pv -p '{"spec":{"accessModes":["ReadWriteOncePod"]}}'

Після цього вам потрібно змінити ваш PersistentVolumeClaim, щоб встановити ReadWriteOncePod як єдиний режим доступу. Ви також повинні встановити spec.volumeName PersistentVolumeClaim на назву вашого PersistentVolume, щоб забезпечити його привʼязку саме до цього конкретного PersistentVolume.

Після цього ви можете перестворити ваш PersistentVolumeClaim та запустити ваші робочі навантаження:

# ВАЖЛИВО: Переконайтеся, що ви відредагували свій PVC в cat-pictures-pvc.yaml перед застосуванням. Вам потрібно:
# - Встановити ReadWriteOncePod як єдиний режим доступу
# - Встановити spec.volumeName на "cat-pictures-pv"

kubectl apply -f cat-pictures-pvc.yaml
kubectl apply -f cat-pictures-writer-deployment.yaml

Нарешті, ви можете відредагувати spec.persistentVolumeReclaimPolicy вашого PersistentVolume і встановити його знову на Delete, якщо ви раніше змінювали його.

kubectl patch pv cat-pictures-pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Delete"}}'

Що далі

2.10 - Зміна типового StorageClass

Ця сторінка показує, як змінити типовий StorageClass, який використовується для створення томів для PersistentVolumeClaims, які не мають особливих вимог.

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

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

Для перевірки версії введіть kubectl version.

Чому змінювати типовий StorageClass?

Залежно від методу встановлення, ваш кластер Kubernetes може бути розгорнутий з наявним StorageClass, який позначений як типовий. Цей типовий StorageClass потім використовується для динамічного створення сховищ для PersistentVolumeClaims, які не вимагають будь-якого конкретного класу сховища. Див. документацію по PersistentVolumeClaim для деталей.

Попередньо встановлений типовий StorageClass може не відповідати вашим очікуваним навантаженням; наприклад, він може створювати занадто дороге сховище. У цьому випадку ви можете змінити типовий StorageClass або повністю вимкнути його, щоб уникнути динамічного створення сховища.

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

Зміна типового StorageClass

  1. Перегляньте StorageClasses у вашому кластері:

    kubectl get storageclass
    

    Вивід буде схожий на це:

    NAME                 PROVISIONER               AGE
    standard (default)   kubernetes.io/gce-pd      1д
    gold                 kubernetes.io/gce-pd      1д
    

    Типовий StorageClass позначений як (default).

  2. Позначте типовий StorageClass як не типовий:

    У типового StorageClass є анотація storageclass.kubernetes.io/is-default-class, встановлена на true. Будь-яке інше значення або відсутність анотації розглядається як false.

    Щоб позначити StorageClass як не типовий, вам потрібно змінити його значення на false:

    kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
    

    де standard — це назва вашого вибраного StorageClass.

  3. Позначте StorageClass як типовий:

    Аналогічно попередньому кроку, вам потрібно додати/встановити анотацію storageclass.kubernetes.io/is-default-class=true.

    kubectl patch storageclass gold -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
    

    Зверніть увагу, що максимум один StorageClass може бути позначений як типовий. Якщо два або більше з них позначені як типові, PersistentVolumeClaim без явно вказаного storageClassName не може бути створений.

  4. Перевірте, що ваш вибраний StorageClass є default:

    kubectl get storageclass
    

    Вивід буде схожий на це:

    NAME             PROVISIONER               AGE
    standard         kubernetes.io/gce-pd      1д
    gold (default)   kubernetes.io/gce-pd      1д
    

Що далі

2.11 - Перехід від опитування до оновлення стану контейнера на основі подій CRI

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.25 [alpha]

Ця сторінка показує, як мігрувати вузли для використання оновлення стану контейнера на основі подій. Реалізація на основі подій зменшує використання ресурсів вузла kubeletʼом порівняно зі старим підходом, який ґрунтується на опитуванні. Ви можете знати цю функцію як evented Pod lifecycle event generator (PLEG). Це назва, яка використовується внутрішньо в проєкті Kubernetes для основних деталей реалізації.

Підхід на основі опитування відомий як generic PLEG.

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

  • Ви повинні запускати версію Kubernetes, яка надає цю функцію. Kubernetes v1.27 включає підтримку бета-версії оновлення стану контейнера на основі подій. Функція є бета-версією, але стандартно вимкнена, оскільки вона потребує підтримки від середовища виконання контейнерів.
  • Версія вашого Kubernetes сервера має бути не старішою ніж 1.26. Для перевірки версії введіть kubectl version. Якщо ви використовуєте іншу версію Kubernetes, перевірте документацію для цього релізу.
  • Використане контейнерне середовище повинно підтримувати події життєвого циклу контейнера. Kubelet автоматично повертається до старого механізму опитування generic PLEG, якщо контейнерне середовище не оголошує підтримку подій життєвого циклу контейнера, навіть якщо у вас увімкнено цей функціонал.

Навіщо переходити на Evented PLEG?

  • Generic PLEG викликає значне навантаження через часте опитування стану контейнерів.
  • Це навантаження загострюється паралельним опитуванням стану контейнерів kublet, що обмежує його масштабованість та призводить до проблем з поганим функціонуванням та надійністю.
  • Мета Evented PLEG — зменшити непотрібну роботу під час бездіяльності шляхом заміни періодичного опитування.

Перехід на Evented PLEG

  1. Запустіть Kubelet з увімкненою функціональною можливістю EventedPLEG. Ви можете керувати feature gate kubelet редагуючи файл конфігурації kubelet і перезапустіть службу kubelet. Вам потрібно зробити це на кожному вузлі, де ви використовуєте цю функцію.

  2. Переконайтеся, що вузол виведено з експлуатації перед продовженням.

  3. Запустіть контейнерне середовище з увімкненою генерацією подій контейнера.

    Версія 1.7+

    Версія 1.26+

    Перевірте, чи CRI-O вже налаштований на відправлення CRI-подій, перевіривши конфігурацію,

    crio config | grep enable_pod_events
    

    Якщо він увімкнений, вивід повинен бути схожий на наступний:

    enable_pod_events = true
    

    Щоб увімкнути його, запустіть демон CRI-O з прапорцем --enable-pod-events=true або використовуйте конфігурацію dropin з наступними рядками:

    [crio.runtime]
    enable_pod_events: true
    
    Версія вашого Kubernetes сервера має бути не старішою ніж 1.26. Для перевірки версії введіть kubectl version.
  4. Перевірте, що kubelet використовує моніторинг стану контейнера на основі подій. Щоб перевірити це, шукайте термін EventedPLEG в журналах kubelet.

    Вивід буде схожий на це:

    I0314 11:10:13.909915 1105457 feature_gate.go:249] feature gates: &{map[EventedPLEG:true]}
    

    Якщо ви встановили --v у значення 4 і вище, ви можете побачити більше записів, що підтверджують, що kubelet використовує моніторинг стану контейнера на основі подій.

    I0314 11:12:42.009542 1110177 evented.go:238] "Evented PLEG: Generated pod status from the received event" podUID=3b2c6172-b112-447a-ba96-94e7022912dc
    I0314 11:12:44.623326 1110177 evented.go:238] "Evented PLEG: Generated pod status from the received event" podUID=b3fba5ea-a8c5-4b76-8f43-481e17e8ec40
    I0314 11:12:44.714564 1110177 evented.go:238] "Evented PLEG: Generated pod status from the received event" podUID=b3fba5ea-a8c5-4b76-8f43-481e17e8ec40
    

Що далі

2.12 - Зміна політики відновлення PersistentVolume

Ця сторінка показує, як змінити політику відновлення PersistentVolume в Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Навіщо змінювати політику відновлення PersistentVolume

У PersistentVolume можуть бути різні політики відновлення, включаючи "Retain", "Recycle" та "Delete". Для динамічно створених PersistentVolume типовою політикою відновлення є "Delete". Це означає, що динамічно створений том автоматично видаляється, коли користувач видаляє відповідний PersistentVolumeClaim. Ця автоматична поведінка може бути неприйнятною, якщо том містить важливі дані. У цьому випадку більш відповідною буде політика "Retain". З політикою "Retain", якщо користувач видаляє PersistentVolumeClaim, відповідний PersistentVolume не буде видалено. Замість цього він переміщується до фази Released, де всі його дані можуть бути вручну відновлені.

Зміна політики відновлення PersistentVolume

  1. Виведіть список PersistentVolume в вашому кластері:

    kubectl get pv
    

    Вивід буде схожий на такий:

    NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
    pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     10s
    pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     6s
    pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim3    manual                     3s
    

    Цей список також включає імена заявок, які привʼязані до кожного тому для полегшення ідентифікації динамічно створених томів.

  2. Виберіть один з ваших PersistentVolume та змініть його політику відновлення:

    kubectl patch pv <your-pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
    

    де <your-pv-name> — це імʼя вашого обраного PersistentVolume.

  3. Перевірте, що ваш обраний PersistentVolume має відповідну політику:

    kubectl get pv
    

    Вивід буде схожий на такий:

    NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM             STORAGECLASS     REASON    AGE
    pvc-b6efd8da-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim1    manual                     40s
    pvc-b95650f8-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Delete          Bound     default/claim2    manual                     36s
    pvc-bb3ca71d-b7b5-11e6-9d58-0ed433a7dd94   4Gi        RWO           Retain          Bound     default/claim3    manual                     33s
    

    У попередньому виводі ви можете побачити, що том, привʼязаний до заявки default/claim3, має політику відновлення Retain. Він не буде автоматично видалений, коли користувач видаляє заявку default/claim3.

Що далі

Посилання

2.13 - Адміністрування менеджера хмарного контролера

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.11 [beta]

Оскільки хмарні провайдери розвиваються та випускають нові функції в іншому темпі порівняно з проєктом Kubernetes, абстрагування провайдеро-залежного коду у бінарний файл cloud-controller-manager дозволяє хмарним постачальникам розвиватися незалежно від основного коду Kubernetes.

cloud-controller-manager може бути повʼязаний з будь-яким хмарним провайдером, який відповідає вимогам cloudprovider.Interface. Для забезпечення зворотної сумісності, cloud-controller-manager, що постачається у складі основного проєкту Kubernetes, використовує ті ж хмарні бібліотеки, що і kube-controller-manager. Очікується, що хмарні постачальники, які вже підтримуються у ядрі Kubernetes, використовуватимуть вбудований cloud-controller-manager для передачі даних з ядра Kubernetes.

Адміністрування

Вимоги

Кожна хмара має свій власний набір вимог для запуску своєї інтеграції з власним провайдером, це не повинно суттєво відрізнятися від вимог до запуску kube-controller-manager. Загалом вам знадобиться:

  • автентифікація/авторизація в хмарі: вашій хмарі може знадобитися токен або правила IAM, щоб дозволити доступ до їхніх API
  • автентифікація/авторизація в Kubernetes: cloud-controller-manager може потребувати налаштування RBAC правила для звʼязку з apiserver Kubernetes
  • висока доступність: як і kube-controller-manager, вам може знадобитися налаштування високої доступності для cloud controller manager з використанням вибору лідера (стандартно увімкнено).

Робота cloud-controller-manager

Успішне виконання cloud-controller-manager вимагає деяких змін у конфігурації кластера.

  • kubelet, kube-apiserver та kube-controller-manager повинні бути налаштовані відповідно до використання користувачем зовнішнього CCM. Якщо користувач має зовнішній CCM (не внутрішні цикли управління хмарою в Kubernetes Controller Manager), то --cloud-provider=external повинен бути вказаний. В іншому випадку це не повинно бути вказано.

Майте на увазі, що налаштування кластера на використання контролера хмарного провайдера змінить поведінку вашого кластера:

  • Компоненти, що вказують --cloud-provider=external, додають спеціальний taint node.cloudprovider.kubernetes.io/uninitialized з ефектом NoSchedule під час ініціалізації. Це помічає вузол як такий, що потребує другої ініціалізації з зовнішнього контролера перед тим, як він зможе розпочати роботу. Зверніть увагу, що в разі недоступності cloud-controller-manager, нові вузли у кластері залишаться незапланованими. Taint важливий, оскільки планувальник може вимагати специфічної для хмари інформації про вузли, такої як їхній регіон чи тип (high cpu, gpu, high memory, spot instance тощо).
  • інформація про вузли в кластері більше не буде вибиратися з локальних метаданих, а замість цього всі виклики API для вибору інформації про вузли будуть проходити через cloud controller manager. Це може означати, що ви можете обмежити доступ до вашого API хмари на kubelet для кращої безпеки. Для великих кластерів вам може бути доцільно розглянути можливість обмеження швидкості викликів API cloud controller manager, оскільки він тепер відповідальний майже за всі виклики API вашої хмари зсередини кластера.

Контролер хмарного провайдера може надавати:

  • контролер Node — відповідає за оновлення вузлів Kubernetes з використанням API хмари та видалення вузлів Kubernetes, які були видалені у вашій хмарі.
  • контролер Service — відповідає за балансування навантаження у вашій хмарі для сервісів типу LoadBalancer.
  • контролер Route — відповідає за налаштування мережевих маршрутів у вашій хмарі
  • будь-які інші функції, які ви хочете реалізувати, якщо ви використовуєте провайдера не з ядра Kubernetes.

Приклади

Якщо ви використовуєте хмару, яка наразі підтримується в ядрі Kubernetes і хочете використовувати контролер хмарного провайдера, див. контролер хмарного провайдера в ядрі Kubernetes.

Для контролерів хмарних провайдерів, які не входять у ядро Kubernetes, ви можете знайти відповідні проєкти у репозиторіях, підтримуваних хмарними постачальниками або SIGs.

Для провайдерів, які вже є в ядрі Kubernetes, ви можете запускати вбудований контролер хмарного провайдера як DaemonSet у вашому кластері, використовуючи наступне як рекомендацію:

# Це приклад налаштування cloud-controller-manager як Daemonset у вашому кластері.
# Він передбачає, що ваші мастери можуть запускати Podʼи та мають роль node-role.kubernetes.io/master.
# Зверніть увагу, що цей Daemonset не працюватиме безпосередньо з коробки для вашого хмарного середовища, це
# призначено як рекомендація.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cloud-controller-manager
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:cloud-controller-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: cloud-controller-manager
  namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    k8s-app: cloud-controller-manager
  name: cloud-controller-manager
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: cloud-controller-manager
  template:
    metadata:
      labels:
        k8s-app: cloud-controller-manager
    spec:
      serviceAccountName: cloud-controller-manager
      containers:
      - name: cloud-controller-manager
        # for in-tree providers we use registry.k8s.io/cloud-controller-manager
        # this can be replaced with any other image for out-of-tree providers
        image: registry.k8s.io/cloud-controller-manager:v1.8.0
        command:
        - /usr/local/bin/cloud-controller-manager
        - --cloud-provider=[YOUR_CLOUD_PROVIDER]  # Add your own cloud provider here!
        - --leader-elect=true
        - --use-service-account-credentials
        # these flags will vary for every cloud provider
        - --allocate-node-cidrs=true
        - --configure-cloud-routes=true
        - --cluster-cidr=172.17.0.0/16
      tolerations:
      # this is required so CCM can bootstrap itself
      - key: node.cloudprovider.kubernetes.io/uninitialized
        value: "true"
        effect: NoSchedule
      # these tolerations are to have the daemonset runnable on control plane nodes
      # remove them if your control plane nodes should not run pods
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      # this is to restrict CCM to only run on master nodes
      # the node selector may vary depending on your cluster setup
      nodeSelector:
        node-role.kubernetes.io/master: ""

Обмеження

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

Підтримка томів

Контролер хмарного провайдера не реалізує жодного з контролерів томів, що знаходяться в kube-controller-manager, оскільки інтеграція томів також потребує координації з kubelet. Під час розвитку CSI (інтерфейс контейнерного сховища) та додавання більш надійної підтримки втулків flex томів буде додано необхідну підтримку до контролера хмарного провайдера, щоб хмари могли повністю інтегруватися з томами. Дізнайтеся більше про зовнішні втулки томів CSI тут.

Масштабованість

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

Курча і яйце

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

Хорошим прикладом цього є функція TLS bootstrapping в Kubelet. TLS bootstrapping передбачає, що Kubelet має можливість запитати у хмарного провайдера (або локального сервісу метаданих) всі його типи адрес (приватні, публічні і т. д.), але контролер хмарного провайдера не може встановити типи адрес вузла без попередньої ініціалізації, що потребує, щоб kubelet мав TLS-сертифікати для звʼязку з apiserver.

Під час розвитку цієї ініціативи будуть внесені зміни для розвʼязання цих питань у майбутніх релізах.

Що далі

Щоб створити та розробити свій власний контролер хмарного провайдера, прочитайте Розробка контролера хмарного провайдера.

2.14 - Налаштування постачальника облікових даних образів в kubelet

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [stable]

Починаючи з Kubernetes v1.20, kubelet може динамічно отримувати облікові дані для реєстру образів контейнерів за допомогою використання втулків. Kubelet та втулок спілкуються через stdio (stdin, stdout та stderr), використовуючи версійні API Kubernetes. Ці втулки дозволяють kubelet запитувати облікові дані для реєстру контейнерів динамічно, на відміну від зберігання статичних облікових даних на диску. Наприклад, втулок може звертатися до локального сервера метаданих, щоб отримати облікові дані з обмеженим терміном дії для образу, який завантажується kubelet.

Ви можете бути зацікавлені у використанні цієї можливості, якщо будь-яке з нижче перерахованих тверджень є правдивим:

  • Потрібні виклики API до служби хмарного постачальника для отримання інформації для автентифікації реєстру.
  • Облікові дані мають короткий термін дії, і потрібно часто запитувати нові облікові дані.
  • Зберігання облікових даних реєстру на диску або в imagePullSecrets неприпустиме.

Цей посібник демонструє, як налаштувати механізм втулка постачальника облікових даних зображення kubelet.

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

  • Вам потрібен кластер Kubernetes з вузлами, які підтримують втулки постачальника облікових даних kubelet. Ця підтримка доступна у Kubernetes 1.31; Kubernetes v1.24 та v1.25 мали це як бета-функцію, яка є типово увімкненою.
  • Робоча реалізація втулка постачальника облікових даних. Ви можете створити власний втулок або використовувати той що, наданий хмарним постачальником.
Версія вашого Kubernetes сервера має бути не старішою ніж v1.26. Для перевірки версії введіть kubectl version.

Встановлення втулків на вузлах

Втулок постачальника облікових даних — це виконавчий бінарний файл, який kubelet буде використовувати. Переконайтеся, що бінарний файл втулка існує на кожному вузлі вашого кластера і зберігається в відомій теці. Ця тека буде потрібна пізніше при налаштуванні прапорців kubelet.

Налаштування Kubelet

Для використання цієї функції kubelet очікує встановлення двох прапорців:

  • --image-credential-provider-config — шлях до файлу конфігурації втулка постачальника облікових даних.
  • --image-credential-provider-bin-dir — шлях до теки, де знаходяться бінарні файли втулка постачальника облікових даних.

Налаштування постачальника облікових даних kubelet

Файл конфігурації, переданий в --image-credential-provider-config, зчитується kubelet для визначення, які виконавчі втулки слід викликати для яких образів контейнерів. Нижче наведено приклад файлу конфігурації, який ви, можливо, будете використовувати, якщо використовуєте втулок, оснований на ECR:

apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
# providers — це список допоміжних втулків постачальників облікових даних, які будуть активовані
# kubelet. Одному образу може відповідати декілька постачальників, в такому випадку облікові дані
# від усіх постачальників будуть повернуті kubelet. Якщо для одного образу є кілька 
# постачальників, результати будуть обʼєднані. Якщо постачальники повертають ключі автентифікації,
# що перекриваються, використовується значення від постачальника, який знаходиться раніше в цьому 
# списку.
providers:
  # name — це обовʼязкове імʼя постачальника облікових даних. Воно повинно відповідати імені
  # виконавчого файлу постачальника, яке бачить kubelet. Виконавчий файл повинен знаходитися в 
  # теці bin kubelet (встановлено за допомогою прапорця --image-credential-provider-bin-dir).
  - name: ecr-credential-provider
    # matchImages - обовʼязковий список рядків, які використовуються для порівняння з образами, щоб
    # визначити, чи потрібно викликати цього постачальника. Якщо один із рядків відповідає
    # запитаному образу від kubelet, втулок буде викликаний і отримає шанс надати облікові дані. 
    # Очікується, що образи містять домен реєстру та URL-шлях.
    #
    # Кожний запис в matchImages — це шаблон, який може необовʼязково містити порт та шлях.
    # Можна використовувати шаблони для домену, але не для порту або шляху. Шаблони підтримуються
    # як піддомени, наприклад, '*.k8s.io' або 'k8s.*.io', так і верхніх рівнів доменів, наприклад, 
    # 'k8s.*'. Підтримується також частковий збіг піддоменів, наприклад, 'app*.k8s.io'. Кожен 
    # шаблон може відповідати лише одному сегменту піддомену, тому `*.io` **не** відповідає 
    # `*.k8s.io`.
    #
    # Збіг існує між образами та matchImage, коли виконуються всі наведені нижче умови:
    # - Обидва мають однакову кількість частин домену і кожна частина має збіг.
    # - URL-шлях matchImages повинен бути префіксом URL-шляху цільового образу.
    # - Якщо matchImages містить порт, то порт також повинен мати збіг в образі.
    # Приклади значент matchImages:
    # - 123456789.dkr.ecr.us-east-1.amazonaws.com
    # - *.azurecr.io
    # - gcr.io
    # - *.*.registry.io
    # - registry.io:8080/path
    matchImages:
      - "*.dkr.ecr.*.amazonaws.com"
      - "*.dkr.ecr.*.amazonaws.com.cn"
      - "*.dkr.ecr-fips.*.amazonaws.com"
      - "*.dkr.ecr.us-iso-east-1.c2s.ic.gov"
      - "*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov"
    # defaultCacheDuration — це типовий час, протягом якої втулок буде кешувати облікові дані у памʼяті,
    # якщо тривалість кешу не надана у відповіді втулку. Це поле є обовʼязковим.
    defaultCacheDuration: "12h"
    # Вхідна версія CredentialProviderRequest є обовʼязковою. Відповідь CredentialProviderResponse
    # ПОВИННА використовувати ту ж версію кодування, що й вхідна. Поточні підтримувані значення:
    # - credentialprovider.kubelet.k8s.io/v1
    apiVersion: credentialprovider.kubelet.k8s.io/v1
    # Аргументи, що перндаються команді під час її виконання.
    # +optional
    # args:
    #   - --example-argument
    # Env визначає додаткові змінні середовища, які слід надати процесу. Ці
    # змінні обʼєднуються з оточенням хоста, а також змінними, які використовує client-go
    # для передачі аргументів до втулка.
    # +optional
    env:
      - name: AWS_PROFILE
        value: example_profile

Поле providers — це список увімкнених втулків, які використовує kubelet. Кожен запис має кілька обовʼязкових полів:

  • name: назва втулка, яка ПОВИННА відповідати назві виконавчого бінарного файлу, який існує в теці, вказаній у --image-credential-provider-bin-dir.
  • matchImages: список рядків, які використовуються для порівняння образів з метою визначення, чи слід викликати цього постачальника для конкретного образу контейнера. Докладніше про це нижче.
  • defaultCacheDuration: термін, протягом якого kubelet буде кешувати облікові дані в памʼяті, якщо втулок не надав термін кешування.
  • apiVersion: версія API, яку використовуватимуть kubelet і виконавчий втулок під час спілкування.

Кожен постачальник облікових даних також може мати додаткові аргументи та змінні середовища. Перегляньте реалізацію втулків, щоб визначити, який набір аргументів та змінних середовища потрібний для певного втулка.

Налаштування збігу образів

Поле matchImages для кожного постачальника облікових даних використовується kubelet для визначення того, чи слід викликати втулок для певного образу, що використовується в Podʼі. Кожний запис у matchImages — це шаблон образу, який може опціонально містити порт та шлях. Символи підстановки дозволяють використовувати шаблони для піддоменів, такі як *.k8s.io або k8s.*.io, а також для доменів верхнього рівня, такі як k8s.*. Збіг між імʼям образу та записом matchImage існує тоді, коли виконуються всі умови:

  • Обидва містять однакову кількість частин домену, і кожна частина має збіг.
  • Шлях URL matchImage повинен бути префіксом шляху URL цільового образу.
  • Якщо matchImages містить порт, то порт також повинен мати збіг в образі.

Деякі приклади значень шаблонів matchImages:

  • 123456789.dkr.ecr.us-east-1.amazonaws.com
  • *.azurecr.io
  • gcr.io
  • *.*.registry.io
  • foo.registry.io:8080/path

Що далі

2.15 - Налаштування квот для обʼєктів API

Ця сторінка показує, як налаштувати квоти для обʼєктів API, включаючи PersistentVolumeClaims та Services. Квота обмежує кількість обʼєктів певного типу, які можуть бути створені в просторі імен. Ви вказуєте квоти в обʼєкті ResourceQuota.

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

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

Для перевірки версії введіть kubectl version.

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були ізольовані від решти вашого кластера.

kubectl create namespace quota-object-example

Створення ResourceQuota

Ось файл конфігурації для обʼєкта ResourceQuota:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
spec:
  hard:
    persistentvolumeclaims: "1"
    services.loadbalancers: "2"
    services.nodeports: "0"

Створіть ResourceQuota:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-objects.yaml --namespace=quota-object-example

Перегляньте докладну інформацію про ResourceQuota:

kubectl get resourcequota object-quota-demo --namespace=quota-object-example --output=yaml

У виводі показано, що в просторі імен quota-object-example може бути максимум один PersistentVolumeClaim, максимум два Services типу LoadBalancer та жодного Services типу NodePort.

status:
  hard:
    persistentvolumeclaims: "1"
    services.loadbalancers: "2"
    services.nodeports: "0"
  used:
    persistentvolumeclaims: "0"
    services.loadbalancers: "0"
    services.nodeports: "0"

Створення PersistentVolumeClaim

Ось файл конфігурації для обʼєкта PersistentVolumeClaim:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

Створіть PersistentVolumeClaim:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-objects-pvc.yaml --namespace=quota-object-example

Перевірте, що PersistentVolumeClaim було створено:

kubectl get persistentvolumeclaims --namespace=quota-object-example

У виводі показано, що PersistentVolumeClaim існує і має статус Pending:

NAME             STATUS
pvc-quota-demo   Pending

Спроба створити другий PersistentVolumeClaim

Ось файл конфігурації для другого PersistentVolumeClaim:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-quota-demo-2
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi

Спробуйте створити другий PersistentVolumeClaim:

kubectl apply -f https://k8s.io/examples/admin/resource/quota-objects-pvc-2.yaml --namespace=quota-object-example

У виводі показано, що другий PersistentVolumeClaim не був створений, оскільки це перевищило квоту для простору імен.

persistentvolumeclaims "pvc-quota-demo-2" is forbidden:
exceeded quota: object-quota-demo, requested: persistentvolumeclaims=1,
used: persistentvolumeclaims=1, limited: persistentvolumeclaims=1

Примітки

Ці рядки використовуються для ідентифікації обʼєктів API, які можуть бути обмежені за допомогою квот:

РядокОбʼєкт API
"pods"Pod
"services"Service
"replicationcontrollers"ReplicationController
"resourcequotas"ResourceQuota
"secrets"Secret
"configmaps"ConfigMap
"persistentvolumeclaims"PersistentVolumeClaim
"services.nodeports"Service типу NodePort
"services.loadbalancers"Service типу LoadBalancer

Очищення

Видаліть свій простір імен:

kubectl delete namespace quota-object-example

Що далі

Для адміністраторів кластера

Для розробників застосунків

2.16 - Управління політиками керування ЦП на вузлі

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [stable]

Kubernetes зберігає багато аспектів того, як Podʼи виконуються на вузлах, абстрагованими від користувача. Це зроблено навмисно. Проте деякі завдання вимагають більших гарантій з погляду часу реакції та/або продуктивності, щоб працювати задовільно. Kubelet надає методи для включення складніших політик розміщення завдань, зберігаючи абстракцію вільною від явних директив розміщення.

Для отримання докладної інформації щодо управління ресурсами, будь ласка, зверніться до документації Управління ресурсами для Podʼів та контейнерів.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.26. Для перевірки версії введіть kubectl version.

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

Політики керування ЦП

Стандартно, kubelet використовує квоту CFS, щоб обмежувати ЦП для Podʼів. Коли вузол виконує багато Podʼів, залежних від ЦП, навантаження може переміщуватися на різні ядра ЦП залежно від того, чи обмежений Pod, і які ядра ЦП доступні на момент планування. Багато завдань не чутливі до цього переміщення і, отже, працюють нормально без будь-якого втручання.

Проте, в завданнях, де спорідненість кешу ЦП та затримка планування значно впливають на продуктивність робочого завдання, kubelet дозволяє використовувати альтернативні політики керування ЦП для визначення деяких вподобань розміщення на вузлі.

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

Політика керування ЦП встановлюється прапорцем kubelet --cpu-manager-policy або полем cpuManagerPolicy в KubeletConfiguration. Підтримуються дві політики:

  • none: стандартна політика.
  • static: дозволяє контейнерам з певними характеристиками ресурсів отримувати збільшену спорідненість ЦП та ексклюзивність на вузлі.

Керування ЦП періодично записує оновлення ресурсів через CRI, щоб звести до мінімуму проблеми сумісності памʼяті та виправити їх. Частота зведення встановлюється за допомогою нового значення конфігурації Kubelet --cpu-manager-reconcile-period. Якщо вона не вказана, стандартно вона дорівнює тому ж часу, що й --node-status-update-frequency.

Поведінку статичної політики можна налаштувати за допомогою прапорця --cpu-manager-policy-options. Прапорець приймає список параметрів політики у вигляді ключ=значення. Якщо ви вимкнули функціональну можливість CPUManagerPolicyOptions, то ви не зможете налаштувати параметри політик керування ЦП. У цьому випадку керування ЦП працюватиме лише зі своїми стандартними налаштуваннями.

Окрім верхнього рівня CPUManagerPolicyOptions, параметри політики розділені на дві групи: якість альфа (типово прихована) та якість бета (типово видима). Відмінно від стандарту Kubernetes, ці feature gate захищають групи параметрів, оскільки було б надто складно додати feature gate для кожного окремого параметра.

Зміна політики керування ЦП

Оскільки політику керування ЦП можна застосовувати лише коли kubelet створює нові Podʼи, просте змінення з "none" на "static" не застосується до наявних Podʼів. Тому, щоб правильно змінити політику керування ЦП на вузлі, виконайте наступні кроки:

  1. Виведіть з експлуатації вузол.
  2. Зупиніть kubelet.
  3. Видаліть старий файл стану керування ЦП. Типовий шлях до цього файлу /var/lib/kubelet/cpu_manager_state. Це очищує стан, що зберігається CPUManager, щоб множини ЦП, налаштовані за новою політикою, не конфліктували з нею.
  4. Відредагуйте конфігурацію kubelet, щоб змінити політику керування ЦП на потрібне значення.
  5. Запустіть kubelet.

Повторіть цей процес для кожного вузла, який потребує зміни політики керування ЦП. Пропуск цього процесу призведе до постійної аварійної роботи kubelet з такою помилкою:

could not restore state from checkpoint: configured policy "static" differs from state checkpoint policy "none", please drain this node and delete the CPU manager checkpoint file "/var/lib/kubelet/cpu_manager_state" before restarting Kubelet

Політика none

Політика none явно активує наявну стандартну схему спорідненості ЦП, не надаючи спорідненості поза тим, що робить планувальник ОС автоматично. Обмеження на використання ЦП для Podʼів з гарантованою продуктивністю та Podʼів з бурхливою продуктивністю накладаються за допомогою квоти CFS.

Політика static

Політика static дозволяє контейнерам у Podʼах з гарантованою продуктивністю із цілими requests ЦП мати доступ до ексклюзивних ЦП на вузлі. Ця ексклюзивність забезпечується за допомогою контролера груп cgroup для cpuset.

Ця політика керує спільним пулом ЦП, який на початку містить всі ЦП на вузлі. Кількість ексклюзивно виділених ЦП дорівнює загальній кількості ЦП на вузлі мінус будь-які резерви ЦП за допомогою параметрів kubelet --kube-reserved або --system-reserved. З версії 1.17 список резервувань ЦП може бути вказаний явно за допомогою параметру kubelet --reserved-cpus. Явний список ЦП, вказаний за допомогою --reserved-cpus, має пріоритет над резервуванням ЦП, вказаним за допомогою --kube-reserved та --system-reserved. ЦП, зарезервовані цими параметрами, беруться, у цілочисельній кількості, зі спільного пулу на основі фізичного ідентифікатора ядра. Цей спільний пул — це множина ЦП, на яких працюють контейнери в Podʼах з BestEffort та Burstable. Контейнери у Podʼах з гарантованою продуктивністю з цілими requests ЦП також працюють на ЦП у спільному пулі. Ексклюзивні ЦП призначаються лише контейнерам, які одночасно належать до Podʼа з гарантованою продуктивністю та мають цілочисельні requests ЦП.

Коли Podʼи з гарантованою продуктивністю (Guaranteed), чий контейнер відповідає вимогам для статичного призначення, планується на вузол, ЦП видаляються зі спільного пулу і поміщаються у cpuset для контейнера. Квота CFS не використовується для обмеження використання ЦП цих контейнерів, оскільки їх використання обмежується самою областю планування. Іншими словами, кількість ЦП у cpuset контейнера дорівнює цілому limit ЦП, вказаному в специфікації Podʼа. Це статичне призначення підвищує спорідненість ЦП та зменшує перемикання контексту через обмеження для навантаження, залежного від ЦП.

Розгляньте контейнери в наступних специфікаціях Podʼа:

spec:
  containers:
  - name: nginx
    image: nginx

Pod, вказаний вище, працює в класі QoS BestEffort, оскільки не вказані requests або limits ресурсів. Він працює у спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

Pod, вказаний вище, працює в класі QoS Burstable, оскільки requests ресурсів не дорівнюють limits, а кількість cpu не вказана. Він працює у спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "100Mi"
        cpu: "1"

Pod, вказаний вище, працює в класі QoS Burstable, оскільки requests ресурсів не дорівнюють limits. Він працює у спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
      requests:
        memory: "200Mi"
        cpu: "2"

Pod, вказаний вище, працює в класі QoS Guaranteed, оскільки requests дорівнюють limits. І ліміт ресурсу ЦП контейнера є цілим числом більшим або рівним одиниці. Контейнер nginx отримує 2 ексклюзивних ЦП.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "1.5"
      requests:
        memory: "200Mi"
        cpu: "1.5"

Pod, вказаний вище, працює в класі QoS Guaranteed, оскільки requests дорівнюють limits. Але ліміт ресурсу ЦП контейнера є дробовим числом. Він працює у спільному пулі.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"

Pod, вказаний вище, працює в класі QoS Guaranteed, оскільки вказані тільки limits, а requests встановлені рівними limits, коли не вказано явно. І ліміт ресурсу ЦП контейнера є цілим числом більшим або рівним одиниці. Контейнер nginx отримує 2 ексклюзивних ЦП.

Параметри політики static

Ви можете включати та виключати групи параметрів на основі їх рівня зрілості, використовуючи наступні feature gate:

  • CPUManagerPolicyBetaOptions — стандартно увімкнено. Вимкніть, щоб приховати параметри рівня бета.
  • CPUManagerPolicyAlphaOptions — стандартно вимкнено. Увімкніть, щоб показати параметри рівня альфа. Ви все ще повинні увімкнути кожен параметр за допомогою опції kubelet CPUManagerPolicyOptions.

Для політики static CPUManager існують наступні параметри:

  • full-pcpus-only (бета, типово видимий) (1.22 або вище)
  • distribute-cpus-across-numa (альфа, типово прихований) (1.23 або вище)
  • align-by-socket (альфа, типово прихований) (1.25 або вище)
  • distribute-cpus-across-cores (альфа, типово прихований) (1.31 або вище)

Якщо вказано параметр політики full-pcpus-only, статична політика завжди виділятиме повні фізичні ядра. Стандартно, без цієї опції, статична політика розподіляє ЦП, використовуючи найкращий вибір з урахуванням топології. На системах з увімкненими SMT, політика може виділяти окремі віртуальні ядра, які відповідають апаратним потокам. Це може призвести до того, що різні контейнери будуть використовувати одні й ті ж фізичні ядра; ця поведінка, своєю чергою, спричиняє проблему шумних сусідів. З включеною опцією, Pod буде прийнятий kubelet лише в тому випадку, якщо запити на ЦП всіх його контейнерів можна задовольнити виділенням повних фізичних ядер. Якщо Pod не пройде процедуру допуску, він буде переведений в стан Failed з повідомленням SMTAlignmentError.

Якщо вказано параметр політики distribute-cpus-across-numa, статична політика рівномірно розподілить ЦП по вузлах NUMA у випадках, коли для задоволення розподілу необхідно більше одного вузла NUMA. Стандартно CPUManager буде пакувати ЦП на один вузол NUMA, поки він не буде заповнений, і будь-які залишкові ЦП просто перейдуть на наступний вузол NUMA. Це може призвести до небажаних заторів у паралельному коді, що покладається на барʼєри (та подібні примітиви синхронізації), оскільки цей тип коду, як правило, працює лише так швидко, як його найповільніший робітник (який сповільнюється тим, що на одному з вузлів NUMA доступно менше ЦП). Рівномірний розподіл ЦП по вузлах NUMA дозволяє розробникам програм легше забезпечувати те, що жоден окремий робітник не страждає від ефектів NUMA більше, ніж будь-який інший, що покращує загальну продуктивність цих типів програм.

Якщо вказано параметр політики align-by-socket, ЦП будуть вважатися вирівненими на межі сокета при вирішенні того, як розподілити ЦП між контейнерами. Стандартно, CPUManager вирівнює виділення ЦП на межі NUMA, що може призвести до погіршання продуктивності, якщо ЦП потрібно вилучати з більш ніж одного вузла NUMA для задоволення виділення. Попри те, що він намагається забезпечити, щоб всі ЦП були виділені з мінімальної кількості вузлів NUMA, немає гарантії, що ці вузли NUMA будуть на одному сокеті. Вказуючи CPUManager явно вирівнювати ЦП на межі сокета, а не NUMA, ми можемо уникнути таких проблем. Зауважте, що ця опція політики несумісна з політикою TopologyManager single-numa-node і не застосовується до апаратного забезпечення, де кількість сокетів більша, ніж кількість вузлів NUMA.

Якщо вказана опція політики distribute-cpus-across-cores, статична політика спробує розподілити віртуальні ядра (апаратні потоки) по різних фізичних ядрах. Стандартно, CPUManager має тенденцію запаковувати процесори на якомога меншій кількості фізичних ядер, що може призвести до конфліктів між процесорами на одному фізичному ядрі та, як наслідок, до проблем з продуктивністю. Увімкнувши політику distribute-cpus-across-cores, статична політика забезпечує розподіл процесорів по якомога більшій кількості фізичних ядер, зменшуючи конфлікти на одному фізичному ядрі й тим самим покращуючи загальну продуктивність. Проте, важливо зазначити, що ця стратегія може бути менш ефективною, коли система сильно навантажена. За таких умов вигода від зменшення конфліктів зменшується. З іншого боку, стандартна поведінка може допомогти зменшити витрати на комунікацію між ядрами, що потенційно забезпечує кращу продуктивність при високих навантаженнях.

Параметр full-pcpus-only можна включити, додавши full-pcpus-only=true до параметрів політики CPUManager. Так само параметр distribute-cpus-across-numa можна включити, додавши distribute-cpus-across-numa=true до параметрів політики CPUManager. Якщо обидва встановлені, вони "адитивні" у тому сенсі, що ЦП будуть розподілені по вузлах NUMA в шматках повних фізичних ядер, а не окремих ядер. Параметр політики align-by-socket можна увімкнути, додавши align-by-socket=true до параметрів політики CPUManager. Він також адитивний до параметрів політики full-pcpus-only та distribute-cpus-across-numa.

Опцію distribute-cpus-across-cores можна увімкнути, додавши distribute-cpus-across-cores=true до параметрів політики CPUManager. Зараз її не можна використовувати разом з параметрами політики full-pcpus-only або distribute-cpus-across-numa.

2.17 - Керування політиками топології на вузлі

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.27 [stable]

Зростаюча кількість систем використовує комбінацію ЦП та апаратних прискорювачів для підтримки виконання, критичного з погляду затримок, та високопродуктивних паралельних обчислень. Ці системи включають робочі навантаження у таких галузях, як телекомунікації, наукові обчислення, машинне навчання, фінансові послуги та аналітика даних. Такі гібридні системи складають високопродуктивне середовище.

Для досягнення найкращої продуктивності потрібні оптимізації, що стосуються ізоляції ЦП, памʼяті та локальності пристроїв. Проте, в Kubernetes ці оптимізації обробляються роздільним набором компонентів.

Topology Manager — це компонент Kubelet, який має на меті координацію набору компонентів, відповідальних за ці оптимізації.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.18. Для перевірки версії введіть kubectl version.

Як працює Topology Manager

Перед введенням Topology Manager, CPU та Device Manager в Kubernetes приймають рішення про розподіл ресурсів незалежно одне від одного. Це може призводити до небажаних розподілів на системах з кількома сокетами, де застосунки, що чутливі до продуктивності/затримки, будуть страждати через ці небажані розподіли. Небажані в цьому випадку означає, наприклад, виділення ЦП та пристроїв з різних вузлів NUMA, що призводить до додаткової затримки.

Topology Manager — це компонент Kubelet, який діє як джерело правди, щоб інші компоненти Kubelet могли робити вибір щодо розподілу ресурсів з урахуванням топології.

Topology Manager надає інтерфейс для компонентів, званих Hint Providers, для надсилання та отримання інформації про топологію. У Topology Manager є набір політик рівня вузла, які пояснюються нижче.

Topology manager отримує інформацію про топологію від Hint Providers у вигляді бітової маски, яка позначає доступні вузли NUMA та попередній показник виділення. Політики Topology Manager виконують набір операцій з наданими підказками та сходяться на підказці, визначеній політикою, щоб дати оптимальний результат, якщо зберігається небажана підказка, бажане поле для підказки буде встановлено у false. У поточних політиках найприйнятніше — це найвужча попередня маска. Обрана підказка зберігається як частина Topology Manager. Залежно від налаштованої політики, Pod може бути прийнятий або відхилений на вузлі на основі обраної підказки. Підказка потім зберігається в Topology Manager для використання Hint Providers під час прийняття рішень про розподіл ресурсів.

Scope та Policy в Topology Manager

Topology Manager наразі:

  • Вирівнює Podʼи усіх класів QoS.
  • Вирівнює запитані ресурси, які Hint Provider надає підказки топології.

Якщо ці умови виконані, Topology Manager вирівнює запитані ресурси.

Для того, щоб налаштувати, як це вирівнювання виконується, Topology Manager надає два відмінні регулятори: scope та policy.

scope визначає гранулярність, на якій ви хочете, щоб виконувалось вирівнювання ресурсів (наприклад, на рівні pod або container). А policy визначає фактичну стратегію, яка використовується для виконання вирівнювання (наприклад, best-effort, restricted, single-numa-node і т.д.). Деталі щодо різних scopes та policies, доступних на сьогодні, можна знайти нижче.

Scope в Topology Manager

Topology Manager може вирівнювати ресурси у кількох різних сферах:

  • container (стандартно)
  • pod

Будь-яку з цих опцій можна вибрати під час запуску kubelet, налаштувавши параметр topologyManagerScope у файлі конфігурації kubelet.

Scope container

Scope container використовується стандартно. Ви також можете явно встановити параметр topologyManagerScope в container у файлі конфігурації kubelet.

У цьому scope Topology Manager виконує кілька послідовних вирівнювань ресурсів, тобто для кожного контейнера (у Pod) окреме вирівнювання. Іншими словами, відсутнє поняття групування контейнерів у певний набір вузлів NUMA, для цього конкретного scope. Фактично Topology Manager виконує довільне вирівнювання окремих контейнерів на вузли NUMA.

Ідея групування контейнерів була затверджена та реалізована навмисно в наступному scope, наприклад, у scope pod.

Scope pod

Для вибору scope pod, встановіть параметр topologyManagerScope у файлі конфігурації kubelet на pod.

Цей scope дозволяє групувати всі контейнери у Podʼі у спільний набір вузлів NUMA. Іншими словами, Topology Manager розглядає Pod як ціле та намагається виділити весь Pod (всі контейнери) на один вузол NUMA або спільний набір вузлів NUMA. Нижче наведено приклади вирівнювання, які виробляє Topology Manager у різні моменти:

  • всі контейнери можуть бути та виділені на один вузол NUMA;
  • всі контейнери можуть бути та виділені на спільний набір вузлів NUMA.

Загальна кількість певного ресурсу, запитаного для всього Podʼа, розраховується за формулою ефективні запити/обмеження, і, таким чином, це загальне значення дорівнює максимуму з:

  • сума всіх запитів контейнерів застосунку,
  • максимум запитів контейнерів ініціалізації,

для ресурсу.

Використання scope pod разом з політикою Topology Manager single-numa-node особливо цінне для робочих навантажень, які чутливі до затримок, або для високопродуктивних застосунків, які виконують міжпроцесну комунікацію (IPC). Поєднуючи обидва варіанти, ви можете помістити всі контейнери в Pod на один вузол NUMA; отже, можливість між-NUMA комунікації може бути усунена для цього Pod.

У випадку політики single-numa-node Pod приймається лише в тому разі, якщо серед можливих виділень є відповідний набір вузлів NUMA. Перегляньте приклад вище:

  • набір, що містить лише один вузол NUMA — це призводить до допуску Podʼа,
  • тоді як набір, що містить більше вузлів NUMA — це призводить до відхилення Podʼа (тому, що для задоволення виділення потрібен один вузол NUMA).

У підсумку, Topology Manager спочатку розраховує набір вузлів NUMA, а потім перевіряє його з політикою Topology Manager, яка призводить до відхилення або допуску Podʼа.

Політики Topology Manager

Topology Manager підтримує чотири політики виділення. Ви можете встановити політику за допомогою прапорця Kubelet --topology-manager-policy. Існують чотири підтримувані політики:

  • none (стандартно)
  • best-effort
  • restricted
  • single-numa-node

Політика none

Це стандартна політика і вона не виконує жодного вирівнювання топології.

Політика best-effort

Для кожного контейнера у Pod kubelet з політикою керування топологією best-effort викликає кожен Hint Provider, щоб дізнатися їхню доступність ресурсів. Використовуючи цю інформацію, Topology Manager зберігає привʼязку до найближчого вузла NUMA для цього контейнера. Якщо привʼязка не є бажаною, Topology Manager також зберігає це і допускає Pod до вузла не зважаючи на це.

Hint Providers можуть використовувати цю інформацію при прийнятті рішення про розподіл ресурсів.

Політика restricted

Для кожного контейнера у Pod kubelet з політикою керування топологією restricted викликає кожен Hint Provider, щоб дізнатися їхню доступність ресурсів. Використовуючи цю інформацію, Topology Manager зберігає привʼязку до вузла NUMA для цього контейнера. Якщо привʼязка не є бажаною, Topology Manager відхиляє Pod від вузла. Це призводить до того, що Pod перебуває в стані Terminated з помилкою допуску Podʼа.

Якщо Pod перебуває в стані Terminated, планувальник Kubernetes не буде намагатися знову запланувати його. Рекомендується використовувати ReplicaSet або Deployment для того, щоб викликати повторне розгортання Podʼа. Зовнішній цикл керування також може бути реалізований для того, щоб викликати повторне розгортання Podʼів, які мають помилку Topology Affinity.

Якщо Pod прийнято, Hint Providers можуть використовувати цю інформацію при прийнятті рішення про розподіл ресурсів.

Політика single-numa-node

Для кожного контейнера у Pod kubelet з політикою керування топологією single-numa-node викликає кожен Hint Provider, щоб дізнатися їхню доступність ресурсів. Використовуючи цю інформацію, Topology Manager визначає, чи можлива привʼязка до одного вузла NUMA. Якщо можливо, Topology Manager зберігає це і Hint Providers можуть використовувати цю інформацію при прийнятті рішення про розподіл ресурсів. Якщо привʼязка не можлива, Topology Manager відхиляє Pod від вузла. Це призводить до того, що Pod перебуває в стані Terminated з помилкою прийняття Podʼа.

Якщо Pod перебуває в стані Terminated, планувальник Kubernetes не буде намагатися знову запланувати його. Рекомендується використовувати ReplicaSet або Deployment для того, щоб викликати повторне розгортання Podʼа. Зовнішній цикл керування також може бути реалізований для того, щоб викликати повторне розгортання Podʼів, які мають помилку Topology Affinity.

Параметри політики керування топологією

Підтримка параметрів політики керування топологією вимагає активації функціональної можливоітсі TopologyManagerPolicyOptions, щоб бути ввімкненими (типово увімкнено).

Ви можете увімкнути та вимкнути групи параметрів на основі їхнього рівня зрілості за допомогою наступних feature gate:

  • TopologyManagerPolicyBetaOptions типово увімкнено. Увімкніть, щоб показати параметри рівня бета.
  • TopologyManagerPolicyAlphaOptions типово вимкнено. Увімкніть, щоб показати параметри рівня альфа.

Ви все ще повинні активувати кожний параметр за допомогою опції kubelet TopologyManagerPolicyOptions.

prefer-closest-numa-nodes (бета)

Опція prefer-closest-numa-nodes є бета-функцією з версії Kubernetes 1.28. У версії Kubernetes 1.31 ця політика стандартно доступна, якщо увімкнено функціональні можливості TopologyManagerPolicyOptions та TopologyManagerPolicyBetaOptions.

Типово менеджер топології не враховує відстані між NUMA вузлами та не бере їх до уваги при прийнятті рішень про допуск Pod. Це обмеження проявляється в системах з кількома сокетами, а також у системах з одним сокетом і кількома NUMA вузлами, і може спричинити значне погіршення продуктивності у виконанні з критичною затримкою та високим пропуском даних, якщо менеджер топології вирішить розмістити ресурси на не сусідніх NUMA вузлах.

Якщо вказано параметр політики prefer-closest-numa-nodes, політики best-effort та restricted будуть віддавати перевагу наборам вузлів NUMA з коротшими відстанями між ними при прийнятті рішень про прийняття.

Стандартно (без цієї опції) менеджер топології розміщує ресурси або на одному NUMA вузлі, або, у випадку, коли потрібно більше одного NUMA вузла, використовує мінімальну кількість NUMA вузлів.

max-allowable-numa-nodes (бета)

Опція max-allowable-numa-nodes є бета-функцією з версії Kubernetes 1.31. У версії Kubernetes 1.31 ця політика стандартно доступна, якщо увімкнено функціональні можливості TopologyManagerPolicyOptions та TopologyManagerPolicyBetaOptions.

Час на прийняття Podʼа повʼязаний з кількістю NUMA вузлів на фізичній машині. стандартно, Kubernetes не запускає kubelet з увімкненим менеджером топології на жодному (Kubernetes) вузлі, де виявлено більше ніж 8 NUMA вузлів.

Ви можете увімкнути цю опцію, додавши max-allowable-numa-nodes=true до параметрів політики Topology Manager.

Встановлення значення max-allowable-numa-nodes не впливає безпосередньо на затримку допуску Pod, але привʼязка Pod до вузла (Kubernetes) з великою кількістю NUMA вузлів має вплив. Майбутні можливі поліпшення в Kubernetes можуть покращити продуктивність допуску Podʼів і зменшити високу затримку, яка виникає зі збільшенням кількості NUMA вузлів.

Взаємодія точок доступу Pod з політиками Topology Manager

Розгляньте контейнери у наступному маніфесті Podʼа:

spec:
  containers:
  - name: nginx
    image: nginx

Цей Pod працює в класі QoS BestEffort, оскільки не вказано жодних ресурсів requests або limits.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

Цей Pod працює в класі QoS Burstable, оскільки запити менше, ніж ліміти.

Якщо вибрана політика відрізняється від none, Topology Manager враховуватиме ці специфікації Pod. Topology Manager буде консультувати Hint Provider для отримання підказок топології. У випадку static політики CPU Manager поверне типову підказку топології, оскільки ці Podʼи не мають явних запитів ресурсів CPU.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"

Цей Pod із цілим запитом CPU працює в класі QoS Guaranteed, оскільки requests дорівнює limits.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"

Цей Pod з спільним запитом CPU працює в класі QoS Guaranteed, оскільки requests дорівнює limits.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        example.com/deviceA: "1"
        example.com/deviceB: "1"
      requests:
        example.com/deviceA: "1"
        example.com/deviceB: "1"

Цей Pod працює в класі QoS BestEffort, оскільки немає запитів CPU та памʼяті.

Topology Manager враховуватиме вищезазначені Podʼи. Topology Manager буде консультувати Hint Provider, які є CPU та Device Manager, для отримання підказок топології для Pod.

У випадку Guaranteed Pod із цілим запитом CPU, політика CPU Manager static поверне підказки топології, що стосуються виключно CPU, а Device Manager надішле підказки для запитаного пристрою.

У випадку Guaranteed Pod з спільним запитом CPU, політика CPU Manager static поверне типову підказку топології, оскільки немає виключного запиту CPU, а Device Manager надішле підказки для запитаних пристроїв.

У вищезазначених двох випадках Guaranteed Pod політика CPU Manager none поверне типову підказку топології.

У випадку BestEffort Pod, політика CPU Manager static поверне типову підказку топології, оскільки немає запиту CPU, а Device Manager надішле підказки для кожного запитаного пристрою.

На основі цієї інформації Topology Manager обчислить оптимальну підказку для Pod і збереже цю інформацію, яка буде використовуватися постачальниками підказок при призначенні ресурсів.

Відомі обмеження

  1. Максимальна кількість вузлів NUMA, яку дозволяє Topology Manager, — 8. З більш ніж 8 вузлами NUMA відбудеться вибух стану при спробі перерахувати можливі спорідненості NUMA та генерувати їх підказки. Дивіться max-allowable-numa-nodes (бета) для отримання додаткової інформації.

  2. Планувальник не знає топології, тому його можна запланувати на вузлі, а потім вивести з ладу на вузлі через Topology Manager.

2.18 - Налаштування служби DNS

На цій сторінці пояснюється, як налаштувати ваші DNS Pod та настроїти процес розвʼязання імен DNS у вашому кластері.

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

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

Ваш кластер повинен працювати з надбудовою CoreDNS.

Версія вашого Kubernetes сервера має бути не старішою ніж v1.12. Для перевірки версії введіть kubectl version.

Вступ

DNS — це вбудована служба Kubernetes, яка автоматично запускається з допомогою менеджера надбудов cluster add-on.

Якщо ви працюєте з CoreDNS як з Deployment, його зазвичай викладають як Service Kubernetes зі статичною IP-адресою. Kubelet передає інформацію про резолвер DNS кожному контейнеру з прапорцем --cluster-dns=<ір-адреса-dns-служби>.

DNS-імена також потребують доменів. Ви налаштовуєте локальний домен в kubelet з прапорцем --cluster-domain=<типовий-локальний-домен>.

DNS-сервер підтримує прямий пошук (записи A та AAAA), пошук портів (записи SRV), зворотній пошук IP-адрес (записи PTR) та інші. Для отримання додаткової інформації дивіться DNS для Service та Pod.

Якщо для Pod dnsPolicy встановлено значення default, він успадковує конфігурацію розвʼязку імен від вузла, на якому працює Pod. Розвʼязок імен Pod повинен поводитися так само як і на вузлі. Проте див. Відомі проблеми.

Якщо вам це не потрібно, або якщо ви хочете іншу конфігурацію DNS для Podʼів, ви можете використовувати прапорець --resolv-conf kubelet. Встановіть цей прапорець у "" для того, щоб запобігти успадкуванню DNS від Podʼів. Встановіть його на дійсний шлях до файлу, щоб вказати файл, відмінний від /etc/resolv.conf для успадкування DNS.

CoreDNS

CoreDNS — це універсальний авторитетний DNS-сервер, який може служити як кластерний DNS, відповідаючи специфікаціям DNS.

Опції ConfigMap CoreDNS

CoreDNS — це DNS-сервер, який є модульним та розширюваним, з допомогою втулків, які додають нові можливості. Сервер CoreDNS може бути налаштований за допомогою збереження Corefile, який є файлом конфігурації CoreDNS. Як адміністратор кластера, ви можете змінювати ConfigMap для Corefile CoreDNS для зміни того, як поводитися служба виявлення служби DNS для цього кластера.

У Kubernetes CoreDNS встановлюється з наступною стандартною конфігурацією Corefile:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }    

Конфігурація Corefile включає наступні втулки CoreDNS:

  • errors: Помилки реєструються в stdout.
  • health: Справність CoreDNS повідомляється за адресою http://localhost:8080/health. У цьому розширеному синтаксисі lameduck зробить процес несправним, а потім зачекає 5 секунд, перш ніж процес буде вимкнено.
  • ready: HTTP-точка на порту 8181 поверне 200 ОК, коли всі втулки, які можуть сигналізувати готовність, зроблять це.
  • kubernetes: CoreDNS відповість на DNS-запити на основі IP-адрес Service та Pod. Ви можете знайти більше деталей про цей втулок на вебсайті CoreDNS.
    • ttl дозволяє встановити власний TTL для відповідей. Стандартно — 5 секунд. Мінімальний дозволений TTL — 0 секунд, а максимальний обмежений 3600 секундами. Встановлення TTL на 0 запобіжить кешуванню записів.
    • Опція pods insecure надається для сумісності з kube-dns.
    • Ви можете використовувати опцію pods verified, яка поверне запис A лише у випадку, якщо існує Pod в тому ж просторі імен зі потрібним IP.
    • Опцію pods disabled можна використовувати, якщо ви не використовуєте записи Podʼів.
  • prometheus: Метрики CoreDNS доступні за адресою http://localhost:9153/metrics у форматі Prometheus (також відомий як OpenMetrics).
  • forward: Будь-які запити, які не належать до домену Kubernetes кластера, будуть переслані на попередньо визначені резолвери (/etc/resolv.conf).
  • cache: Це увімкнення кешу фронтенду.
  • loop: Виявлення простих переспрямовуваннь та припинення процесу CoreDNS у випадку виявлення циклу.
  • reload: Дозволяє автоматичне перезавантаження зміненого Corefile. Після редагування конфігурації ConfigMap дайте дві хвилини для того, щоб ваші зміни набули чинності.
  • loadbalance: Це round-robin DNS-балансувальник, який випадковим чином змінює порядок записів A, AAAA та MX в відповіді.

Ви можете змінити типову поведінку CoreDNS, змінивши ConfigMap.

Конфігурація Stub-домену та віддаленого сервера імен за допомогою CoreDNS

CoreDNS має можливість налаштувати stub-домени та віддалені сервери імен за допомогою втулку forward.

Приклад

Якщо оператор кластера має сервер домену Consul за адресою "10.150.0.1", і всі імена Consul мають суфікс ".consul.local". Для його налаштування в CoreDNS адміністратор кластера створює наступний запис в ConfigMap CoreDNS.

consul.local:53 {
    errors
    cache 30
    forward . 10.150.0.1
}

Щоб явно пересилати всі не-кластерні DNS-пошуки через конкретний сервер імен за адресою 172.16.0.1, вказуйте forward на сервер імен замість /etc/resolv.conf.

forward .  172.16.0.1

Кінцевий ConfigMap разом з типовою конфігурацією Corefile виглядає так:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . 172.16.0.1
        cache 30
        loop
        reload
        loadbalance
    }
    consul.local:53 {
        errors
        cache 30
        forward . 10.150.0.1
    }    

Що далі

2.19 - Налагодження розвʼязання імен DNS

Ця сторінка надає вказівки щодо діагностування проблем DNS.

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

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

Ваш кластер повинен бути налаштований на використання надбудови CoreDNS або її попередника, kube-dns.

Версія вашого Kubernetes сервера має бути не старішою ніж v1.6. Для перевірки версії введіть kubectl version.

Створіть простий Pod для використання як тестове середовище

apiVersion: v1
kind: Pod
metadata:
  name: dnsutils
  namespace: default
spec:
  containers:
  - name: dnsutils
    image: registry.k8s.io/e2e-test-images/agnhost:2.39
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

Використайте цей маніфест, щоб створити Pod:

kubectl apply -f https://k8s.io/examples/admin/dns/dnsutils.yaml
pod/dnsutils created

…і перевірте його статус:

kubectl get pods dnsutils
NAME       READY     STATUS    RESTARTS   AGE
dnsutils   1/1       Running   0          <some-time>

Після того, як цей Pod працює, ви можете виконати nslookup в цьому середовищі. Якщо ви бачите щось схоже на наступне, DNS працює правильно.

kubectl exec -i -t dnsutils -- nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      kubernetes.default
Address 1: 10.0.0.1

Якщо команда nslookup не вдається, перевірте наступне:

Спочатку перевірте локальну конфігурацію DNS

Подивіться всередину файлу resolv.conf. (Див. Налаштування служби DNS та Відомі проблеми нижче для отримання додаткової інформації)

kubectl exec -ti dnsutils -- cat /etc/resolv.conf

Перевірте, що шлях пошуку та імʼя сервера налаштовані так, як показано нижче (зверніть увагу, що шлях пошуку може відрізнятися для різних постачальників хмарних послуг):

search default.svc.cluster.local svc.cluster.local cluster.local google.internal c.gce_project_id.internal
nameserver 10.0.0.10
options ndots:5

Помилки, такі як наведені нижче, вказують на проблему з CoreDNS (або kube-dns) або з повʼязаними службами:

kubectl exec -i -t dnsutils -- nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10

nslookup: can't resolve 'kubernetes.default'

або

kubectl exec -i -t dnsutils -- nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'kubernetes.default'

Перевірте, чи працює Pod DNS

Використайте команду kubectl get pods, щоб перевірити, що Pod DNS працює.

kubectl get pods --namespace=kube-system -l k8s-app=kube-dns
NAME                       READY     STATUS    RESTARTS   AGE
...
coredns-7b96bf9f76-5hsxb   1/1       Running   0           1h
coredns-7b96bf9f76-mvmmt   1/1       Running   0           1h
...

Якщо ви бачите, що жоден Pod CoreDNS не працює або що Pod несправний/завершено, надбудова DNS може не бути типово розгорнута у вашому поточному середовищі та вам доведеться розгорнути його вручну.

Перевірка помилок у Podʼі DNS

Використайте команду kubectl logs, щоб переглянути логи контейнерів DNS.

Для CoreDNS:

kubectl logs --namespace=kube-system -l k8s-app=kube-dns

Ось приклад здорових журналів CoreDNS:

.:53
2018/08/15 14:37:17 [INFO] CoreDNS-1.2.2
2018/08/15 14:37:17 [INFO] linux/amd64, go1.10.3, 2e322f6
CoreDNS-1.2.2
linux/amd64, go1.10.3, 2e322f6
2018/08/15 14:37:17 [INFO] plugin/reload: Running configuration MD5 = 24e6c59e83ce706f07bcc82c31b1ea1c

Перегляньте, чи є будь-які підозрілі або несподівані повідомлення у логах.

Чи працює служба DNS?

Перевірте, чи працює служба DNS за допомогою команди kubectl get service.

kubectl get svc --namespace=kube-system
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)             AGE
...
kube-dns     ClusterIP   10.0.0.10      <none>        53/UDP,53/TCP        1h
...

Якщо ви створили Service або у випадку, якщо вона повинна типово створюватися, але вона не зʼявляється, див. налагодження Service для отримання додаткової інформації.

Чи відкриті точки доступу DNS?

Ви можете перевірити, чи викриті точки доступу DNS, використовуючи команду kubectl get endpoints.

kubectl get endpoints kube-dns --namespace=kube-system
NAME       ENDPOINTS                       AGE
kube-dns   10.180.3.17:53,10.180.3.17:53    1h

Якщо ви не бачите точки доступу, дивіться розділ про точки доступу у документації налагодження Service.

Для додаткових прикладів DNS Kubernetes дивіться приклади dns в кластері у репозиторії Kubernetes GitHub.

Чи надходять/обробляються DNS-запити?

Ви можете перевірити, чи надходять запити до CoreDNS, додавши втулок log до конфігурації CoreDNS (тобто файлу Corefile). Файл CoreDNS Corefile зберігається у ConfigMap з назвою coredns. Щоб редагувати його, використовуйте команду:

kubectl -n kube-system edit configmap coredns

Потім додайте log у розділ Corefile, як показано у прикладі нижче:

apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        log
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          upstream
          fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }    

Після збереження змін може знадобитися до хвилини або двох, щоб Kubernetes поширив ці зміни на Podʼи CoreDNS.

Далі зробіть кілька запитів і перегляньте логи, як показано у попередніх розділах цього документа. Якщо Podʼи CoreDNS отримують запити, ви повинні побачити їх в логах.

Ось приклад запиту у логах:

.:53
2018/08/15 14:37:15 [INFO] CoreDNS-1.2.0
2018/08/15 14:37:15 [INFO] linux/amd64, go1.10.3, 2e322f6
CoreDNS-1.2.0
linux/amd64, go1.10.3, 2e322f6
2018/09/07 15:29:04 [INFO] plugin/reload: Running configuration MD5 = 162475cdf272d8aa601e6fe67a6ad42f
2018/09/07 15:29:04 [INFO] Reloading complete
172.17.0.18:41675 - [07/Sep/2018:15:29

:11 +0000] 59925 "A IN kubernetes.default.svc.cluster.local. udp 54 false 512" NOERROR qr,aa,rd,ra 106 0.000066649s

Чи має CoreDNS достатні дозволи?

CoreDNS повинен мати можливість переглядати повʼязані Service та endpoint ресурси для правильного розпізнавання імен служб.

Приклад повідомлення про помилку:

2022-03-18T07:12:15.699431183Z [INFO] 10.96.144.227:52299 - 3686 "A IN serverproxy.contoso.net.cluster.local. udp 52 false 512" SERVFAIL qr,aa,rd 145 0.000091221s

Спочатку отримайте поточну ClusterRole system:coredns:

kubectl describe clusterrole system:coredns -n kube-system

Очікуваний вивід:

PolicyRule:
  Resources                        Non-Resource URLs  Resource Names  Verbs
  ---------                        -----------------  --------------  -----
  endpoints                        []                 []              [list watch]
  namespaces                       []                 []              [list watch]
  pods                             []                 []              [list watch]
  services                         []                 []              [list watch]
  endpointslices.discovery.k8s.io  []                 []              [list watch]

Якщо відсутні будь-які дозволи, відредагуйте ClusterRole, щоб додати їх:

kubectl edit clusterrole system:coredns -n kube-system

Приклад вставки дозволів для EndpointSlices:

...
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
...

Чи ви у правильному просторі імен для служби?

DNS-запити, які не вказують простір імен, обмежені простором імен Podʼа.

Якщо простір імен Podʼа та Service відрізняються, DNS-запит повинен включати простір імен Service.

Цей запит обмежений простором імен Podʼа:

kubectl exec -i -t dnsutils -- nslookup <service-name>

Цей запит вказує простір імен:

kubectl exec -i -t dnsutils -- nslookup <service-name>.<namespace>

Щоб дізнатися більше про розпізнавання імен, дивіться DNS для Service та Pod.

Відомі проблеми

Деякі дистрибутиви Linux (наприклад, Ubuntu) стандартно використовують локальний резолвер DNS (systemd-resolved). Systemd-resolved переміщує та замінює /etc/resolv.conf на файл-заглушку, що може спричинити фатальний цикл переспрямовування при розпізнаванні імен на вихідних серверах. Це можна виправити вручну, використовуючи прапорець --resolv-conf kubelet, щоб вказати правильний resolv.confsystemd-resolved це /run/systemd/resolve/resolv.conf). kubeadm автоматично визначає systemd-resolved та налаштовує відповідні прапорці kubelet.

Установки Kubernetes не налаштовують файли resolv.conf вузлів для використання кластерного DNS стандартно, оскільки цей процес властивий для певного дистрибутиву. Це, можливо, має бути реалізовано надалі.

У Linux бібліотека libc (відома як glibc) має обмеження для записів nameserver DNS на 3 стандартно, і Kubernetes потрібно використовувати 1 запис nameserver. Це означає, що якщо локальна установка вже використовує 3 nameserver, деякі з цих записів будуть втрачені. Щоб обійти це обмеження, вузол може запускати dnsmasq, який надасть більше записів nameserver. Ви також можете використовувати прапорець --resolv-conf kubelet.

Якщо ви використовуєте Alpine версії 3.17 або раніше як базовий образ, DNS може працювати неправильно через проблему з дизайном Alpine. До версії musl 1.24 не включено резервне перемикання на TCP для stub-резолвера DNS, що означає, що будь-який виклик DNS понад 512 байтів завершиться невдачею. Будь ласка, оновіть свої образи до версії Alpine 3.18 або вище.

Що далі

2.20 - Оголошення мережевої політики

Цей документ допоможе вам розпочати використання API мережевої політики Kubernetes NetworkPolicy API, щоб оголосити політики мережі, які керують тим, як Podʼи спілкуються один з одним.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.8. Для перевірки версії введіть kubectl version.

Переконайтеся, що ви налаштували постачальника мережі з підтримкою політики мережі. Існує кілька постачальників мережі, які підтримують NetworkPolicy, включаючи:

Створення nginx deployment та надання доступу через Service

Щоб переглянути, як працює політика мережі Kubernetes, почніть зі створення Deployment nginx.

kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

Експонуйте Deployment через Service під назвою nginx.

kubectl expose deployment nginx --port=80
service/nginx exposed

Вищезазначені команди створюють Deployment з Podʼом nginx і експонують Deployment через Service під назвою nginx. Pod nginx та Deployment знаходяться в просторі імен default.

kubectl get svc,pod
NAME                        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/kubernetes          10.100.0.1    <none>        443/TCP    46m
service/nginx               10.100.0.16   <none>        80/TCP     33s

NAME                        READY         STATUS        RESTARTS   AGE
pod/nginx-701339712-e0qfq   1/1           Running       0          35s

Перевірте роботу Service, звернувшись до неї з іншого Podʼа

Ви повинні мати можливість звернутися до нового Service nginx з інших Podʼів. Щоб отримати доступ до Service nginx з іншого Podʼа в просторі імен default, запустіть контейнер busybox:

kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh

У вашій оболонці запустіть наступну команду:

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

Обмеження доступу до Service nginx

Щоб обмежити доступ до Service nginx так, щоб запити до неї могли робити лише Podʼи з міткою access: true, створіть обʼєкт NetworkPolicy наступним чином:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"

Назва обʼєкта NetworkPolicy повинна бути дійсним піддоменом DNS.

Назначте політику для Service

Використовуйте kubectl для створення NetworkPolicy з файлу nginx-policy.yaml вище:

kubectl apply -f https://k8s.io/examples/service/networking/nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created

Перевірте доступ до Service, коли мітка доступу не визначена

Коли ви намагаєтеся отримати доступ до Service nginx з Podʼа без відповідних міток, запит завершується тайм-аутом:

kubectl run busybox --rm -ti --image=busybox:1.28 -- /bin/sh

У вашій оболонці виконайте команду:



wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out

Визначте мітку доступу і перевірте знову

Ви можете створити Pod із відповідними мітками, щоб переконатися, що запит дозволено:

kubectl run busybox --rm -ti --labels="access=true" --image=busybox:1.28 -- /bin/sh

У вашій оболонці запустіть команду:

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

2.21 - Розробка Cloud Controller Manager

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.11 [beta]

Cloud Controller Manager (cloud-controller-manager) є компонент панелі управління Kubernetes, що інтегрує управління логікою певної хмари. Cloud controller manager дозволяє звʼязувати ваш кластер з API хмарного провайдера та відокремлює компоненти, що взаємодіють з хмарною платформою від компонентів, які взаємодіють тільки в кластері.

Відокремлюючи логіку сумісності між Kubernetes і базовою хмарною інфраструктурою, компонент cloud-controller-manager дає змогу хмарним провайдерам випускати функції з іншою швидкістю порівняно з основним проєктом Kubernetes.

Контекст

Оскільки постачальники хмар розвиваються та випускаються іншим темпом порівняно з проєктом Kubernetes, абстрагування специфічного для постачальників коду в окремий бінарний файл cloud-controller-manager дозволяє хмарним постачальникам еволюціонувати незалежно від основного коду Kubernetes.

Проєкт Kubernetes надає кістяк коду cloud-controller-manager з інтерфейсами Go, щоб дозволити вам (або вашому постачальнику хмар) додати свої власні реалізації. Це означає, що постачальник хмар може реалізувати cloud-controller-manager, імпортуючи пакунки з ядра Kubernetes; кожен постачальник хмар буде реєструвати свій власний код, викликаючи cloudprovider.RegisterCloudProvider, щоб оновити глобальну змінну доступних постачальників хмар.

Розробка

Зовнішня реалізація

Для створення зовнішнього cloud-controller-manager для вашої хмари:

  1. Створіть пакунок Go з реалізацією, яка задовольняє cloudprovider.Interface.
  2. Використовуйте main.go у cloud-controller-managerр з ядра Kubernetes як шаблон для свого main.go. Як зазначено вище, єдина відмінність полягатиме у пакунку хмари, який буде імпортуватися.
  3. Імпортуйте свій пакунок хмари в main.go, переконайтеся, що ваш пакунок має блок init для запуску cloudprovider.RegisterCloudProvider.

Багато постачальників хмар публікують свій код контролера як відкритий код. Якщо ви створюєте новий cloud-controller-manager з нуля, ви можете взяти наявний зовнішній cloud-controller-manager як вашу вихідну точку.

У коді Kubernetes

Для внутрішніх постачальників хмар ви можете запустити cloud-controller-managerу, що працює у внутрішньому коді, як DaemonSet у вашому кластері. Див. Адміністрування Cloud Controller Manager для отримання додаткової інформації.

2.22 - Увімкнення або вимкнення API Kubernetes

На цій сторінці показано, як увімкнути або вимкнути версію API зі вузла панелі управління вашого кластера.

Конкретні версії API можна увімкнути або вимкнути, передаючи --runtime-config=api/<version> як аргумент командного рядка до сервера API. Значення для цього аргументу є розділеним комами списком версій API. Пізніші значення перекривають попередні.

Аргумент командного рядка runtime-config також підтримує 2 спеціальні ключі:

  • api/all, що представляє всі відомі API.
  • api/legacy, що представляє лише застарілі API. Застарілі API — це будь-які API, які були явно визнані застарілими.

Наприклад, щоб вимкнути всі версії API, крім v1, передайте --runtime-config=api/all=false,api/v1=true до kube-apiserver.

Що далі

Прочитайте повну документацію для компонента kube-apiserver.

2.23 - Шифрування конфіденційних даних у спокої

Усі API в Kubernetes, які дозволяють записувати постійні дані ресурсів API, підтримують шифрування у спокої. Наприклад, ви можете увімкнути шифрування у спокої для Secret. Це шифрування у спокої є додатковим до будь-якого шифрування на рівні системи для кластера etcd або для файлових систем на вузлах, де ви запускаєте kube-apiserver.

На цій сторінці показано, як увімкнути та налаштувати шифрування даних API у спокої.

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

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

  • Це завдання передбачає, що ви запускаєте сервер API Kubernetes як статичний Pod на кожному вузлі панелі управління.

  • Панель управління кластера обовʼязково має використовувати etcd версії 3.x (головна версія 3, будь-яка мінорна версія).

  • Для шифрування власного ресурсу ваш кластер повинен працювати на Kubernetes v1.26 або новіше.

  • Щоб використовувати символ підстановки для відповідності ресурсів, ваш кластер повинен працювати на Kubernetes v1.27 або новіше.

Для перевірки версії введіть kubectl version.

Визначте, чи вже увімкнено шифрування у спокої

Стандартно, сервер API зберігає текстові представлення ресурсів у etcd без шифрування у спокої.

Процес kube-apiserver приймає аргумент --encryption-provider-config, який вказує шлях до конфігураційного файлу. Зміст цього файлу, якщо ви його вказали, контролює спосіб шифрування даних API Kubernetes у etcd. Якщо ви запускаєте kube-apiserver без аргументу командного рядка --encryption-provider-config, то у вас немає увімкненого шифрування у спокої. Якщо ви запускаєте kube-apiserver з аргументом командного рядка --encryption-provider-config, і файл, на який він посилається, вказує на постачальника шифрування identity як першого постачальника шифрування у списку, то у вас немає увімкненого шифрування у спокої (типовий постачальник identity не надає захисту конфіденційності.)

Якщо ви запускаєте kube-apiserver з аргументом командного рядка --encryption-provider-config, і файл, на який він посилається, вказує на іншого постачальника, а не identity, як першого постачальника шифрування у списку, то ви вже маєте увімкнене шифрування у спокої. Однак ця перевірка не показує, чи вдалася попередня міграція до зашифрованого сховища. Якщо ви не впевнені, переконайтеся, що всі відповідні дані зашифровані.

Розуміння конфігурації шифрування у спокої

---
#
# УВАГА: це приклад конфігурації.
# Не використовуйте його для вашого кластера!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example # a custom resource API
    providers:
      # Ця конфігурація не забезпечує конфіденційність даних. Перший
      # налаштований постачальник вказує механізм "identity", який
      # зберігає ресурси як простий текст.
      #
      - identity: {} # простий текст, іншими словами, шифрування НЕМАЄ
      - aesgcm:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
      - aescbc:
          keys:
            - name: key1
              secret: c2VjcmV0IGlzIHNlY3VyZQ==
            - name: key2
              secret: dGhpcyBpcyBwYXNzd29yZA==
      - secretbox:
          keys:
            - name: key1
              secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
  - resources:
      - events
    providers:
      - identity: {} # не шифрувати Events навіть якщо *.* вказаний нижче
  - resources:
      - '*.apps' # використання символу підстановки потребує Kubernetes 1.27 або пізніше
    providers:
      - aescbc:
          keys:
          - name: key2
            secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
  - resources:
      - '*.*' # використання символу підстановки потребує Kubernetes 1.27 або пізніше
    providers:
      - aescbc:
          keys:
          - name: key3
            secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==

Кожен елемент масиву resources — це окрема конфігурація і містить повну конфігурацію. Поле resources.resources — це масив імен ресурсів Kubernetes (resource або resource.group), які мають бути зашифровані, наприклад, Secrets, ConfigMaps або інші ресурси.

Якщо ви додаєте власні ресурси до EncryptionConfiguration, і версія кластера — 1.26 або новіша, то будь-які нові створені власні ресурси, зазначені в EncryptionConfiguration, будуть зашифровані. Будь-які власні ресурси, які існували в etcd до цієї версії та конфігурації, залишаться незашифрованими до тих пір, поки вони наступного разу не будуть записані у сховище. Така ж поведінка застосовується й до вбудованих ресурсів. Дивіться розділ Переконайтеся, що всі секрети зашифровані.

Масив providers — це упорядкований список можливих постачальників шифрування, які можна використовувати для перелічених вами API. Кожен постачальник підтримує кілька ключів — ключі перевіряються по черзі для розшифрування, і якщо постачальник є першим, перший ключ використовується для шифрування.

Для кожного елементу може бути вказано лише один тип постачальника (identity або aescbc може бути наданий, але не обидва в одному елементі). Перший постачальник у списку використовується для шифрування ресурсів, записаних у сховище. При читанні ресурсів зі сховища кожен постачальник, який відповідає збереженим даним, намагається по черзі розшифрувати дані. Якщо жоден постачальник не може прочитати збережені дані через несумісність формату або секретного ключа, повертається помилка, яка перешкоджає клієнтам отримати доступ до цього ресурсу.

EncryptionConfiguration підтримує використання символів підстановки для вказівки ресурсів, які мають бути зашифровані. Використовуйте '*.<group>', щоб зашифрувати всі ресурси у групі (наприклад, '*.apps' у вищезазначеному прикладі) або '*.*' для шифрування всіх ресурсів. '*.' може бути використаний для шифрування всіх ресурсів у групі ядра. '*.*' зашифрує всі ресурси, навіть власні ресурси, які додаються після запуску сервера API.

Якщо у вас є символ підстановки, який охоплює ресурси, і ви хочете відмовитися від шифрування у спокої певного типу ресурсу, ви можете досягати цього, додавши окремий елемент масиву resources з іменем ресурсу, який ви хочете виключити, за яким слідує елемент масиву providers, де ви вказуєте постачальника identity. Ви додаєте цей елемент до списку так, щоб він зʼявився раніше, ніж конфігурація, де ви вказуєте шифрування (постачальник, який не є identity).

Наприклад, якщо ввімкнено '*.*', і ви хочете відмовитися від шифрування для Events та ConfigMaps, додайте новий раніший елемент до resources, за яким слідує елемент масиву постачальників з identity як постачальник. Більш конкретний запис повинен зʼявитися перед записом з символом підстановки.

Новий елемент буде схожий на:

  ...
  - resources:
      - configmaps. # спеціально з ядра API групи,
                    # через крапку в кінці
      - events
    providers:
      - identity: {}
  # а потім інші записи в resources

Переконайтеся, що виключення перераховане до позначки символу підстановки '*.*' в масиві ресурсів, щоб надати йому пріоритет.

Для отримання більш детальної інформації про структуру EncryptionConfiguration, зверніться до API конфігурації шифрування.

Доступні постачальники

Перш ніж налаштувати шифрування у спокої для даних у Kubernetes API вашого кластера, вам потрібно вибрати, які постачальники ви будете використовувати.

Наступна таблиця описує кожного доступного постачальника.

Постачальники для шифрування у спокої Kubernetes
НазваШифруванняСтійкістьШвидкістьДовжина ключа
identityВідсутнєН/ДН/ДН/Д
Ресурси зберігаються як є без шифрування. Коли встановлено як перший постачальник, ресурс буде розшифрований при записі нових значень. Існуючі зашифровані ресурси автоматично не перезаписуються даними у відкритому тексті. Постачальник `identity` є типовим, якщо ви не вказали інше.
aescbcAES-CBC з використанням padding PKCS#7СлабкаШвидка32 байти
Не рекомендується через вразливість CBC до атаки padding oracle attacks. Вміст ключа доступний з хоста панелі управління.
aesgcmAES-GCM з випадковим нонсомПотрібно оновлювати через кожні 200,000 записівНайшвидший16, 24 або 32 байти
Не рекомендується для використання, крім як у випадку, коли реалізована автоматизована схема оновлення ключів. Вміст ключа доступний з хоста панелі управління.
kms v1 (застарілий з Kubernetes v1.28)Використовує схему шифрування конвертів із DEK для кожного ресурсу.НайміцнішийПовільний (порівняно з kms версією 2)32 байти
Дані шифруються за допомогою ключів шифрування даних (DEK) з використанням AES-GCM; DEK шифруються ключами шифрування ключів (KEKs) згідно з конфігурацією в Службі управління ключами (KMS). Просте оновлення ключа, з новим DEK, що генерується для кожного шифрування, та оновлення KEK, контрольоване користувачем.
Прочитайте, як налаштувати постачальника KMS V1.
kms v2Використовує схему шифрування конвертів із DEK для кожного сервера API.НайміцнішийШвидкий32 байти
Дані шифруються за допомогою ключів шифрування даних (DEKs) з використанням AES-GCM; DEKs шифруються ключами шифрування ключів (KEKs) згідно з конфігурацією в Службі управління ключами (KMS). Kubernetes генерує новий DEK для кожного шифрування з секретного насіння. Насіння змінюється кожного разу, коли змінюється KEK.
Добрий вибір, якщо використовується сторонній інструмент для управління ключами. Доступний як стабільний з Kubernetes v1.29.
Прочитайте, як налаштувати постачальника KMS V2.
secretboxXSalsa20 і Poly1305НайміцнішийШвидший32 байти
Використовує відносно нові технології шифрування, які можуть не бути прийнятними в середовищах, які вимагають високого рівня перегляду. Вміст ключа доступний з хоста панелі управління.

Постачальник identity є типовим, якщо ви не вказали інше. Постачальник identity не шифрує збережені дані та не надає жодного додаткового захисту конфіденційності.

Зберігання ключів

Локальне зберігання ключів

Шифрування конфіденційних даних за допомогою локально керованих ключів захищає від компрометації etcd, але не забезпечує захисту від компрометації хосту. Оскільки ключі шифрування зберігаються на хості у файлі EncryptionConfiguration YAML, кваліфікований зловмисник може отримати доступ до цього файлу і витягнути ключі шифрування.

Зберігання ключів сервісом KMS

Постачальник KMS використовує шифрування конвертів: Kubernetes шифрує ресурси за допомогою ключа даних, а потім шифрує цей ключ даних за допомогою служби керування шифруванням. Kubernetes генерує унікальний ключ даних для кожного ресурсу. API-сервер зберігає зашифровану версію ключа даних в etcd поряд із шифротекстом; при читанні ресурсу API-сервер викликає служби керування шифруванням і надає як шифротекст, так і (зашифрований) ключ даних. У межах служби керування шифруванням, постачальник використовує ключ шифрування ключа, щоб розшифрувати ключ даних, розшифровує ключ даних і, нарешті, відновлює звичайний текст. Комунікація між панеллю управління та KMS вимагає захисту під час передачі, такого як TLS.

Використання шифрування конвертів створює залежність від ключа шифрування ключа, який не зберігається в Kubernetes. У випадку KMS зловмиснику, який намагається отримати несанкціонований доступ до значень у відкритому тексті, необхідно скомпрометувати etcd та стороннього постачальника KMS.

Захист для ключів шифрування

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

Навіть коли ви покладаєтесь на постачальника для керування використанням та життєвим циклом основного ключа шифрування (або ключів), ви все одно відповідаєте за те, щоб контроль доступу та інші заходи безпеки для служби керування шифруванням були відповідними для ваших потреб у безпеці.

Зашифруйте ваші дані

Згенеруйте ключ шифрування

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

Розпочніть з генерації нового ключа шифрування, а потім закодуйте його за допомогою base64:

Згенеруйте випадковий ключ з 32 байтів і закодуйте його у форматі base64. Ви можете використовувати цю команду:

head -c 32 /dev/urandom | base64

Ви можете використовувати /dev/hwrng замість /dev/urandom, якщо ви хочете використовувати вбудований апаратний генератор ентропії вашого ПК. Не всі пристрої з Linux надають апаратний генератор випадкових чисел.

Згенеруйте випадковий ключ з 32 байтів і закодуйте його у форматі base64. Ви можете використовувати цю команду:

head -c 32 /dev/urandom | base64

Згенеруйте випадковий ключ з 32 байтів і закодуйте його у форматі base64. Ви можете використовувати цю команду:

# Не запускайте це в сесії, де ви встановили насіння генератора випадкових чисел.
[Convert]::ToBase64String((1..32|%{[byte](Get-Random -Max 256)}))

Реплікація ключа шифрування

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

Як мінімум використовуйте шифрування під час передачі даних — наприклад, захищену оболонку (SSH). Для більшої безпеки використовуйте асиметричне шифрування між вузлами або змініть підхід, який ви використовуєте, щоб ви покладалися на шифрування KMS.

Створіть файл конфігурації шифрування

Створіть новий файл конфігурації шифрування. Зміст повинен бути подібним до:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - aescbc:
          keys:
            - name: key1
              # Див. наступний текст для отримання додаткової інформації про секретне значення
              secret: <BASE 64 ENCODED SECRET>
      - identity: {} # цей резервний варіант дозволяє читати незашифровані секрети;
                     # наприклад, під час початкової міграції

Щоб створити новий ключ шифрування (який не використовує KMS), див. Згенеруйте ключ шифрування.

Використовуйте новий файл конфігурації шифрування

Вам потрібно буде приєднати новий файл конфігурації шифрування до статичного Podʼа kube-apiserver. Ось приклад того, як це зробити:

  1. Збережіть новий файл конфігурації шифрування у /etc/kubernetes/enc/enc.yaml на вузлі панелі управління.

  2. Відредагуйте маніфест для статичного Podʼа kube-apiserver: /etc/kubernetes/manifests/kube-apiserver.yaml, щоб він був подібний до:

    ---
    #
    # Це фрагмент маніфеста для статичного Podʼа.
    # Перевірте, чи це правильно для вашого кластера та для вашого API-сервера.
    #
    apiVersion: v1
    kind: Pod
    metadata:
      annotations:
        kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.20.30.40:443
      creationTimestamp: null
      labels:
        app.kubernetes.io/component: kube-apiserver
        tier: control-plane
      name: kube-apiserver
      namespace: kube-system
    spec:
      containers:
      - command:
        - kube-apiserver
        ...
        - --encryption-provider-config=/etc/kubernetes/enc/enc.yaml  # додайте цей рядок
        volumeMounts:
        ...
        - name: enc                           # додайте цей рядок
          mountPath: /etc/kubernetes/enc      # додайте цей рядок
          readOnly: true                      # додайте цей рядок
        ...
      volumes:
      ...
      - name: enc                             # додайте цей рядок
        hostPath:                             # додайте цей рядок
          path: /etc/kubernetes/enc           # додайте цей рядок
          type: DirectoryOrCreate             # додайте цей рядок
      ...
    
  3. Перезапустіть свій API-сервер.

Тепер у вас є шифрування для одного вузла панелі управління. У типовому кластері Kubernetes є кілька вузлів керування, тому зробіть це на інших вузлах.

Переконфігуруйте інші вузли керування

Якщо у вашому кластері є кілька API-серверів, вам слід розгорнути зміни по черзі на кожному API-сервері.

Коли ви плануєте оновлення конфігурації шифрування вашого кластера, сплануйте це так, щоб API-сервери у вашій панелі управління завжди могли розшифрувати збережені дані (навіть під час часткового впровадження змін).

Переконайтеся, що ви використовуєте однакову конфігурацію шифрування на кожному вузлі керування.

Перевірте, що нові дані записані зашифровано

Дані зашифровуються під час запису в etcd. Після перезапуску вашого kube-apiserver будь-який новий створений або оновлений Secret (або інші види ресурсів, налаштовані в EncryptionConfiguration), повинні бути зашифровані при зберіганні.

Щоб перевірити це, ви можете використовувати інтерфейс командного рядка etcdctl, щоб отримати вміст вашого секретного ключа.

Цей приклад показує, як це перевірити для шифрування Secret API.

  1. Створіть новий Secret під назвою secret1 у просторі імен default:

    kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
    
  2. Використовуючи інтерфейс командного рядка etcdctl, прочитайте цей Секрет з etcd:

    ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C
    

    де [...] повинно бути додатковими аргументами для приєднання до сервера etcd.

    Наприклад:

    ETCDCTL_API=3 etcdctl \
       --cacert=/etc/kubernetes/pki/etcd/ca.crt   \
       --cert=/etc/kubernetes/pki/etcd/server.crt \
       --key=/etc/kubernetes/pki/etcd/server.key  \
       get /registry/secrets/default/secret1 | hexdump -C
    

    Вивід подібний до цього (скорочено):

    00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
    00000010  73 2f 64 65 66 61 75 6c  74 2f 73 65 63 72 65 74  |s/default/secret|
    00000020  31 0a 6b 38 73 3a 65 6e  63 3a 61 65 73 63 62 63  |1.k8s:enc:aescbc|
    00000030  3a 76 31 3a 6b 65 79 31  3a c7 6c e7 d3 09 bc 06  |:v1:key1:.l.....|
    00000040  25 51 91 e4 e0 6c e5 b1  4d 7a 8b 3d b9 c2 7c 6e  |%Q...l..Mz.=..|n|
    00000050  b4 79 df 05 28 ae 0d 8e  5f 35 13 2c c0 18 99 3e  |.y..(..._5.,...>|
    [...]
    00000110  23 3a 0d fc 28 ca 48 2d  6b 2d 46 cc 72 0b 70 4c  |#:..(.H-k-F.r.pL|
    00000120  a5 fc 35 43 12 4e 60 ef  bf 6f fe cf df 0b ad 1f  |..5C.N`..o......|
    00000130  82 c4 88 53 02 da 3e 66  ff 0a                    |...S..>f..|
    0000013a
    
  3. Перевірте, що збережений Secret має префікс k8s:enc:aescbc:v1:, що вказує на те, що провайдер aescbc зашифрував результати. Переконайтеся, що імʼя ключа, вказане в etcd, відповідає імені ключа, вказаному в EncryptionConfiguration вище. У цьому прикладі ви бачите, що ключ шифрування під назвою key1 використовується в etcd і в EncryptionConfiguration.

  4. Перевірте, що Secret правильно розшифровується під час отримання через API:

    kubectl get secret secret1 -n default -o yaml
    

    Вивід повинен містити mykey: bXlkYXRh, з вмістом mydata, закодованим за допомогою base64; прочитайте декодування Secret, щоб дізнатися, як повністю декодувати Secret.

Забезпечте шифрування всіх відповідних даних

Часто недостатньо лише переконатися, що нові обʼєкти зашифровані: ви також хочете, щоб шифрування застосовувалося до обʼєктів, які вже збережені.

У цьому прикладі ви налаштували свій кластер таким чином, що Secret зашифровані при записі. Виконання операції заміни для кожного Secret зашифрує цей вміст в спокої, де обʼєкти залишаються незмінними.

Ви можете виконати цю зміну для всіх Secret у вашому кластері:

# Виконайте це в якості адміністратора, який може читати і записувати всі Secret
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

Команда вище зчитує всі Secret, а потім оновлює їх з тими самими даними, щоб застосувати шифрування на стороні сервера.

Запобігання отриманню відкритого тексту

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

Після того, як всі Secret у вашому кластері зашифровані, ви можете видалити identity частину конфігурації шифрування. Наприклад:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <BASE 64 ENCODED SECRET>
      - identity: {} # ВИДАЛІТЬ ЦЮ СТРОКУ

… а потім перезапустіть кожен API-сервер по черзі. Ця зміна запобігає API-серверу отримувати доступ до Secret у відкритому текстовому форматі, навіть випадково.

Ротація ключа розшифрування

Зміна ключа шифрування для Kubernetes без простою вимагає багатокрокової операції, особливо в умовах високодоступного розгортання, де працюють кілька процесів kube-apiserver.

  1. Згенеруйте новий ключ і додайте його як другий запис ключа для поточного провайдера на всіх вузлах панелі управління.
  2. Перезапустіть усі процеси kube-apiserver, щоб кожен сервер міг розшифрувати будь-які дані, які зашифровані новим ключем.
  3. Зробіть безпечне резервне копіювання нового ключа шифрування. Якщо ви втратите всі копії цього ключа, вам доведеться видалити всі ресурси, які були зашифровані загубленим ключем, і робочі навантаження можуть не працювати як очікується протягом часу, коли шифровання у спокої зламане.
  4. Зробіть новий ключ першим записом у масиві keys, щоб він використовувався для шифрування у спокої для нових записів.
  5. Перезапустіть всі процеси kube-apiserver, щоб кожен хост панелі управління тепер шифрував за допомогою нового ключа.
  6. Як привілейований користувач виконайте команду kubectl get secrets --all-namespaces -o json | kubectl replace -f -, щоб зашифрувати всі наявні секрети новим ключем.
  7. Після того, як ви оновили всі наявні секрети, щоб вони використовували новий ключ, і зробили безпечне резервне копіювання нового ключа, видаліть старий ключ розшифрування з конфігурації.

Розшифрування всіх даних

У цьому прикладі показано, як зупинити шифрування API Secret у спокої. Якщо ви шифруєте інші види API, адаптуйте кроки відповідно.

Щоб вимкнути шифрування у спокої, розмістіть провайдера identity як перший запис у вашому файлі конфігурації шифрування:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      # перерахуйте тут будь-які інші ресурси, які ви раніше
      # шифрували у спокої
    providers:
      - identity: {} # додайте цей рядок
      - aescbc:
          keys:
            - name: key1
              secret: <BASE 64 ENCODED SECRET> # залиште це на місці
                                               # переконайтеся, що воно йде після "identity"

Потім виконайте таку команду, щоб примусити розшифрування всіх Secrets:

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

Після того, як ви замінили всі наявні зашифровані ресурси резервними даними, які не використовують шифрування, ви можете видалити налаштування шифрування з kube-apiserver.

Налаштування автоматичного перезавантаження

Ви можете налаштувати автоматичне перезавантаження конфігурації провайдера шифрування. Ця настройка визначає, чи має API server завантажувати файл, який ви вказали для --encryption-provider-config тільки один раз при запуску, або автоматично кожного разу, коли ви змінюєте цей файл. Увімкнення цієї опції дозволяє змінювати ключі для шифрування у спокої без перезапуску API server.

Щоб дозволити автоматичне перезавантаження, налаштуйте API server для запуску з параметром: --encryption-provider-config-automatic-reload=true. Коли ввімкнено, зміни файлів перевіряються кожну хвилину для спостереження за модифікаціями. Метрика apiserver_encryption_config_controller_automatic_reload_last_timestamp_seconds визначає час, коли нова конфігурація набирає чинності. Це дозволяє виконувати ротацію ключів шифрування без перезапуску API-сервера.

Що далі

2.24 - Розшифровування конфіденційних даних, які вже зашифровані у спокої

Усі API в Kubernetes, що дозволяють записувати постійні дані ресурсів, підтримують шифрування у спокої. Наприклад, ви можете увімкнути шифрування у спокої для Secret. Це шифрування у спокої додається до будь-якого шифрування системного рівня для кластера etcd або файлових систем на вузлах, де запущений kube-apiserver.

Ця сторінка показує, як перейти від шифрування даних API у спокої, щоб дані API зберігалися у незашифрованому вигляді. Ви можете зробити це для покращення продуктивності; проте, якщо шифрування було раціональним рішенням для деяких даних, то його також варто залишити.

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

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

  • Це завдання передбачає, що Kubernetes API server працює як статичний Pod на кожному вузлі панелі управління.

  • Панель управління вашого кластера має використовувати etcd v3.x (основна версія 3, будь-яка мінорна версія).

  • Щоб зашифрувати власний ресурс, ваш кластер повинен працювати на Kubernetes v1.26 або новіше.

  • У вас повинні бути деякі дані API, які вже зашифровані.

Для перевірки версії введіть kubectl version.

Визначте, чи вже увімкнене шифрування у спокої

Стандартно API server використовує провайдера identity, який зберігає представлення ресурсів у текстовому вигляді. Стандартний провайдер identity не надає жодного захисту конфіденційності.

Процес kube-apiserver приймає аргумент --encryption-provider-config, який вказує шлях до файлу конфігурації. Вміст цього файлу, якщо ви вказали його, керує тим, як дані API Kubernetes шифруються в etcd. Якщо він не вказаний, у вас не увімкнене шифрування у спокої.

Форматом цього файлу конфігурації є YAML, який представляє конфігурацію API-ресурсу під назвою EncryptionConfiguration. Приклад конфігурації ви можете побачити в Шифрування конфіденційних даних у спокої.

Якщо встановлено --encryption-provider-config, перевірте, які ресурси (наприклад, secrets) налаштовані для шифрування, і який провайдер використовується. Переконайтеся, що вподобаний провайдер для цього типу ресурсу не є identity; ви встановлюєте лише identity (без шифрування) як типовий, коли хочете вимкнути шифрування у спокої. Перевірте, чи перший провайдер, зазначений для ресурсу, щось інше, ніж identity, що означає, що будь-яка нова інформація, записана до ресурсів цього типу, буде зашифрована, як налаштовано. Якщо ви бачите, що identity — перший провайдер для якого-небудь ресурсу, це означає, що ці ресурси записуються в etcd без шифрування.

Розшифруйте всі дані

Цей приклад показує, як зупинити шифрування API Secret у спокої. Якщо ви шифруєте інші види API, адаптуйте кроки відповідно.

Визначте файл конфігурації шифрування

Спочатку знайдіть файли конфігурації API server. На кожному вузлі панелі управління маніфест статичного Pod для kube-apiserver вказує аргумент командного рядка --encryption-provider-config. Ймовірно, цей файл монтується у статичний Pod за допомогою томуhostPath. Після того, як ви знайдете том, ви можете знайти файл у файловій системі вузла і перевірити його.

Налаштуйте API server для розшифрування обʼєктів

Щоб вимкнути шифрування у спокої, розмістіть провайдера identity як перший запис у вашому файлі конфігурації шифрування.

Наприклад, якщо ваш наявний файл EncryptionConfiguration виглядає так:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            # Не використовуйте цей (недійсний) приклад ключа для шифрування
            - name: example
              secret: 2KfZgdiq2K0g2YrYpyDYs9mF2LPZhQ==

то змініть його на:

---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - identity: {} # додайте цей рядок
      - aescbc:
          keys:
            - name: example
              secret: 2KfZgdiq2K0g2YrYpyDYs9mF2LPZhQ==

і перезапустіть kube-apiserver Pod на цьому вузлі.

Переконфігуруйте інші вузли панелі управління

Якщо у вашому кластері є кілька серверів API, ви повинні по черзі впроваджувати зміни на кожен з серверів API.

Переконайтеся, що ви використовуєте однакову конфігурацію шифрування на кожному вузлі панелі управління.

Примусове розшифрування

Потім виконайте таку команду, щоб примусити розшифрування всіх Secrets:

# Якщо ви розшифровуєте інший тип обʼєкта, змініть "secrets" відповідно.
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

Після того, як ви замінили всі наявні зашифровані ресурси резервними даними, які не використовують шифрування, ви можете видалити налаштування шифрування з kube-apiserver.

Параметри командного рядка, які потрібно видалити:

  • --encryption-provider-config
  • --encryption-provider-config-automatic-reload

Знову перезапустіть kube-apiserver Pod, щоб застосувати нову конфігурацію.

Переконфігуруйте інші вузли панелі управління

Якщо у вашому кластері є кілька серверів API, ви знову повинні по черзі впроваджувати зміни на кожен з серверів API.

Переконайтеся, що ви використовуєте однакову конфігурацію шифрування на кожному вузлі панелі управління.

Що далі

2.25 - Гарантоване планування для критичних Podʼів надбудов

Основні компоненти Kubernetes, такі як сервер API, планувальник і контролер-менеджер, працюють на вузлі панелі управління. Однак надбудови мають працювати на звичайному вузлі кластера. Деякі з цих надбудов є критичними для повноцінної функціональності кластера, наприклад, metrics-server, DNS та інтерфейс користувача. Кластер може перестати правильно працювати, якщо критичний Pod надбудови буде видалений (або вручну, або як побічний ефект іншої операції, такої як оновлення) і стане в очікуванні (наприклад, коли кластер високо завантажений і інші очікувані Podʼи заплановані на місце, звільнене видаленим критичним Podʼом надбудови, або кількість доступних ресурсів на вузлі змінилася з якоїсь іншої причини).

Зверніть увагу, що позначення Podʼа як критичного не означає для повного запобігання його видаленню; це лише запобігає тому, що Pod стає постійно недоступним. Статичний Pod, позначений як критичний, не може бути видалений. Однак, нестатичні Podʼи, позначені як критичні, завжди будуть переплановані.

Позначення Podʼа як критичного

Щоб позначити Pod як критичний, встановіть для цього Podʼа priorityClassName у system-cluster-critical або system-node-critical. system-node-critical має найвищий доступний пріоритет, вищий навіть, ніж system-cluster-critical.

2.26 - Керівництво користувача агента маскування IP

Ця сторінка показує, як налаштувати та ввімкнути ip-masq-agent.

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

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

Для перевірки версії введіть kubectl version.

Керівництво користувача агента маскування IP

ip-masq-agent налаштовує правила iptables для приховування IP-адреси Podʼа за IP-адресою вузла кластера. Це зазвичай робиться, коли трафік надсилається до пунктів призначення за межами діапазону CIDR Podʼів кластера.

Основні терміни

  • NAT (Network Address Translation): Це метод переназначення однієї IP-адреси на іншу шляхом модифікації інформації адреси відправника та/або одержувача у заголовку IP. Зазвичай виконується пристроєм, що здійснює маршрутизацію IP.
  • Маскування (Masquerading): Форма NAT, яка зазвичай використовується для здійснення трансляції багатьох адрес в одну, де декілька початкових IP-адрес маскуються за однією адресою, яка зазвичай є адресою пристрою, що виконує маршрутизацію. У Kubernetes це IP-адреса вузла.
  • CIDR (Classless Inter-Domain Routing): Заснований на маскуванні підмереж змінної довжини, дозволяє вказувати префікси довільної довжини. CIDR ввів новий метод представлення IP-адрес, тепер відомий як нотація CIDR, у якій адреса або маршрутизаційний префікс записується з суфіксом, що вказує кількість бітів префікса, наприклад 192.168.2.0/24.
  • Локальне посилання: Локальна адреса посилання — це мережева адреса, яка є дійсною лише для комунікацій у межах сегмента мережі або домену розсилки, до якого підʼєднано хост. Локальні адреси для IPv4 визначені в блоку адрес 169.254.0.0/16 у нотації CIDR.

ip-masq-agent налаштовує правила iptables для обробки маскування IP-адрес вузлів/Podʼів при надсиланні трафіку до пунктів призначення за межами IP вузла кластера та діапазону IP кластера. Це по суті приховує IP-адреси Podʼів за IP-адресою вузла кластера. У деяких середовищах, трафік до "зовнішніх" адрес має походити з відомої адреси машини. Наприклад, у Google Cloud будь-який трафік до Інтернету має виходити з IP VM. Коли використовуються контейнери, як у Google Kubernetes Engine, IP Podʼа не буде мати виходу. Щоб уникнути цього, ми повинні приховати IP Podʼа за IP адресою VM — зазвичай відомою як "маскування". Типово агент налаштований так, що три приватні IP-діапазони, визначені RFC 1918, не вважаються діапазонами маскування CIDR. Ці діапазони — 10.0.0.0/8, 172.16.0.0/12 і 192.168.0.0/16. Агент також типово вважає локальне посилання (169.254.0.0/16) CIDR не-маскуванням. Агент налаштований на перезавантаження своєї конфігурації з розташування /etc/config/ip-masq-agent кожні 60 секунд, що також можна налаштувати.

приклад маскування/немаскування

Файл конфігурації агента повинен бути написаний у синтаксисі YAML або JSON і може містити три необовʼязкові ключі:

  • nonMasqueradeCIDRs: Список рядків у форматі CIDR, які визначають діапазони без маскування.
  • masqLinkLocal: Булеве значення (true/false), яке вказує, чи маскувати трафік до локального префіксу 169.254.0.0/16. Типово — false.
  • resyncInterval: Інтервал часу, через який агент намагається перезавантажити конфігурацію з диска. Наприклад: '30s', де 's' означає секунди, 'ms' — мілісекунди.

Трафік до діапазонів 10.0.0.0/8, 172.16.0.0/12 і 192.168.0.0/16 НЕ буде маскуватися. Будь-який інший трафік (вважається Інтернетом) буде маскуватися. Прикладом локального пункту призначення з Podʼа може бути IP-адреса його вузла, а також адреса іншого вузла або одна з IP-адрес у діапазоні IP кластера. Будь-який інший трафік буде стандартно маскуватися. Нижче наведено стандартний набір правил, які застосовує агент ip-masq:

iptables -t nat -L IP-MASQ-AGENT
target     prot opt source               destination
RETURN     all  --  anywhere             169.254.0.0/16       /* ip-masq-agent: клієнтський трафік в межах кластера не повинен піддаватися маскуванню */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             10.0.0.0/8           /* ip-masq-agent: клієнтський трафік в межах кластера не повинен піддаватися маскуванню */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             172.16.0.0/12        /* ip-masq-agent: клієнтський трафік в межах кластера не повинен піддаватися маскуванню */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             192.168.0.0/16       /* ip-masq-agent: клієнтський трафік в межах кластера не повинен піддаватися маскуванню */ ADDRTYPE match dst-type !LOCAL
MASQUERADE  all  --  anywhere             anywhere             /* ip-masq-agent: вихідний трафік повинен піддаватися маскуванню (ця відповідність повинна бути після відповідності клієнтським CIDR у межах кластера) */ ADDRTYPE match dst-type !LOCAL

Стандартно, в середовищі GCE/Google Kubernetes Engine, якщо ввімкнуто мережеву політику або ви використовуєте CIDR кластера не в діапазоні 10.0.0.0/8, агент ip-masq-agent буде запущений у вашому кластері. Якщо ви працюєте в іншому середовищі, ви можете додати DaemonSet ip-masq-agent до свого кластера.

Створення агента маскування IP

Щоб створити агента маскування IP, виконайте наступну команду kubectl:

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/ip-masq-agent/master/ip-masq-agent.yaml

Також вам потрібно застосувати відповідну мітку вузла на будь-яких вузлах вашого кластера, на яких ви хочете запустити агента.

kubectl label nodes my-node node.kubernetes.io/masq-agent-ds-ready=true

Додаткову інформацію можна знайти в документації агента маскування IP тут.

У більшості випадків стандартний набір правил має бути достатнім; однак, якщо це не так для вашого кластера, ви можете створити та застосувати ConfigMap, щоб налаштувати діапазони IP-адрес, що залучаються. Наприклад, щоб дозволити розгляд тільки діапазону 10.0.0.0/8 ip-masq-agent, ви можете створити наступний ConfigMap у файлі з назвою "config".

Виконайте наступну команду, щоб додати configmap до вашого кластера:

kubectl create configmap ip-masq-agent --from-file=config --namespace=kube-system

Це оновить файл, розташований у /etc/config/ip-masq-agent, який періодично перевіряється кожен resyncInterval та застосовується до вузла кластера. Після закінчення інтервалу синхронізації ви повинні побачити, що правила iptables відображають ваші зміни:

iptables -t nat -L IP-MASQ-AGENT
Chain IP-MASQ-AGENT (1 references)
target     prot opt source               destination
RETURN     all  --  anywhere             169.254.0.0/16       /* ip-masq-agent: cluster-local traffic should not be subject to MASQUERADE */ ADDRTYPE match dst-type !LOCAL
RETURN     all  --  anywhere             10.0.0.0/8           /* ip-masq-agent: cluster-local
MASQUERADE  all  --  anywhere             anywhere             /* ip-masq-agent: outbound traffic should be subject to MASQUERADE (this match must come after cluster-local CIDR matches) */ ADDRTYPE match dst-type !LOCAL

Стандартно, діапазон локальних посилань (169.254.0.0/16) також обробляється агентом ip-masq, який налаштовує відповідні правила iptables. Щоб агент ip-masq ігнорував локальні посилання, ви можете встановити masqLinkLocal як true у ConfigMap.

nonMasqueradeCIDRs:
  - 10.0.0.0/8
resyncInterval: 60s
masqLinkLocal: true

2.27 - Обмеження використання сховища

Цей приклад демонструє, як обмежити обсяг використання сховища в просторі імен.

У демонстрації використовуються наступні ресурси: ResourceQuota, LimitRange, та PersistentVolumeClaim.

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

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

    Для перевірки версії введіть kubectl version.

Сценарій: Обмеження використання сховища

Адміністратор кластера керує кластером від імені користувачів і хоче контролювати, скільки сховища може використати один простір імен, щоб контролювати витрати.

Адміністратор хоче обмежити:

  1. Кількість заявок на постійні томи в просторі імен
  2. Обсяг сховища, який може бути запитаний кожною заявкою
  3. Загальний обсяг сховища, який може мати простір імен

LimitRange для обмеження запитів на сховище

Додавання LimitRange до простору імен забезпечує встановлення мінімального і максимального розміру запитів на сховище. Сховище запитується через PersistentVolumeClaim. Контролер допуску, який забезпечує дотримання лімітів, відхилить будь-яку PVC, яка перевищує або не досягає встановлених адміністратором значень.

У цьому прикладі, PVC, що запитує 10Gi сховища, буде відхилено, оскільки це перевищує максимум у 2Gi.

apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
  - type: PersistentVolumeClaim
    max:
      storage: 2Gi
    min:
      storage: 1Gi

Мінімальні запити на сховище використовуються, коли провайдер сховища вимагає певних мінімумів. Наприклад, диски AWS EBS мають мінімальну вимогу у 1Gi.

ResourceQuota для обмеження кількості PVC та загальної місткості сховища

Адміністратори можуть обмежити кількість PVC в просторі імен, а також загальну місткість цих PVC. Нові PVC, які перевищують будь-яке максимальне значення, будуть відхилені.

У цьому прикладі, шоста PVC у просторі імен буде відхилена, оскільки вона перевищує максимальну кількість у 5. Альтернативно, максимальна квота у 5Gi при поєднанні з максимальним лімітом у 2Gi вище, не може мати 3 PVC, де кожна має по 2Gi. Це було б 6Gi, запитані для простору імен з лімітом у 5Gi.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: storagequota
spec:
  hard:
    persistentvolumeclaims: "5"
    requests.storage: "5Gi"

Підсумок

Limit range може встановити максимальний поріг для запитів на сховище, тоді як квота на ресурси може ефективно обмежити обсяг сховища, використовуваного простором імен через кількість заявок та загальну місткість. Це дозволяє адміністратору кластера планувати бюджет на сховище кластера без ризику перевитрати будь-якого проєкту.

2.28 - Міграція реплікованої панелі управління на використання менеджера керування хмарою

Менеджер керування хмарою — компонент панелі управління Kubernetes, що інтегрує управління логікою певної хмари. Cloud controller manager дозволяє звʼязувати ваш кластер з API хмарного провайдера та відокремлює компоненти, що взаємодіють з хмарною платформою від компонентів, які взаємодіють тільки в кластері.

Відокремлюючи логіку сумісності між Kubernetes і базовою хмарною інфраструктурою, компонент cloud-controller-manager дає змогу хмарним провайдерам випускати функції з іншою швидкістю порівняно з основним проєктом Kubernetes.

Контекст

У рамках зусиль щодо виокремлення хмарного провайдера, всі контролери, специфічні для хмари, повинні бути виокремлені з kube-controller-manager. Усі поточні кластери, які використовують контролери хмари в kube-controller-manager, повинні перейти на запуск контролерів за допомогою специфічному для хмарного провайдера cloud-controller-manager.

Міграція лідера надає механізм, за допомогою якого високодоступні кластери можуть безпечно мігрувати у "хмароспецифічні" контролери між kube-controller-manager та cloud-controller-manager за допомогою загального замикання ресурсів між двома компонентами під час оновлення реплікованої панелі управління. Для панелі управління з одним вузлом або якщо недоступність менеджерів контролерів може бути терпимим під час оновлення, міграція лідера не потрібна, і цей посібник можна ігнорувати.

Міграція лідера може бути увімкнена, встановленням --enable-leader-migration у kube-controller-manager або cloud-controller-manager. Міграція лідера застосовується лише під час оновлення і може бути безпечно вимкнена або залишена увімкненою після завершення оновлення.

Цей посібник на крок за кроком показує вам процес оновлення вручну панелі управління з kube-controller-manager із вбудованим хмарним провайдером до запуску як kube-controller-manager, так і cloud-controller-manager. Якщо ви використовуєте інструмент для розгортання та управління кластером, будь ласка, зверніться до документації інструменту та хмарного провайдера для конкретних інструкцій щодо міграції.

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

Припускається, що панель управління працює на версії Kubernetes N і планується оновлення до версії N + 1. Хоча можливе мігрування в межах однієї версії, ідеально міграцію слід виконати як частину оновлення, щоб зміни конфігурації можна було узгодити з кожним випуском. Точні версії N та N + 1 залежать від кожного хмарного провайдера. Наприклад, якщо хмарний провайдер створює cloud-controller-manager для роботи з Kubernetes 1.24, то N може бути 1.23, а N + 1 може бути 1.24.

Вузли панелі управління повинні запускати kube-controller-manager з увімкненим вибором лідера, що є стандартною поведінкою. З версії N, вбудований хмарний провайдер має бути налаштований за допомогою прапорця --cloud-provider, а cloud-controller-manager ще не повинен бути розгорнутим.

Зовнішній хмарний провайдер повинен мати cloud-controller-manager зібраний з реалізацією міграції лідера. Якщо хмарний провайдер імпортує k8s.io/cloud-provider та k8s.io/controller-manager версії v0.21.0 або пізніше, міграція лідера буде доступною. Однак для версій до v0.22.0 міграція лідера є альфа-версією та потребує увімкнення ControllerManagerLeaderMigration в cloud-controller-manager.

Цей посібник передбачає, що kubelet кожного вузла панелі управління запускає kube-controller-manager та cloud-controller-manager як статичні контейнери, визначені їх маніфестами. Якщо компоненти працюють в іншому середовищі, будь ласка, відповідно скорегуйте дії.

Щодо авторизації цей посібник передбачає, що кластер використовує RBAC. Якщо інший режим авторизації надає дозволи на компоненти kube-controller-manager та cloud-controller-manager, будь ласка, надайте необхідний доступ таким чином, що відповідає режиму.

Надання доступу до Лізингу Міграції

Стандартні дозволи менеджера керування дозволяють доступ лише до їхнього основного Лізингу. Для того, щоб міграція працювала, потрібен доступ до іншого Лізингу.

Ви можете надати kube-controller-manager повний доступ до API лізингів, змінивши роль system::leader-locking-kube-controller-manager. Цей посібник передбачає, що назва лізингу для міграції — cloud-provider-extraction-migration.

kubectl patch -n kube-system role 'system::leader-locking-kube-controller-manager' -p '{"rules": [ {"apiGroups":[ "coordination.k8s.io"], "resources": ["leases"], "resourceNames": ["cloud-provider-extraction-migration"], "verbs": ["create", "list", "get", "update"] } ]}' --type=merge`

Зробіть те саме для ролі system::leader-locking-cloud-controller-manager.

kubectl patch -n kube-system role 'system::leader-locking-cloud-controller-manager' -p '{"rules": [ {"apiGroups":[ "coordination.k8s.io"], "resources": ["leases"], "resourceNames": ["cloud-provider-extraction-migration"], "verbs": ["create", "list", "get", "update"] } ]}' --type=merge`

Початкова конфігурація міграції лідера

Міграція лідера опціонально використовує файл конфігурації, що представляє стан призначення контролерів до менеджера. На цей момент, з вбудованим хмарним провайдером, kube-controller-manager запускає route, service, та cloud-node-lifecycle. Наведений нижче приклад конфігурації показує призначення.

Міграцію лідера можна увімкнути без конфігурації. Будь ласка, див. Станадартну конфігурацію для отримання деталей.

kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: kube-controller-manager
  - name: service
    component: kube-controller-manager
  - name: cloud-node-lifecycle
    component: kube-controller-manager

Альтернативно, оскільки контролери можуть працювати з менеджерами контролера, налаштування component на * для обох сторін робить файл конфігурації збалансованим між обома сторонами міграції.

# версія з підстановкою
kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: *
  - name: service
    component: *
  - name: cloud-node-lifecycle
    component: *

На кожному вузлі панелі управління збережіть вміст у /etc/leadermigration.conf, та оновіть маніфест kube-controller-manager, щоб файл був змонтований всередині контейнера за тим самим шляхом. Також, оновіть цей маніфест, щоб додати наступні аргументи:

  • --enable-leader-migration для увімкнення міграції лідера у менеджері керування
  • --leader-migration-config=/etc/leadermigration.conf для встановлення файлу конфігурації

Перезапустіть kube-controller-manager на кожному вузлі. Тепер, kube-controller-manager має увімкнену міграцію лідера і готовий до міграції.

Розгортання менеджера керування хмарою

У версії N + 1, бажаний стан призначення контролерів до менеджера може бути представлений новим файлом конфігурації, який показано нижче. Зверніть увагу, що поле component кожного controllerLeaders змінюється з kube-controller-manager на cloud-controller-manager. Альтернативно, використовуйте версію з підстановкою, згадану вище, яка має той самий ефект.

kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: cloud-controller-manager
  - name: service
    component: cloud-controller-manager
  - name: cloud-node-lifecycle
    component: cloud-controller-manager

Під час створення вузлів панелі управління версії N + 1, вміст повинен бути розгорнутим в /etc/leadermigration.conf. Маніфест cloud-controller-manager повинен бути оновлений для монтування файлу конфігурації так само як і kube-controller-manager версії N. Також, додайте --enable-leader-migration та --leader-migration-config=/etc/leadermigration.conf до аргументів cloud-controller-manager.

Створіть новий вузол панелі управління версії N + 1 з оновленим маніфестом cloud-controller-manager, та з прапорцем --cloud-provider, встановленим на external для kube-controller-manager. kube-controller-manager версії N + 1 НЕ МУСИТЬ мати увімкненої міграції лідера, оскільки, зовнішній хмарний провайдер вже не запускає мігровані контролери, і, отже, він не бере участі в міграції.

Будь ласка, зверніться до Адміністрування менеджера керування хмарою для отримання детальнішої інформації щодо розгортання cloud-controller-manager.

Оновлення панелі управління

Панель управління тепер містить вузли як версії N, так і N + 1. Вузли версії N запускають лише kube-controller-manager, а вузли версії N + 1 запускають як kube-controller-manager, так і cloud-controller-manager. Мігровані контролери, зазначені у конфігурації, працюють під менеджером управління хмарою версії N або cloud-controller-manager версії N + 1 залежно від того, який менеджер управління утримує лізинг міграції. Жоден контролер ніколи не працюватиме під обома менеджерами управління одночасно.

Поступово створіть новий вузол панелі управління версії N + 1 та вимкніть один вузол версії N до тих пір, поки панель управління не буде містити лише вузли версії N + 1. Якщо потрібно відкотитись з версії N + 1 на версію N, додайте вузли версії N з увімкненою міграцією лідера для kube-controller-manager назад до панелі управління, замінюючи один вузол версії N + 1 кожен раз, поки не залишаться лише вузли версії N.

(Необовʼязково) Вимкнення міграції лідера

Тепер, коли панель управління була оновлена для запуску як kube-controller-manager, так і cloud-controller-manager версії N + 1, міграція лідера завершила свою роботу і може бути безпечно вимкнена для збереження ресурсу лізингу. У майбутньому можна безпечно повторно увімкнути міграцію лідера для відкату.

Поступово у менеджері оновіть маніфест cloud-controller-manager, щоб скасувати встановлення як --enable-leader-migration, так і --leader-migration-config=, також видаліть підключення /etc/leadermigration.conf, а потім видаліть /etc/leadermigration.conf. Щоб повторно увімкнути міграцію лідера, створіть знову файл конфігурації та додайте його монтування та прапорці, які увімкнуть міграцію лідера назад до cloud-controller-manager.

Стандартна конфігурація

Починаючи з Kubernetes 1.22, Міграція лідера надає стандартну конфігурацію, яка підходить для стандартного призначення контролерів до менеджера. Стандартну конфігурацію можна увімкнути, встановивши --enable-leader-migration, але без --leader-migration-config=.

Для kube-controller-manager та cloud-controller-manager, якщо немає жодних прапорців, що увімкнуть будь-якого вбудованого хмарного провайдера або змінять володільця контролерів, можна використовувати стандартну конфігурацію, щоб уникнути ручного створення файлу конфігурації.

Спеціальний випадок: міграція контролера Node IPAM

Якщо ваш хмарний провайдер надає реалізацію контролера Node IPAM, вам слід перейти до реалізації в cloud-controller-manager. Вимкніть контролер Node IPAM в kube-controller-manager версії N + 1, додавши --controllers=*,-nodeipam до його прапорців. Потім додайте nodeipam до списку мігрованих контролерів.

# версія з підстановкою, з nodeipam
kind: LeaderMigrationConfiguration
apiVersion: controllermanager.config.k8s.io/v1
leaderName: cloud-provider-extraction-migration
controllerLeaders:
  - name: route
    component: *
  - name: service
    component: *
  - name: cloud-node-lifecycle
    component: *
  - name: nodeipam
    component: *

Що далі

2.29 - Посібник з роботи з просторами імен

Kubernetes namespaces допомагають різним проєктам, командам або клієнтам спільно використовувати кластер Kubernetes.

Вони це роблять, надаючи наступне:

  1. Область для Імен.
  2. Механізм для прикріплення авторизації та політики до підрозділу кластера.

Використання кількох просторів імен є необовʼязковим.

У цьому прикладі показано, як використовувати простори імен Kubernetes для розділення вашого кластера.

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

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

Для перевірки версії введіть kubectl version.

Передумови

У цьому прикладі передбачається наступне:

  1. У вас є кластер Kubernetes.
  2. Ви маєте базове розуміння що таке Pod, Service та Deployment в Kubernetes.

Стандартний простір імен

Типово кластер Kubernetes створює стандартний простір імен default під час створення кластера для утримання стандартного набору Podʼів, Serviceʼів та Deploymentʼів, що використовуються кластером.

Якщо у вас є свіжий кластер, ви можете перевірити доступні простори імен, виконавши наступне:

kubectl get namespaces
NAME      STATUS    AGE
default   Active    13m

Створення нових просторів імен

Для цієї вправи ми створимо два додаткові простори імен Kubernetes для зберігання нашого контенту.

Уявімо собі сценарій, де організація використовує спільний кластер Kubernetes для розробки та операційної експлуатації.

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

Операційна команда хоче мати простір в кластері, де вони можуть дотримуватися строгих процедур щодо того, хто може або не може маніпулювати набором Podʼів, Serviceʼів та Deploymentʼів, які підтримують операційну роботу.

Одним із шаблонів, який ця організація може використовувати, є розбиття кластера Kubernetes на два простори імен: development та production.

Створімо два нових простори імен для зберігання нашої роботи.

Використовуйте файл namespace-dev.yaml, який описує простір імен development:

apiVersion: v1
kind: Namespace
metadata:
  name: development
  labels:
    name: development

Створіть простір імен development за допомогою kubectl.

kubectl create -f https://k8s.io/examples/admin/namespace-dev.yaml

Збережіть наступний вміст у файл namespace-prod.yaml, який описує простір імен production:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    name: production

А потім створімо простір імен production за допомогою kubectl.

kubectl create -f https://k8s.io/examples/admin/namespace-prod.yaml

Щоб бути впевненими, що все правильно, перелічімо всі простори імен у нашому кластері.

kubectl get namespaces --show-labels
NAME          STATUS    AGE       LABELS
default       Active    32m       <none>
development   Active    29s       name=development
production    Active    23s       name=production

Створення Podʼів у кожному просторі імен

Простір імен Kubernetes надає область для Podʼів, Service та Deployment у кластері.

Користувачі, які взаємодіють з одним простором імен, не бачать вмісту в іншому просторі імен.

Щоб продемонструвати це, запустімо простий Deployment та Podʼи у просторі імен development.

Спочатку перевіримо поточний контекст:

kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: https://130.211.122.180
  name: lithe-cocoa-92103_kubernetes
contexts:
- context:
    cluster: lithe-cocoa-92103_kubernetes
    user: lithe-cocoa-92103_kubernetes
  name: lithe-cocoa-92103_kubernetes
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
    token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
  user:
    password: h5M0FtUUIflBSdI7
    username: admin
kubectl config current-context
lithe-cocoa-92103_kubernetes

Наступний крок — визначення контексту для клієнта kubectl для роботи в кожному просторі імен. Значення полів "cluster" та "user" копіюються з поточного контексту.

kubectl config set-context dev --namespace=development \
  --cluster=lithe-cocoa-92103_kubernetes \
  --user=lithe-cocoa-92103_kubernetes

kubectl config set-context prod --namespace=production \
  --cluster=lithe-cocoa-92103_kubernetes \
  --user=lithe-cocoa-92103_kubernetes

Типово, ці команди додають два контексти, які зберігаються у файлі .kube/config. Тепер ви можете переглянути контексти та перемикатися між двома новими контекстами запитів, залежно від того, з яким простором імен ви хочете працювати.

Щоб переглянути нові контексти:

kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: REDACTED
    server: https://130.211.122.180
  name: lithe-cocoa-92103_kubernetes
contexts:
- context:
    cluster: lithe-cocoa-92103_kubernetes
    user: lithe-cocoa-92103_kubernetes
  name: lithe-cocoa-92103_kubernetes
- context:
    cluster: lithe-cocoa-92103_kubernetes
    namespace: development
    user: lithe-cocoa-92103_kubernetes
  name: dev
- context:
    cluster: lithe-cocoa-92103_kubernetes
    namespace: production
    user: lithe-cocoa-92103_kubernetes
  name: prod
current-context: lithe-cocoa-92103_kubernetes
kind: Config
preferences: {}
users:
- name: lithe-cocoa-92103_kubernetes
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED
    token: 65rZW78y8HbwXXtSXuUw9DbP4FLjHi4b
- name: lithe-cocoa-92103_kubernetes-basic-auth
  user:
    password: h5M0FtUUIflBSdI7
    username: admin

Перемкнімся, щоб працювати у просторі імен development.

kubectl config use-context dev

Ви можете перевірити поточний контекст за допомогою наступного:

kubectl config current-context
dev

На цьому етапі всі запити, які ми робимо до кластера Kubernetes з командного рядка, зосереджені на просторі імен development.

Створімо деякий вміст.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: snowflake
  name: snowflake
spec:
  replicas: 2
  selector:
    matchLabels:
      app: snowflake
  template:
    metadata:
      labels:
        app: snowflake
    spec:
      containers:
      - image: registry.k8s.io/serve_hostname
        imagePullPolicy: Always
        name: snowflake

Застосуйте маніфест для створення Deployment

kubectl apply -f https://k8s.io/examples/admin/snowflake-deployment.yaml

Ми створили розгортання, кількість реплік якого становить 2, що запускає Pod під назвою snowflake з базовим контейнером, який обслуговує імʼя хосту.

kubectl get deployment
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
snowflake    2/2     2            2           2m
kubectl get pods -l app=snowflake
NAME                         READY     STATUS    RESTARTS   AGE
snowflake-3968820950-9dgr8   1/1       Running   0          2m
snowflake-3968820950-vgc4n   1/1       Running   0          2m

І це чудово, розробники можуть робити все, що вони хочуть, і їм не потрібно хвилюватися про вплив на вміст у просторі імен production.

Тепер перейдемо до простору імен production та покажемо, як ресурси в одному просторі імен приховані від іншого.

kubectl config use-context prod

Простір імен production повинен бути порожнім, і наступні команди не повернуть нічого.

kubectl get deployment
kubectl get pods

Операційна діяльність вимагає догляду за худобою, тому створімо кілька Podʼів для худоби.

kubectl create deployment cattle --image=registry.k8s.io/serve_hostname --replicas=5

kubectl get deployment
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
cattle       5/5     5            5           10s
kubectl get pods -l app=cattle
NAME                      READY     STATUS    RESTARTS   AGE
cattle-2263376956-41xy6   1/1       Running   0          34s
cattle-2263376956-kw466   1/1       Running   0          34s
cattle-2263376956-n4v97   1/1       Running   0          34s
cattle-2263376956-p5p3i   1/1       Running   0          34s
cattle-2263376956-sxpth   1/1       Running   0          34s

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

З розвитком підтримки політики в Kubernetes ми розширимо цей сценарій, щоб показати, як можна надавати різні правила авторизації для кожного простору імен.

2.30 - Управління кластерами etcd для Kubernetes

etcd — це сумісне та високодоступне сховище ключ-значення, яке використовується як сховище Kubernetes для резервування всіх даних кластера.

Якщо ваш кластер Kubernetes використовує etcd як сховище для резервування, переконайтеся, що у вас є план резервного копіювання даних.

Ви можете знайти докладну інформацію про etcd в офіційній документації.

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

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

Основні деталі включають:

  • Мінімальні рекомендовані версії etcd для використання в операційній діяльності: 3.4.22+ і 3.5.6+.

  • etcd є розподіленою системою з визначенням лідера. Забезпечте, щоб лідер періодично надсилав своєчасні сигнали всім підлеглим для підтримки стабільності кластера.

  • Ви повинні запускати etcd як кластер з непарною кількістю членів.

  • Намагайтеся уникати виснаження ресурсів.

    Продуктивність і стабільність кластера чутлива до наявності ресурсів мережі та введення/виведення даних на диск. Будь-яка нестача ресурсів може призвести до вичерпання тайм-ауту пульсу, що призводить до нестабільності кластера. Нестабільність etcd означає, що лідер не обраний. У таких обставинах кластер не може вносити зміни до свого поточного стану, що означає, що нові Podʼи не можуть бути заплановані.

Вимоги до ресурсів для etcd

Експлуатація etcd з обмеженими ресурсами підходить тільки для тестових цілей. Для розгортання в операційній діяльності потрібна розширена конфігурація обладнання. Перед розгортанням etcd в операційній діяльності дивіться довідник щодо вимог до ресурсів.

Підтримання стабільності кластерів etcd є критичним для стабільності кластерів Kubernetes. Тому запускайте кластери etcd на виділених машинах або в ізольованих середовищах для гарантованих вимог до ресурсів.

Інструменти

Залежно від конкретного завдання, яке ви виконуєте, вам знадобиться інструмент etcdctl або etcdutl (можливо, обидва).

Розуміння etcdctl і etcdutl

etcdctl і etcdutl — це інструменти командного рядка для взаємодії з кластерами etcd, але вони виконують різні завдання:

  • etcdctl — це основний клієнт командного рядка для взаємодії з etcd через мережу. Використовується для повсякденних операцій, таких як керування ключами та значеннями, адміністрування кластера, перевірка справності та багато іншого.

  • etcdutl — це утиліта адміністратора, призначена для роботи безпосередньо з файлами даних etcd, включаючи міграцію даних між версіями etcd, дефрагментацію бази даних, відновлення знімків і перевірку цілісності даних. Для мережевих операцій слід використовувати etcdctl.

Для отримання додаткової інформації про etcdutl, ви можете звернутися до документації з відновлення etcd.

Запуск кластерів etcd

У цьому розділі розглядається запуск одно- та багатовузлового кластерів etcd.

Це керівництво передбачає, що etcd встановлено.

Одновузловий кластер etcd

Використовуйте одновузловий кластер etcd лише для тестування.

  1. Виконайте наступне:

    etcd --listen-client-urls=http://$PRIVATE_IP:2379 \
       --advertise-client-urls=http://$PRIVATE_IP:2379
    
  2. Запустіть сервер API Kubernetes з прапорцем --etcd-servers=$PRIVATE_IP:2379.

    Переконайтеся, що PRIVATE_IP встановлено на ваш IP-адрес клієнта etcd.

Багатовузловий кластер etcd

Для забезпечення надійності та високої доступності запускайте etcd як багатовузловий кластер для операційної діяльності та періодично робіть резервні копії. В операційній діяльності рекомендується використовувати кластер з пʼятьма членами. Для отримання додаткової інформації див. ЧаПи.

Оскільки ви використовуєте Kubernetes, у вас є можливість запускати etcd як контейнер всередині одного або декількох Podʼів. Інструмент kubeadm типово налаштовує etcd як статичні Podʼи, або ви можете розгорнути окремий кластер і вказати kubeadm використовувати цей кластер etcd як сховище для контрольної площини.

Ви можете налаштувати кластер etcd або за допомогою статичної інформації про учасників, або за допомогою динамічного виявлення. Для отримання додаткової інформації про кластеризацію дивіться документацію з кластеризації etcd.

Наприклад, розглянемо кластер etcd з пʼятьох членів, що працює з наступними URL-адресами клієнта: http://$IP1:2379, http://$IP2:2379, http://$IP3:2379, http://$IP4:2379 та http://$IP5:2379. Щоб запустити сервер API Kubernetes:

  1. Виконайте наступне:

    etcd --listen-client-urls=http://$IP1:2379,http://$IP2:2379,http://$IP3:2379,http://$IP4:2379,http://$IP5:2379 --advertise-client-urls=http://$IP1:2379,http://$IP2:2379,http://$IP3:2379,http://$IP4:2379,http://$IP5:2379
    
  2. Запустіть сервери API Kubernetes з прапорцем --etcd-servers=$IP1:2379,$IP2:2379,$IP3:2379,$IP4:2379,$IP5:2379.

    Переконайтеся, що змінні IP<n> встановлені на ваші IP-адреси клієнтів.

Багатовузловий кластер etcd з балансувальником навантаження

Для запуску кластера etcd з балансувальником навантаження:

  1. Налаштуйте кластер etcd.
  2. Налаштуйте балансувальник навантаження перед кластером etcd. Наприклад, адреса балансувальника навантаження може бути $LB.
  3. Запустіть сервери API Kubernetes з прапорцем --etcd-servers=$LB:2379.

Захист кластерів etcd

Доступ до etcd еквівалентний правам кореневого користувача в кластері, тому ідеально, щоб доступ до нього мав лише сервер API. З урахуванням чутливості даних рекомендується надавати дозвіл лише тим вузлам, які потребують доступу до кластерів etcd.

Для захисту etcd налаштуйте правила брандмауера або використовуйте засоби безпеки, надані etcd. Функції безпеки etcd залежать від інфраструктури відкритого ключа x509 (PKI). Для початку налаштуйте безпечні канали звʼязку, згенерувавши пару ключа та сертифікату. Наприклад, використовуйте пари ключів peer.key та peer.cert для захисту звʼязку між членами etcd та client.key та client.cert для захисту звʼязку між etcd та його клієнтами. Див. приклади скриптів, надані проєктом etcd, для генерації пар ключів та файлів ЦС для автентифікації клієнтів.

Захист комунікації

Для налаштування etcd з безпечною взаємодією між членами вкажіть прапорці --peer-key-file=peer.key та --peer-cert-file=peer.cert, та використовуйте протокол HTTPS у схемі URL.

Аналогічно, для налаштування etcd з безпечною взаємодією клієнтів вкажіть прапорці --key-file=k8sclient.key та --cert-file=k8sclient.cert, та використовуйте протокол HTTPS у схемі URL. Ось приклад команди клієнта, яка використовує безпечну комунікацію:

ETCDCTL_API=3 etcdctl --endpoints 10.2.0.9:2379 \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  member list

Обмеження доступу до кластерів etcd

Після налаштування безпечної комунікації обмежте доступ до кластера etcd лише для серверів API Kubernetes, використовуючи автентифікацію TLS.

Наприклад, розгляньте пари ключів k8sclient.key та k8sclient.cert, яким довіряє ЦС etcd.ca. Коли etcd налаштовано з параметром --client-cert-auth разом з TLS, він перевіряє сертифікати від клієнтів, використовуючи системні ЦС або ЦС, передані за допомогою прапорця --trusted-ca-file. Вказівка прапорців --client-cert-auth=true та --trusted-ca-file=etcd.ca обмежить доступ до клієнтів з сертифікатом k8sclient.cert.

Після коректного налаштування etcd до нього можуть отримувати доступ лише клієнти з дійсними сертифікатами. Щоб дати серверам API Kubernetes доступ, налаштуйте їх з прапорцями --etcd-certfile=k8sclient.cert, --etcd-keyfile=k8sclient.key та --etcd-cafile=ca.cert.

Заміна несправного члена etcd

Кластер etcd досягає високої доступності, толеруючи невеликі відмови членів. Однак, для покращення загального стану кластера, замінюйте несправних членів негайно. Коли відмовляють декілька членів, замінюйте їх по одному. Заміна несправного члена включає два кроки: видалення несправного члена та додавання нового члена.

Хоча etcd зберігає унікальні ідентифікатори членів всередині, рекомендується використовувати унікальне імʼя для кожного члена, щоб уникнути фактора людської помилки. Наприклад, розгляньте кластер etcd з трьох членів. Нехай URL буде таким: member1=http://10.0.0.1, member2=http://10.0.0.2, і member3=http://10.0.0.3. Коли відмовляє member1, замініть його на member4=http://10.0.0.4.

  1. Отримайте ідентифікатор несправного member1:

    etcdctl --endpoints=http://10.0.0.2,http://10.0.0.3 member list
    

    Показується наступне повідомлення:

    8211f1d0f64f3269, started, member1, http://10.0.0.1:2380, http://10.0.0.1:2379
    91bc3c398fb3c146, started, member2, http://10.0.0.2:2380, http://10.0.0.2:2379
    fd422379fda50e48, started, member3, http://10.0.0.3:2380, http://10.0.0.3:2379
    
  2. Виконайте одне з наступного:

    1. Якщо кожен сервер API Kubernetes налаштований на спілкування з усіма членами etcd, видаліть несправного члена з прапорця --etcd-servers, а потім перезапустіть кожен сервер API Kubernetes.
    2. Якщо кожен сервер API Kubernetes спілкується з одним членом etcd, зупиніть сервер API Kubernetes, який спілкується з несправним etcd.
  3. Зупиніть сервер etcd на несправному вузлі. Можливо, інші клієнти окрім сервера API Kubernetes створюють трафік до etcd, і бажано зупинити весь трафік, щоб запобігти записам до теки з даними.

  4. Видаліть несправного члена:

    etcdctl member remove 8211f1d0f64f3269
    

    Показується наступне повідомлення:

    Removed member 8211f1d0f64f3269 from cluster
    
  5. Додайте нового члена:

    etcdctl member add member4 --peer-urls=http://10.0.0.4:2380
    

    Показується наступне повідомлення:

    Member 2be1eb8f84b7f63e added to cluster ef37ad9dc622a7c4
    
  6. Запустіть новододаного члена на машині з IP 10.0.0.4:

    export ETCD_NAME="member4"
    export ETCD_INITIAL_CLUSTER="member2=http://10.0.0.2:2380,member3=http://10.0.0.3:2380,member4=http://10.0.0.4:2380"
    export ETCD_INITIAL_CLUSTER_STATE=existing
    etcd [flags]
    
  7. Виконайте одне з наступного:

    1. Якщо кожен сервер API Kubernetes налаштований на спілкування з усіма членами etcd, додайте новододаного члена до прапорця --etcd-servers, а потім перезапустіть кожен сервер API Kubernetes.
    2. Якщо кожен сервер API Kubernetes спілкується з одним членом etcd, запустіть сервер API Kubernetes, який був зупинений на кроці 2. Потім налаштуйте клієнти сервера API Kubernetes знову маршрутизувати запити до сервера API Kubernetes, який був зупинений. Це часто можна зробити, налаштувавши балансувальник навантаження.

За додатковою інформацією про налаштування кластера etcd див. документацію з переналаштування etcd.

Резервне копіювання кластера etcd

Усі обʼєкти Kubernetes зберігаються в etcd. Періодичне резервне копіювання даних кластера etcd важливо для відновлення кластерів Kubernetes у випадку катастрофи, такої як втрата всіх вузлів панелі управління. Файл знімка містить весь стан Kubernetes та критичну інформацію. Для збереження конфіденційних даних Kubernetes в безпеці зашифруйте файли знімків.

Резервне копіювання кластера etcd можна виконати двома способами: вбудованим засобами знімків etcd та знімком тому.

Вбудовані засоби знімків

etcd підтримує вбудовані засоби знімків. Знімок можна створити з активного члена за допомогою команди etcdctl snapshot save або скопіювавши файл member/snap/db з теки даних etcd, яка в цей момент не використовується процесом etcd. Створення знімка не вплине на продуктивність члена.

Нижче наведено приклад створення знімка простору ключів, який обслуговується за адресою $ENDPOINT, у файл snapshot.db:

ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db

Перевірте знімок:

Приклад нижче показує, як використовувати etcdutl для перевірки знімка:

etcdutl --write-out=table snapshot status snapshot.db

Це повинно згенерувати результат, подібний до наведеного нижче прикладу:

+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| fe01cf57 |       10 |          7 | 2.1 MB     |
+----------+----------+------------+------------+

Приклад нижче показує, як використовувати etcdctl для перевірки знімка:

export ETCDCTL_API=3
etcdctl --write-out=table snapshot status snapshot.db

Це повинно згенерувати результат, подібний до наведеного нижче прикладу:

Застаріло: Використовуйте `etcdutl snapshot status` натомість.

+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| fe01cf57 |       10 |          7 | 2.1 MB     |
+----------+----------+------------+------------+

Знімок тому {volume-snapshot}

Якщо etcd працює за томом сховища, який підтримує резервне копіювання, наприклад, Amazon Elastic Block Store, зробіть резервну копію даних etcd, створивши знімок тому сховища.

Знімок за допомогою параметрів etcdctl

Ми також можемо створити знімок, використовуючи різноманітні параметри, надані etcdctl. Наприклад:

ETCDCTL_API=3 etcdctl -h

покаже різні параметри, доступні з etcdctl. Наприклад, ви можете створити знімок, вказавши точку доступу, сертифікати та ключ, як показано нижче:

ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=<trusted-ca-file> --cert=<cert-file> --key=<key-file> \
  snapshot save <backup-file-location>

де trusted-ca-file, cert-file та key-file можна отримати з опису модуля etcd.

Масштабування кластерів etcd

Масштабування кластерів etcd підвищує доступність шляхом зниження продуктивності. Масштабування не збільшує продуктивність або можливості кластера. Загальне правило — не масштабуйте кластери etcd. Не налаштовуйте жодних автоматичних груп масштабування для кластерів etcd. Настійно рекомендується завжди запускати статичний кластер etcd з 5-ти членів для операційних кластерів Kubernetes будь-якого офіційно підтримуваного масштабу.

Доречне масштабування — це оновлення кластера з трьох до пʼяти членів, коли потрібно більше надійності. Див. документацію з переконфігурації etcd для інформації про те, як додавати учасників у наявний кластер.

Відновлення кластера etcd

etcd підтримує відновлення зі знімків, які були створені з процесу etcd версії major.minor. Відновлення версії з іншої версії патча etcd також підтримується. Операція відновлення використовується для відновлення даних несправного кластера.

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

Під час відновлення кластера використовуючи etcdutl використовуйте опцію --data-dir, щоб вказати, у яку теку слід відновити кластер:

etcdutl --data-dir <data-dir-location> snapshot restore snapshot.db

де <data-dir-location> — це тека, яка буде створена під час процесу відновлення.

У наведеному нижче прикладі показано використання інструмента etcdctl для операції відновлення:

export ETCDCTL_API=3
etcdctl --data-dir <data-dir-location> snapshot restore snapshot.db

Якщо <data-dir-location> є тою самою текою, що й раніше, видаліть її та зупиніть процес etcd перед відновленням кластера. В іншому випадку, змініть конфігурацію etcd і перезапустіть процес etcd після відновлення, щоб він використовував нову теку даних: спочатку змініть /etc/kubernetes/manifests/etcd.yaml у volumes.hostPath.path для name: etcd-data на <data-dir-location>, потім виконайте kubectl -n kube-system delete pod <name-of-etcd-pod> або ystemctl restart kubelet.service (або обидві команди).

Для отримання додаткової інформації та прикладів відновлення кластера з файлу знімка, див. документацію з відновлення після збою etcd.

Якщо доступні URL-адреси відновленого кластера відрізняються від попереднього кластера, сервер API Kubernetes повинен бути відповідно переконфігурований. У цьому випадку перезапустіть сервери API Kubernetes з прапорцем --etcd-servers=$NEW_ETCD_CLUSTER замість прапорця --etcd-servers=$OLD_ETCD_CLUSTER. Замініть $NEW_ETCD_CLUSTER та $OLD_ETCD_CLUSTER на відповідні IP-адреси. Якщо перед кластером etcd використовується балансувальник навантаження, можливо, потрібно оновити балансувальник навантаження.

Якщо більшість членів etcd є остаточно несправними, кластер etcd вважається несправним. У цьому сценарії Kubernetes не може вносити зміни у свій поточний стан. Хоча заплановані Podʼи можуть продовжувати працювати, нові Podʼи не можуть бути заплановані. У таких випадках відновіть кластер etcd та, можливо, переконфігуруйте сервери API Kubernetes, щоб усунути проблему.

Оновлення кластерів etcd

Для отримання додаткових відомостей щодо оновлення etcd дивіться документацію з оновлення etcd.

Обслуговування кластерів etcd

Для отримання додаткових відомостей щодо обслуговування etcd дивіться документацію з обслуговування etcd.

Дефрагментація кластера

Дефрагментація є дорогою операцією, тому її слід виконувати якомога рідше. З іншого боку, також необхідно переконатися, що жоден з учасників etcd не перевищить квоту зберігання. Проєкт Kubernetes рекомендує, що при виконанні дефрагментації ви використовували інструмент, такий як etcd-defrag.

Ви також можете запускати інструмент дефрагментації як Kubernetes CronJob, щоб переконатися, що дефрагментація відбувається регулярно. Дивіться etcd-defrag-cronjob.yaml для отримання деталей.

2.31 - Резервування обчислювальних ресурсів для системних служб

Вузли Kubernetes можуть бути заплановані на Capacity. Podʼи можуть використовувати типово всю доступну місткість на вузлі. Це проблема, оскільки на вузлах зазвичай працює досить багато системних служб, які забезпечують операційну систему та сам Kubernetes. Якщо не виділити ресурси для цих системних служб, Podʼи та системні служби конкурують за ресурси, що призводить до проблем з вичерпання ресурсів на вузлі.

kubelet надає можливість під назвою 'Node Allocatable', яка допомагає резервувати обчислювальні ресурси для системних служб. Kubernetes рекомендує адміністраторам кластера налаштовувати 'Node Allocatable' на основі щільності робочого навантаження на кожному вузлі.

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

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

Ви можете налаштувати kubelet за допомогою параметрів конфігурації використовуючи конфігураційний файл kubelet.

Node Allocatable

ємність вузла

'Виділення' (Allocatable) на вузлі Kubernetes визначається як обсяг обчислювальних ресурсів, які доступні для Podʼів. Планувальник не надає перевищення обсягу 'Виділення'. Зараз підтримуються 'CPU', 'memory' та 'ephemeral-storage'.

Node Allocatable експонується як частина обʼєкта v1.Node в API та як частина kubectl describe node в CLI.

Ресурси можуть бути зарезервовані для двох категорій системних служб в kubelet.

Увімкнення QoS та cgroups на рівні Pod

Для належного застосування обмежень на вузлі виділення ресурсів вузлові вам потрібно увімкнути нову ієрархію cgroup за допомогою параметру налаштувань cgroupsPerQOS. Цей параметр є типово увімкненим. Коли він увімкнений, kubelet буде розташовувати всі Podʼи кінцевих користувачів в ієрархії cgroup, керованій kubelet.

Налаштування драйвера cgroup

kubelet підтримує маніпулювання ієрархією cgroup на хості за допомогою драйвера cgroup. Драйвер налаштовується за допомогою параметра cgroupDriver.

Підтримувані значення наступні:

  • cgroupfs — це типовий драйвер, який виконує пряме маніпулювання файловою системою cgroup на хості для управління пісочницями cgroup.
  • systemd — це альтернативний драйвер, який управляє пісочницями cgroup за допомогою тимчасових сегментів для ресурсів, які підтримуються цією системою ініціалізації.

Залежно від конфігурації відповідного контейнерного середовища, оператори можуть вибрати певний драйвер cgroup, щоб забезпечити належну роботу системи. Наприклад, якщо оператори використовують драйвер cgroup systemd, наданий контейнерним середовищем containerd, kubelet повинен бути налаштований на використання драйвера cgroup systemd.

Kube Reserved

  • KubeletConfiguration Setting: kubeReserved: {}. Example value {cpu: 100m, memory: 100Mi, ephemeral-storage: 1Gi, pid=1000}
  • KubeletConfiguration Setting: kubeReservedCgroup: ""

kubeReserved призначено для захоплення резервування ресурсів для системних демонів Kubernetes, таких як kubelet, container runtime тощо. Він не призначений для резервування ресурсів для системних служб, які запускаються як Podʼи. kubeReserved зазвичай є функцією pod density на вузлах.

Крім cpu, memory та ephemeral-storage, можна вказати pid, щоб зарезервувати вказану кількість ідентифікаторів процесів для системних демонів Kubernetes.

Для необовʼязкового застосування kubeReserved до системних демонів Kubernetes вкажіть батьківську контрольну групу для демонів kube як значення параметра kubeReservedCgroup та додайте kube-reserved до enforceNodeAllocatable.

Рекомендується розміщувати системні демони Kubernetes під верхньою контрольною групою (runtime.slice на машинах з systemd, наприклад). Кожен системний демон повинен ідеально працювати у власній дочірній контрольній групі. Докладнішу інформацію про рекомендовану ієрархію контрольних груп дивіться у пропозиції дизайну.

Зверніть увагу, що Kubelet не створює kubeReservedCgroup, якщо він не існує. Kubelet не запуститься, якщо вказано недійсну контрольну групу. З драйвером cgroup systemd вам слід дотримуватися певного шаблону для імені контрольної групи, яку ви визначаєте: імʼя повинно бути значенням, яке ви встановлюєте для kubeReservedCgroupp, з додаванням .slice.

System Reserved

  • KubeletConfiguration Setting: systemReserved: {}. Example value {cpu: 100m, memory: 100Mi, ephemeral-storage: 1Gi, pid=1000}
  • KubeletConfiguration Setting: systemReservedCgroup: ""

systemReserved призначено для захоплення резервування ресурсів для системних служб операційної системи, таких як sshd, udev і т. д. systemReserved повинно резервувати memory для kernel, оскільки памʼять kernel наразі не враховується для Podʼів у Kubernetes. Рекомендується також резервувати ресурси для сеансів входу користувача (user.slice у світі systemd).

Крім cpu, memory та ephemeral-storage, можна вказати pid, щоб зарезервувати вказану кількість ідентифікаторів процесів для системних служб операційної системи.

Для необовʼязкового застосування systemReserved до системних служб вкажіть батьківську контрольну групу для системних служб операційної системи як значення параметра systemReservedCgroup та додайте system-reserved до enforceNodeAllocatable.

Рекомендується розміщувати системні служби операційної системи під верхньою контрольною групою (system.slice на машинах з systemd, наприклад).

Зверніть увагу, що kubelet не створює systemReservedCgroup, якщо він не існує. kubelet відмовить у запуску, якщо вказано недійсну контрольну групу. З драйвером cgroup systemd вам слід дотримуватися певного шаблону для імені контрольної групи, яку ви визначаєте: імʼя повинно бути значенням, яке ви встановлюєте для systemReservedCgroup, з додаванням .slice.

Явно зарезервований список CPU

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.17 [stable]

Параметр KubeletConfiguration Setting: reservedSystemCPUs:. Наприклад: 0-3

reservedSystemCPUs призначено для визначення явного набору CPU для системних служб операційної системи та системних служб Kubernetes. reservedSystemCPUs призначений для систем, які не мають наміру визначати окремі верхні рівні контрольні групи для системних служб операційної системи та системних служб Kubernetes з урахуванням ресурсу cpuset. Якщо Kubelet не має kubeReservedCgroup та systemReservedCgroup, явний набір cpuset, наданий reservedSystemCPUs, переважатиме над CPU, визначеними параметрами kubeReservedCgroup та systemReservedCgroup.

Ця опція спеціально розроблена для випадків використання в телекомунікаціях/NFV, де неконтрольовані переривання/таймери можуть впливати на продуктивність робочого навантаження. Ви можете використовувати цю опцію для визначення явного набору cpuset для системних/кластерних служб та переривань/таймерів, щоб решта процесорів у системі могли використовуватися виключно для робочих навантажень, з меншим впливом неконтрольованих переривань/таймерів. Щоб перенести системні служби, системні служби Kubernetes та переривання/таймери до явного набору cpuset, визначеного цією опцією, слід використовувати інші механізми поза Kubernetes. Наприклад: у CentOS це можна зробити за допомогою інструменту tuned.

Пороги виселення

Параметр KubeletConfiguration: evictionHard: {memory.available: "100Mi", nodefs.available: "10%", nodefs.inodesFree: "5%", imagefs.available: "15%"}. Наприклад: {memory.available: "<500Mi"}

Нестача памʼяті на рівні вузла призводить до System OOMs, що впливає на весь вузол та всі Podʼи, що працюють на ньому. Вузли можуть тимчасово вийти з ладу, поки памʼять не буде відновлена. Щоб уникнути (або зменшити ймовірність) System OOMs, kubelet надає управління ресурсами. Виселення підтримується тільки для memory та ephemeral-storage. Резервуючи певний обсяг памʼяті за допомогою параметра evictionHard, kubelet намагається виселити Podʼи, коли доступність памʼяті на вузлі впаде нижче зарезервованого значення. Гіпотетично, якщо системні служби не існують на вузлі, Podʼи не можуть використовувати більше, ніж capacity - eviction-hard. З цієї причини ресурси, зарезервовані для виселень, не доступні для Podʼів.

Застосування Node Allocatable

KubeletConfiguration setting: enforceNodeAllocatable: [pods]. Наприклад: [pods,system-reserved,kube-reserved]

Планувальник розглядає 'Allocatable' як доступну capacity для Podʼів.

kubelet типово застосовує 'Allocatable' на всіх Podʼах. Застосування виконується шляхом видалення Podʼів, коли загальне використання у всіх Podʼах перевищує 'Allocatable'. Додаткові відомості про політику виселення можна знайти на сторінці Виселення внаслідок тиску на вузол. Це застосування контролюється, вказуючи значення pods для параметра enforceNodeAllocatable.

Необовʼязково, kubelet можна змусити застосовувати kubeReserved та systemReserved, вказавши значення kube-reserved та system-reserved у в одному і тому ж параметрі. Зверніть увагу, що для застосування kubeReserved або systemReserved, потрібно вказати kubeReservedCgroup або ystemReservedCgroup відповідно.

Загальні настанови

Очікується, що системні служби будуть оброблятися аналогічно гарантованим Podʼам. Системні служби можуть розширюватися в межах своїх обмежувальних контрольних груп, і цією поведінкою потрібно керувати як частиною розгортань Kubernetes. Наприклад, kubelet повинен мати свою власну контрольну групу і ділити ресурси kubeReserved з контейнерним середовищем. Однак Kubelet не може розширюватися і використовувати всі доступні ресурси вузла, якщо застосовується kubeReserved.

Будьте особливо обережні при застосуванні резервування systemReserved, оскільки це може призвести до нестачі ресурсів CPU для критичних системних служб, припинення роботи через нестачу памʼяті (OOM) або неможливості форка на вузлі. Рекомендація полягає в застосуванні systemReserved лише у випадку, якщо користувач детально проаналізував свої вузли, щоб надати точні оцінки та має впевненість у своїй здатності відновитися, якщо будь-який процес у цій групі буде примусово завершений через брак памʼяті.

  • Спочатку застосовуйте 'Allocatable' на Pod.
  • Як тільки буде встановлено достатньо моніторингу та попереджень для відстеження системних служб kube, спробуйте застосувати kubeReserved на основі використання евристик.
  • Якщо це абсолютно необхідно, з часом застосуйте systemReserved.

Вимоги до ресурсів системних служб kube можуть зростати з часом з введенням все більшої кількості функцій. З часом проєкт Kubernetes буде намагатися знизити використання системних служб вузла, але зараз це не пріоритет. Так що очікуйте зниження доступної місткості Allocatable у майбутніх версіях.

Приклад сценарію

Ось приклад для ілюстрації обчислення виділення ресурсів вузла:

  • Вузол має 32Гб памʼяті, 16 ЦП і 100Гб сховища
  • kubeReserved встановлено у {cpu: 1000m, memory: 2Gi, ephemeral-storage: 1Gi}
  • systemReserved встановлено у {cpu: 500m, memory: 1Gi, ephemeral-storage: 1Gi}
  • evictionHard встановлено у {memory.available: "<500Mi", nodefs.available: "<10%"}

У цьому сценарії "Allocatable" складатиме 14,5 ЦП, 28,5Гб памʼяті та 88Гб локального сховища. Планувальник забезпечує, що загальна памʼять запитів у всіх Podʼів на цьому вузлі не перевищує 28,5Гб, а сховище не перевищує 88Гб. Kubelet виселяє Podʼи, коли загальне використання памʼяті у всіх Podʼах перевищує 28,5Гб, або якщо загальне використання диска перевищує 88Гб. Якщо всі процеси на вузлі використовують як можна більше ЦП, Podʼи разом не можуть використовувати більше ніж 14,5 ЦП.

Якщо kubeReserved та/або systemReserved не застосовується, і системні служби перевищують своє резервування, kubelet виводить Podʼи, коли загальне використання памʼяті вузла вище 31,5Гб або storage перевищує 90Гб.

2.32 - Запуск компонентів вузла Kubernetes користувачем без прав root

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.22 [alpha]

У цьому документі описано, як запустити компоненти вузла Kubernetes, такі як kubelet, CRI, OCI та CNI, без прав root, використовуючи простір імен користувача.

Ця техніка також відома як rootless mode.

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.22. Для перевірки версії введіть kubectl version.

Запуск Kubernetes в Rootless Docker або Rootless Podman

kind

kind підтримує запуск Kubernetes в середовищі Rootless Docker або Rootless Podman.

Див. Запуск kind з Rootless Docker.

minikube

minikube також підтримує запуск Kubernetes в середовищі Rootless Docker або Rootless Podman.

Див. документацію Minikube:

Запуск Kubernetes всередині непривілейованих контейнерів

sysbox

Sysbox — це відкрите програмне забезпечення для виконання контейнерів (подібне до "runc"), яке підтримує запуск робочих навантажень на рівні системи, таких як Docker та Kubernetes, всередині непривілейованих контейнерів, ізольованих за допомогою просторів користувачів Linux.

Дивіться Sysbox Quick Start Guide: Kubernetes-in-Docker для отримання додаткової інформації.

Sysbox підтримує запуск Kubernetes всередині непривілейованих контейнерів без потреби в Cgroup v2 і без використання KubeletInUserNamespace. Він досягає цього за допомогою розкриття спеціально створених файлових систем /proc та /sys всередині контейнера, а також кількох інших передових технік віртуалізації операційної системи.

Запуск Rootless Kubernetes безпосередньо на хості

K3s

K3s експериментально підтримує режим без root-прав.

Дивіться Запуск K3s у режимі Rootless для використання.

Usernetes

Usernetes — це референсний дистрибутив Kubernetes, який може бути встановлений у теці $HOME без привілеїв root.

Usernetes підтримує як containerd, так і CRI-O як середовище виконання контейнерів CRI. Usernetes підтримує багатовузлові кластери з використанням Flannel (VXLAN).

Дивіться репозиторій Usernetes для використання.

Ручне розгортання вузла, який використовує kubelet в просторі користувача

У цьому розділі надаються вказівки для запуску Kubernetes у просторі користувача вручну.

Створення простору користувача

Першим кроком є створення простору користувача.

Якщо ви намагаєтеся запустити Kubernetes в контейнері з простором користувача, такому як Rootless Docker/Podman або LXC/LXD, ви готові та можете перейти до наступного підрозділу.

Інакше вам доведеться створити простір користувача самостійно, викликавши unshare(2) з CLONE_NEWUSER.

Простір користувача також можна відокремити за допомогою інструментів командного рядка, таких як:

Після відокремлення простору користувача вам також доведеться відокремити інші простори імен, такі як простір імен монтування.

Вам не потрібно викликати chroot() або pivot_root() після відокремлення простору імен монтування, однак вам потрібно буде монтувати записувані файлові системи у кількох теках в просторі імен.

Принаймні, наступні теки повинні бути записуваними в просторі імен (не поза простором імен):

  • /etc
  • /run
  • /var/logs
  • /var/lib/kubelet
  • /var/lib/cni
  • /var/lib/containerd (для containerd)
  • /var/lib/containers (для CRI-O)

Створення делегованого дерева cgroup

Крім простору користувача, вам також потрібно мати записуване дерево cgroup з cgroup v2.

Якщо ви намагаєтеся запустити Kubernetes у Rootless Docker/Podman або LXC/LXD на хості на основі systemd, у вас все готове.

У противному вам доведеться створити службу systemd з властивістю Delegate=yes, щоб делегувати дерево cgroup з правами на запис.

На вашому вузлі система systemd вже повинна бути налаштована на дозвіл делегування; для отримання докладнішої інформації дивіться cgroup v2 в документації Rootless Containers.

Налаштування мережі

Простір імен мережі компонентів вузла повинен мати не-loopback інтерфейс, який, наприклад, може бути налаштований з використанням slirp4netns, VPNKit, або lxc-user-nic(1).

Простори імен мережі Podʼів можна налаштувати за допомогою звичайних втулків CNI. Для мережі з багатьма вузлами відомо, що Flannel (VXLAN, 8472/UDP) працює.

Порти, такі як порт kubelet (10250/TCP) і порти служби NodePort, повинні бути викриті з простору імен мережі вузла на хост зовнішнім перенаправлювачем портів, таким як RootlessKit, slirp4netns, або socat(1).

Ви можете використовувати перенаправлювач портів від K3s. Див. Запуск K3s в режимі без root-прав для отримання докладнішої інформації. Реалізацію можна знайти в пакунку pkg/rootlessports k3s.

Налаштування CRI

Kubelet покладається на середовище виконання контейнерів. Ви повинні розгорнути середовище виконання контейнерів, таке як containerd або CRI-O, і переконатися, що воно працює у просторі користувача до запуску kubelet.

Запуск CRI втулка containerd в просторі користувача підтримується з версії containerd 1.4.

Запуск containerd у просторі користувача вимагає наступних налаштувань.

version = 2

[plugins."io.containerd.grpc.v1.cri"]
# Вимкнути AppArmor
  disable_apparmor = true
# Ігнорувати помилку під час встановлення oom_score_adj
  restrict_oom_score_adj = true
# Вимкнути контролер hugetlb cgroup v2 (тому що systemd не підтримує делегування контролера hugetlb)
  disable_hugetlb_controller = true

[plugins."io.containerd.grpc.v1.cri".containerd]
# Можливий також використання non-fuse overlayfs для ядра >= 5.11, але потребує вимкненого SELinux
  snapshotter = "fuse-overlayfs"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# Ми використовуємо cgroupfs, який делегується системою systemd, тому ми не використовуємо драйвер SystemdCgroup
# (якщо ви не запускаєте іншу систему systemd у просторі імен)
  SystemdCgroup = false

Типовий шлях конфігураційного файлу — /etc/containerd/config.toml. Шлях можна вказати з containerd -c /шлях/до/конфігураційного/файлу.toml.

Запуск CRI-O у просторі користувача підтримується з версії CRI-O 1.22.

CRI-O вимагає, щоб була встановлена змінна середовища _CRIO_ROOTLESS=1.

Також рекомендуються наступні налаштування:

[crio]
  storage_driver = "overlay"
# Можливий також використання non-fuse overlayfs для ядра >= 5.11, але потребує вимкненого SELinux
  storage_option = ["overlay.mount_program=/usr/local/bin/fuse-overlayfs"]

[crio.runtime]
# Ми використовуємо cgroupfs, який делегується системою systemd, тому ми не використовуємо драйвер "systemd"
# (якщо ви не запускаєте іншу систему systemd у просторі імен)
  cgroup_manager = "cgroupfs"

Типовий шлях конфігураційного файлу — /etc/crio/crio.conf. Шлях можна вказати з crio --config /шлях/до/конфігураційного/файлу/crio.conf.

Налаштування kubelet

Запуск kubelet у просторі користувача вимагає наступної конфігурації:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  KubeletInUserNamespace: true
# Ми використовуємо cgroupfs, який делегується системою systemd, тому ми не використовуємо драйвер "systemd"
# (якщо ви не запускаєте іншу систему systemd у просторі імен)
cgroupDriver: "cgroupfs"

Коли увімкнено KubeletInUserNamespace, kubelet ігнорує помилки, які можуть виникнути під час встановлення наступних значень sysctl на вузлі.

  • vm.overcommit_memory
  • vm.panic_on_oom
  • kernel.panic
  • kernel.panic_on_oops
  • kernel.keys.root_maxkeys
  • kernel.keys.root_maxbytes.

У просторі користувача kubelet також ігнорує будь-яку помилку, яка виникає при спробі відкрити /dev/kmsg. Цей feature gate також дозволяє kube-proxy ігнорувати помилку під час встановлення RLIMIT_NOFILE.

KubeletInUserNamespace був введений у Kubernetes v1.22 зі статусом "alpha".

Запуск kubelet у просторі користувача без використання цього feature gate також можливий, шляхом монтування спеціально створеного файлової системи proc (як це робить Sysbox), але це не є офіційно підтримуваним.

Налаштування kube-proxy

Запуск kube-proxy у просторі користувача вимагає наступної конфігурації:

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "iptables" # або "userspace"
conntrack:
# Пропустити встановлення значення sysctl "net.netfilter.nf_conntrack_max"
  maxPerCore: 0
# Пропустити встановлення "net.netfilter.nf_conntrack_tcp_timeout_established"
  tcpEstablishedTimeout: 0s
# Пропустити встановлення "net.netfilter.nf_conntrack_tcp_timeout_close"
  tcpCloseWaitTimeout: 0s

Застереження

  • Більшість "нелокальних" драйверів томів, таких як nfs та iscsi, не працюють. Відомо, що працюють локальні томи, такі як local, hostPath, emptyDir, configMap, secret та downwardAPI.

  • Деякі втулки CNI можуть не працювати. Відомо, що працює Flannel (VXLAN).

Для отримання додаткової інформації з цього питання, див. сторінку Застереження та майбутня робота на веб-айті rootlesscontaine.rs.

Дивіться також

2.33 - Безпечне очищення вузла

Ця сторінка показує, як безпечно очистити вузол, опційно дотримуючись PodDisruptionBudget, який ви визначили.

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

Це завдання передбачає, що ви виконали такі попередні умови:

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

(Необовʼязково) Налаштування бюджету відмови

Щоб забезпечити доступність ваших робочих навантажень під час технічного обслуговування, ви можете налаштувати PodDisruptionBudget.

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

Рекомендується встановити AlwaysAllow Політика виселення несправного Podʼа для PodDisruptionBudgets, щоб підтримувати виселення погано працюючих застосунків під час очищення вузла. Стандартна поведінка полягає в очікуванні на те, щоб Podʼи застосунків стали справними, перш ніж можна буде продовжити очищення.

Використання kubectl drain для очищення вузла

Ви можете використовувати kubectl drain для безпечного виселення всіх ваших Podʼів з вузла перед тим, як ви будете виконувати обслуговування вузла (наприклад, оновлення ядра, обслуговування обладнання тощо). Безпечні виселення дозволяють контейнерам Podʼів належним чином завершувати роботу і дотримуватись PodDisruptionBudgets, які ви визначили.

Коли kubectl drain успішно завершується, це означає, що всі Podʼи (крім тих, які виключені, як описано в попередньому абзаці) безпечно виселені (дотримуючись бажаного періоду належного завершення роботи та PodDisruptionBudget, який ви визначили). Тоді безпечно вимкніть вузол, вимкнувши його фізичний компʼютер або, якщо ви працюєте на хмарній платформі, видаливши його віртуальну машину.

Спочатку визначте імʼя вузла, який ви хочете очистити. Ви можете перелічити всі вузли у своєму кластері за допомогою

kubectl get nodes

Далі скажіть Kubernetes очистити вузол:

kubectl drain --ignore-daemonsets <імʼя вузла>

Якщо є Podʼи, керовані DaemonSet, вам потрібно вказати --ignore-daemonsets в kubectl, щоб успішно очистити вузол. Підкоманда kubectl drain сама по собі насправді не очищує вузол від його Podʼів DaemonSet: контролер DaemonSet (частина контролера управління) негайно замінює відсутні Podʼи новими еквівалентними Podʼами. Контролер DaemonSet також створює Podʼи, які ігнорують taint, що перешкоджають плануванню, що дозволяє новим Podʼам запуститися на вузлі, який ви очистили.

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

kubectl uncordon <імʼя вузла>

після того, як ви дасте цю команду Kubernetes, він може продовжити планування нових Podʼів на вузол.

Очищення кількох вузлів паралельно

Команду kubectl drain слід використовувати тільки для одного вузла за раз. Однак ви можете запускати кілька команд kubectl drain для різних вузлів паралельно, в різних терміналах або у фоні. Декілька команд очищення, які працюють паралельно, все одно дотримуються PodDisruptionBudget, який ви вказуєте.

Наприклад, якщо у вас є StatefulSet із трьома репліками та ви встановили PodDisruptionBudget для цього набору, вказуючи minAvailable: 2, kubectl drain видаляє тільки Pod з StatefulSet, якщо всі три репліки Pod є справними; якщо дати декілька команд паралельно, Kubernetes дотримується PodDisruptionBudget та забезпечує, що в будь-який момент часу лише один (обчислюється як replicas - minAvailable) Pod недоступний. Будь-які очищення, які призведуть до того, що кількість справних реплік падає нижче визначеного бюджету, блокуються.

API Eviction

Якщо ви не бажаєте використовувати kubectl drain (наприклад, для уникнення виклику зовнішньої команди або для отримання більш детального керування процесом виселення Podʼа), ви також можете програмно викликати виселення, використовуючи API Eviction.

Для отримання додаткової інформації див. Виселення, ініційоване API.

Що далі

2.34 - Захист кластера

У цьому документі розглядаються теми, повʼязані з захистом кластера від випадкового або зловмисного доступу та надаються загальні рекомендації безпеки.

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

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

    Для перевірки версії введіть kubectl version.

Контроль доступу до API Kubernetes

Оскільки Kubernetes повністю заснований на API, контроль та обмеження, хто може мати доступ до кластера та які дії вони можуть виконувати, є першою лінією захисту.

Використання Transport Layer Security (TLS) для всього трафіку API

Kubernetes очікує, що всі комунікації API в кластері будуть типово зашифровані за допомогою TLS, і більшість методів встановлення дозволять створювати необхідні сертифікати та розподіляти їх між компонентами кластера. Зверніть увагу, що деякі компоненти та методи встановлення можуть дозволяти локальні порти через HTTP, і адміністраторам варто ознайомитися з налаштуваннями кожного компонента для ідентифікації потенційно незахищеного трафіку.

Автентифікація API

Виберіть механізм автентифікації для використання API-серверами, який відповідає загальним сценаріям доступу при встановленні кластера. Наприклад, невеликі, однокористувацькі кластери можуть бажати використовувати простий підхід з використанням сертифікатів або статичних токенів Bearer. Більші кластери можуть бажати інтегрувати наявний сервер OIDC або LDAP, який дозволяє розподіляти користувачів на групи.

Всі клієнти API повинні бути автентифіковані, навіть ті, що є частиною інфраструктури, такі як вузли, проксі, планувальник та втулки томів. Зазвичай ці клієнти є сервісними обліковими записами або використовують сертифікати клієнта x509, і вони створюються автоматично при запуску кластера або налаштовуються як частина встановлення кластера.

Для отримання додаткової інформації звертайтеся до документації з автентифікації.

Авторизація API

Після автентифікації кожен виклик API також повинен пройти перевірку авторизації. Kubernetes має вбудований компонент контролю доступу на основі ролей (Role-Based Access Control (RBAC)), який зіставляє користувача або групу набору дозволів, згрупованих за ролями. Ці дозволи поєднують дії (отримати, створити, видалити) з ресурсами (Podʼи, служби, вузли) і можуть бути обмежені простором імен або розгортанням кластера. Доступні набори стандартних ролей, які надають розумне розділення відповідальності залежно від дій, які може бажати виконати клієнт. Рекомендується використовувати авторизатори Node та RBAC разом з втулком входу NodeRestriction.

Так само як і з автентифікацією, для менших кластерів можуть бути відповідні прості та загальні ролі, але зі збільшенням кількості користувачів, які взаємодіють з кластером, може знадобитися розділити команди на окремі простори імен з більш обмеженими ролями.

При авторизації важливо розуміти, як оновлення одного обʼєкта може призвести до дій в інших місцях. Наприклад, користувач не зможе створити Podʼи безпосередньо, але дозволивши їм створювати Deployment, що створює Podʼи для них, дозволить їм створювати ці Podʼи опосередковано. Так само, видалення вузла з API призведе до припинення роботи Podʼів, запланованих на цей вузол, і їх повторного створення на інших вузлах. Стандартні ролі являють собою компроміс між гнучкістю та загальними випадками використання, але більш обмежені ролі повинні бути уважно переглянуті, щоб запобігти випадковому підвищенню привілеїв. Якщо стандартні ролі не відповідають вашим потребам, ви можете створити ролі, специфічні для вашого випадку використання.

Для отримання додаткової інформації звертайтеся до розділу довідки з авторизації.

Контроль доступу до Kubelet

Kubelet експонує HTTPS-точки доступу, які надають потужний контроль над вузлом та контейнерами. Типово Kubelet дозволяє неавтентифікований доступ до цього API.

Операційні кластери повинні увімкнути автентифікацію та авторизацію Kubelet.

Для отримання додаткової інформації звертайтеся до розділу довідки з автентифікації/авторизації Kubelet.

Керування можливостями робочого навантаження або користувача під час виконання

Авторизація в Kubernetes має високий рівень, спрямований на грубі дії з ресурсами. Більш потужні елементи керування існують як політики для обмеження за випадком використання того, як ці обʼєкти діють на кластер, себе та інші ресурси.

Обмеження використання ресурсів у кластері

Квоти ресурсів обмежують кількість або потужність виділених ресурсів для простору імен. Це найчастіше використовується для обмеження обсягу процесора, памʼяті або постійного дискового простору, який може виділятися простору імен, але також може контролювати кількість Podʼів, Serviceʼів або томів, що існують у кожному просторі імен.

Діапазони обмежень обмежують максимальний або мінімальний розмір деяких з вищезазначених ресурсів, щоб уникнути можливості користувачів запитувати надмірно високі або низькі значення для часто зарезервованих ресурсів, таких як памʼять, або надавати типові значення, коли вони не вказані.

Керування привілеями, з якими працюють контейнери

В описі Podʼа міститься контекст безпеки, який дозволяє запитувати доступ до виконання від імені конкретного користувача Linux на вузлі (наприклад, root), доступ до виконання з підвищеними привілеями або доступ до мережі хосту та інші елементи керування, які інакше дозволили б йому працювати без обмежень на вузлі хосту.

Ви можете налаштувати допуски безпеки Pod, щоб забезпечити використання певного стандарту безпеки Pod у просторі імен або виявити порушення.

Загалом, більшість робочих навантажень застосунків потребують обмеженого доступу до ресурсів хосту, щоб вони могли успішно працювати як процес root (uid 0) без доступу до інформації про хост. Однак, враховуючи привілеї, повʼязані з користувачем root, слід робити контейнери застосунків такими, що не вимагають прав root для виконання. Так само адміністратори, які бажають запобігти втечі клієнтських застосунків з їхніх контейнерів, повинні застосувати стандарт безпеки Pod Baseline або Restricted.

Запобігання завантаженню небажаних модулів ядра контейнерами

Ядро Linux автоматично завантажує модулі ядра з диска за потребою в певних обставинах, наприклад, коли пристрій приєднаний або файлова система змонтована. Особливо актуальним для Kubernetes є те, що навіть непривілейовані процеси можуть спричинити завантаження певних модулів, повʼязаних з мережевими протоколами, просто створюючи сокет відповідного типу. Це може дозволити зловмиснику використовувати дірку в безпеці ядра, щодо якої адміністратор має припущення, що вона не використовується.

Щоб запобігти автоматичному завантаженню конкретних модулів, ви можете деінсталювати їх з вузла або додати правила для їх блокування. У більшості дистрибутивів Linux ви можете це зробити, створивши файл, наприклад, /etc/modprobe.d/kubernetes-blacklist.conf, з таким вмістом:

# DCCP майже не потрібен, має кілька серйозних вразливостей
# і не знаходиться у доброму стані обслуговування.
blacklist dccp

# SCTP не використовується в більшості кластерів Kubernetes, і також мав
# вразливості в минулому.
blacklist sctp

Для більш загального блокування завантаження модулів можна використовувати Linux Security Module (наприклад, SELinux), щоб абсолютно відмовити контейнерам у дозволі module_request, запобігаючи ядру завантажувати модулі для контейнерів у будь-яких обставинах. (Podʼи все ще можуть використовувати модулі, які були завантажені вручну або модулі, які були завантажені ядром від імені якогось більш привілейованого процесу.)

Обмеження доступу до мережі

Мережеві політики для простору імен дозволяють авторам застосунків обмежувати, які Podʼи в інших просторах імен можуть отримувати доступ до Podʼів і портів у їхніх просторах імен. Багато з підтримуваних постачальників мережі Kubernetes зараз враховують мережеві політики.

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

Додаткові захисти можуть бути доступні, які контролюють правила мережі на рівні втулка чи середовища, такі як брандмауери на рівні вузла, фізичне розділення вузлів кластера для запобігання звʼязку між ними або розширена політика мережевого зʼєднання.

Обмеження доступу до API метаданих хмари

Хмарні платформи (AWS, Azure, GCE і т. д.) часто надають доступ до служб метаданих локально на екземплярах обчислювальних потужностей. Типово ці API доступні Podʼам, що працюють на екземплярі, і можуть містити облікові дані хмари для цього вузла або дані про створення, такі як облікові дані kubelet. Ці облікові дані можуть бути використані для підвищення привілеїв всередині кластера або до інших хмарних служб за тим самим обліковим записом.

При запуску Kubernetes на хмарній платформі обмежуйте дозволи, надані обліковим записам екземплярів, використовуйте мережеві політики для обмеження доступу Podʼів до API метаданих та уникайте використання даних про створення для передачі секретів.

Керування того, до яких вузлів мають доступ Podʼи

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

Як адміністратор, бета-втулок обробки доспуску PodNodeSelector може бути використаний для примусового призначення Podʼів у межах стандартного простору імен або для вимоги до вказання певного селектора вузла, і якщо кінцеві користувачі не можуть змінювати простори імен, це може суттєво обмежити розміщення всіх Podʼів в певному робочому навантаженні.

Захист компонентів кластера від компрометації

У цьому розділі описані деякі загальні шаблони для захисту кластерів від компрометації.

Обмеження доступу до etcd

Права на запис до бази даних etcd для API еквівалентні отриманню root-прав на весь кластер, а доступ на читання може бути використаний для ескалації досить швидко. Адміністратори повинні завжди використовувати надійні облікові дані від серверів API до сервера etcd, такі як взаємна автентифікація за допомогою сертифікатів TLS клієнта, і часто рекомендується ізолювати сервери etcd за допомогою брандмауера, до яких можуть отримувати доступ лише сервери API.

Увімкнення логування аудиту

Audit logger є бета-функцією, яка записує дії, виконані API, для подальшого аналізу в разі компрометації. Рекомендується увімкнути логування аудиту та архівувати файл аудиту на захищеному сервері.

Обмеження доступу до альфа- або бета-функцій

Альфа- і бета-функції Kubernetes знаходяться в активній розробці і можуть мати обмеження або помилки, які призводять до вразливостей безпеки. Завжди оцінюйте цінність, яку можуть надати альфа- або бета-функції, у порівнянні з можливим ризиком для вашої безпеки. У разі сумнівів вимикайте функції, які ви не використовуєте.

Часто змінюйте облікові дані інфраструктури

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

Перегляд інтеграцій сторонніх розробників перед їх включенням

Багато сторонніх інтеграцій до Kubernetes можуть змінювати профіль безпеки вашого кластера. При включенні інтеграції завжди переглядайте дозволи, які запитує розширення, перед наданням доступу. Наприклад, багато інтеграцій з безпеки можуть запитувати доступ до перегляду всіх секретів у вашому кластері, що фактично робить цей компонент адміністратором кластера. У разі сумнівів обмежте інтеграцію на роботу в одному просторі імен, якщо це можливо.

Компоненти, які створюють Podʼи, також можуть мати неочікувану потужність, якщо вони можуть робити це всередині просторів імен, таких як простір імен kube-system, оскільки ці Podʼи можуть отримати доступ до секретів облікових записів служб або працювати з підвищеними привілеями, якщо цим службовим обліковим записам надано доступ до дозвольних PodSecurityPolicies.

Якщо ви використовуєте Pod Security admission та дозволяєте будь-якому компоненту створювати Podʼи в межах простору імен, що дозволяє привілейовані Podʼи, ці Podʼи можуть мати здатність втікати зі своїх контейнерів і використовувати цей розширений доступ для підвищення своїх привілеїв.

Ви не повинні дозволяти ненадійним компонентам створювати Podʼи в будь-якому системному просторі імен (те, що починається з kube-) або в будь-якому просторі імен, де цей доступ дозволяє можливість підвищення привілеїв.

Шифрування секретів у спокої

Загалом, база даних etcd буде містити будь-яку інформацію, доступну через API Kubernetes і може надати зловмиснику значний обсяг інформації про стан вашого кластера. Завжди шифруйте свої резервні копії за допомогою розглянутого рішення для резервного копіювання та шифрування, і розгляньте можливість використання повного шифрування диска, де це можливо.

Kubernetes підтримує необовʼязкове шифрування у спокої для інформації в API Kubernetes. Це дозволяє вам забезпечити те, що при збереженні Kubernetes даних для обʼєктів (наприклад, обʼєктів Secret або ConfigMap), сервер API записує зашифроване представлення обʼєкта. Це шифрування означає, що навіть у того, хто має доступ до даних резервних копій etcd, не має можливості переглянути вміст цих обʼєктів. У Kubernetes 1.31 ви також можете шифрувати власні ресурси; шифрування в спокої для розширених API, визначених у визначеннях CustomResourceDefinitions, було додано в Kubernetes як частина випуску v1.26.

Отримання сповіщень про оновлення безпеки та повідомлення про вразливості

Приєднуйтесь до групи kubernetes-announce, щоб отримувати електронні листи про оголошення з питань безпеки. Див. сторінку повідомлень про безпеку для отримання додаткової інформації щодо повідомлення про вразливості.

Що далі

2.35 - Встановлення параметрів Kubelet через файл конфігурації

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

Деякі кроки на цій сторінці використовують інструмент jq. Якщо у вас немає jq, ви можете встановити його через отримання оновлень програм вашої операційної системи або завантажити з https://jqlang.github.io/jq/.

Деякі кроки також включають встановлення curl, який також можна встановити засобами встановлення програмного забезпечення вашої операційної системи.

Частину параметрів конфігурації kubelet можна встановити за допомогою конфігураційного файлу на диску, як альтернативу командним прапорцям.

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

Створіть файл конфігурації

Підмножина конфігурації kubelet, яку можна налаштувати через файл, визначається структурою KubeletConfiguration.

Файл конфігурації повинен бути у форматі JSON або YAML, який представляє параметри цієї структури. Переконайтеся, що у kubelet є права для читання файлу.

Ось приклад того, як може виглядати цей файл:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
address: "192.168.0.8"
port: 20250
serializeImagePulls: false
evictionHard:
    memory.available:  "100Mi"
    nodefs.available:  "10%"
    nodefs.inodesFree: "5%"
    imagefs.available: "15%"

У цьому прикладі kubelet налаштовано з наступними параметрами:

  1. address: Kubelet буде доступний за IP-адресою 192.168.0.8.
  2. port: Kubelet буде слухати порт 20250.
  3. serializeImagePulls: Завантаження образів виконуватиметься паралельно.
  4. evictionHard: Kubelet буде виселяти Podʼи за однією з наступних умов:
    • Коли доступна памʼять вузла впаде нижче 100 МіБ.
    • Коли доступний простір основної файлової системи вузла менше 10%.
    • Коли доступний простір файлової системи образів менше 15%.
    • Коли більше ніж 95% inodes основної файлової системи вузла використано.

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

Запуск процесу kubelet, налаштованого через файл конфігурації

Запустіть kubelet з параметром --config, вказавши шлях до файлу конфігурації kubelet. Після цього kubelet завантажить свою конфігурацію з цього файлу.

Зверніть увагу, що параметри командного рядка, які стосуються того ж значення, що й файл конфігурації, перекриватимуть це значення. Це допомагає забезпечити зворотну сумісність з API командного рядка.

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

Зауважте, що деякі типові значення відрізняються між параметрами командного рядка та файлом конфігурації kubelet. Якщо наданий параметр --config і значення не вказані через командний рядок, то застосовуються типові значення для версії KubeletConfiguration. У згаданому вище прикладі ця версія є kubelet.config.k8s.io/v1beta1.

Тека для файлів конфігурації kubelet

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.30 [beta]

Ви можете вказати теку конфігурації drop-in для kubelet. Стандартно kubelet не шукає файли конфігурації drop-in — ви повинні вказати шлях. Наприклад: --config-dir=/etc/kubernetes/kubelet.conf.d

Для Kubernetes v1.28 по v1.29 ви можете вказати лише --config-dir, якщо також встановите змінну середовища KUBELET_CONFIG_DROPIN_DIR_ALPHA для процесу kubelet (значення цієї змінної не має значення).

Kubelet обробляє файли у своїй теці конфігурації drop-in, сортуючи по повному імені файлу. Наприклад, 00-kubelet.conf обробляється першим, а потім перезаписується файлом з назвою 01-kubelet.conf.

Ці файли можуть містити часткові конфігурації, але не повинні бути не валідними та обовʼязково повинні включати метадані типу, зокрема apiVersion та kind. Перевірка виконується лише на кінцевій результівній структурі конфігурації, збереженій внутрішньо в kubelet. Це надає гнучкість в управлінні та злитті конфігурацій kubelet з різних джерел, запобігаючи небажаній конфігурації. Однак важливо зазначити, що поведінка варіюється залежно від типу даних поля конфігурації.

Різні типи даних у структурі конфігурації kubelet обʼєднуються по-різному. Дивіться посилання на довідку для отримання додаткової інформації.

Порядок обʼєднання конфігурації kubelet

При запуску kubelet обʼєднує конфігурацію з:

  • Feature gate, вказаних через командний рядок (найнижчий пріоритет).
  • Конфігурація kubelet.
  • Файли конфігурації drop-in, відповідно до порядку сортування.
  • Аргументи командного рядка, за винятком feature gate (найвищий пріоритет).

Перегляд конфігурації kubelet

Оскільки конфігурація тепер може бути розподілена у декілька файлів за допомогою цієї функції, якщо хтось хоче переглянути остаточну активовану конфігурацію, то вони можуть скористатися цими кроками для перегляду конфігурації kubelet:

  1. Запустіть проксі-сервер за допомогою команди kubectl proxy у вашому терміналі.

    kubectl proxy
    

    Це дозволить отримати вивід подібний до:

    Starting to serve on 127.0.0.1:8001
    
  2. Відкрийте інше вікно термінала і скористайтесь curl, щоб отримати конфігурацію kubelet. Замініть <node-name> на фактичне імʼя вашого вузла:

    curl -X GET http://127.0.0.1:8001/api/v1/nodes/<node-name>/proxy/configz | jq .
    
    {
      "kubeletconfig": {
        "enableServer": true,
        "staticPodPath": "/var/run/kubernetes/static-pods",
        "syncFrequency": "1m0s",
        "fileCheckFrequency": "20s",
        "httpCheckFrequency": "20s",
        "address": "192.168.1.16",
        "port": 10250,
        "readOnlyPort": 10255,
        "tlsCertFile": "/var/lib/kubelet/pki/kubelet.crt",
        "tlsPrivateKeyFile": "/var/lib/kubelet/pki/kubelet.key",
        "rotateCertificates": true,
        "authentication": {
          "x509": {
            "clientCAFile": "/var/run/kubernetes/client-ca.crt"
          },
          "webhook": {
            "enabled": true,
            "cacheTTL": "2m0s"
          },
          "anonymous": {
            "enabled": true
          }
        },
        "authorization": {
          "mode": "AlwaysAllow",
          "webhook": {
            "cacheAuthorizedTTL": "5m0s",
            "cacheUnauthorizedTTL": "30s"
          }
        },
        "registryPullQPS": 5,
        "registryBurst": 10,
        "eventRecordQPS": 50,
        "eventBurst": 100,
        "enableDebuggingHandlers": true,
        "healthzPort": 10248,
        "healthzBindAddress": "127.0.0.1",
        "oomScoreAdj": -999,
        "clusterDomain": "cluster.local",
        "clusterDNS": [
          "10.0.0.10"
        ],
        "streamingConnectionIdleTimeout": "4h0m0s",
        "nodeStatusUpdateFrequency": "10s",
        "nodeStatusReportFrequency": "5m0s",
        "nodeLeaseDurationSeconds": 40,
        "imageMinimumGCAge": "2m0s",
        "imageMaximumGCAge": "0s",
        "imageGCHighThresholdPercent": 85,
        "imageGCLowThresholdPercent": 80,
        "volumeStatsAggPeriod": "1m0s",
        "cgroupsPerQOS": true,
        "cgroupDriver": "systemd",
        "cpuManagerPolicy": "none",
        "cpuManagerReconcilePeriod": "10s",
        "memoryManagerPolicy": "None",
        "topologyManagerPolicy": "none",
        "topologyManagerScope": "container",
        "runtimeRequestTimeout": "2m0s",
        "hairpinMode": "promiscuous-bridge",
        "maxPods": 110,
        "podPidsLimit": -1,
        "resolvConf": "/run/systemd/resolve/resolv.conf",
        "cpuCFSQuota": true,
        "cpuCFSQuotaPeriod": "100ms",
        "nodeStatusMaxImages": 50,
        "maxOpenFiles": 1000000,
        "contentType": "application/vnd.kubernetes.protobuf",
        "kubeAPIQPS": 50,
        "kubeAPIBurst": 100,
        "serializeImagePulls": true,
        "evictionHard": {
          "imagefs.available": "15%",
          "memory.available": "100Mi",
          "nodefs.available": "10%",
          "nodefs.inodesFree": "5%"
        },
        "evictionPressureTransitionPeriod": "1m0s",
        "enableControllerAttachDetach": true,
        "makeIPTablesUtilChains": true,
        "iptablesMasqueradeBit": 14,
        "iptablesDropBit": 15,
        "featureGates": {
          "AllAlpha": false
        },
        "failSwapOn": false,
        "memorySwap": {},
        "containerLogMaxSize": "10Mi",
        "containerLogMaxFiles": 5,
        "configMapAndSecretChangeDetectionStrategy": "Watch",
        "enforceNodeAllocatable": [
          "pods"
        ],
        "volumePluginDir": "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
        "logging": {
          "format": "text",
          "flushFrequency": "5s",
          "verbosity": 3,
          "options": {
            "json": {
              "infoBufferSize": "0"
            }
          }
        },
        "enableSystemLogHandler": true,
        "enableSystemLogQuery": false,
        "shutdownGracePeriod": "0s",
        "shutdownGracePeriodCriticalPods": "0s",
        "enableProfilingHandler": true,
        "enableDebugFlagsHandler": true,
        "seccompDefault": false,
        "memoryThrottlingFactor": 0.9,
        "registerNode": true,
        "localStorageCapacityIsolation": true,
        "containerRuntimeEndpoint": "unix:///var/run/crio/crio.sock"
      }
    }
    

Що далі

2.36 - Спільне використання кластера з просторами імен

Ця сторінка показує, як переглядати, працювати та видаляти простори імен. На сторінці також розказується, як використовувати простори імен Kubernetes для розділення вашого кластера.

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

Перегляд просторів імен

Перелік поточних просторів імен у кластері можна отримати за допомогою команди:

kubectl get namespaces
NAME              STATUS   AGE
default           Active   11d
kube-node-lease   Active   11d
kube-public       Active   11d
kube-system       Active   11d

Kubernetes запускається з чотирма просторами імен:

  • default — стандартний простір імен для обʼєктів без іншого простору імен.
  • kube-node-lease — цей простір імен містить обʼєкти Lease, повʼязані з кожним вузлом. Лізинги вузлів дозволяють kubelet надсилати пульси, щоб панель управління було в змозі виявляти збої вузлів.
  • kube-public — цей простір імен створюється автоматично і доступний для читання всіма користувачами (включаючи неавтентифікованих). Цей простір імен зазвичай зарезервований для використання у межах кластера, у випадках коли деякі ресурси мають бути відкриті та доступні для загального огляду у всьому кластері. Публічний аспект цього простору імен є лише конвенцією, а не вимогою.
  • kube-system — простір імен для обʼєктів, створених системою Kubernetes.

Також ви можете отримати інформацію про певний простір імен за допомогою команди:

kubectl get namespaces <name>

Або отримати детальну інформацію за допомогою:

kubectl describe namespaces <name>
Name:           default
Labels:         <none>
Annotations:    <none>
Status:         Active

No resource quota.

Resource Limits
 Type       Resource    Min Max Default
 ----       --------    --- --- -------
 Container  cpu         -   -   100m

Зверніть увагу, що ці деталі включають інформацію про квоти ресурсів (якщо вони є) та обмеження діапазону ресурсів.

Квота ресурсів відстежує загальне використання ресурсів у просторі імен та дозволяє операторам кластера встановлювати жорсткі ліміти використання ресурсів, які може споживати простір імен.

Обмеження діапазону визначає мінімальні/максимальні обмеження на кількість ресурсів, які може споживати одиниця у просторі імен.

Див. Контроль допуску: Limit Range

Простір імен може перебувати в одному з двох станів:

  • Active — простір імен використовується
  • Terminating — простір імен видаляється і не може бути використаний для нових обʼєктів

Для отримання додаткової інформації дивіться Простір імен в довідковій документації API.

Створення нового простору імен

Створіть новий файл YAML з назвою my-namespace.yaml з таким вмістом:

apiVersion: v1
kind: Namespace
metadata:
  name: <вставте-назву-простору-імен-тут>

Потім виконайте:

kubectl create -f ./my-namespace.yaml

Або ви можете створити простір імен за допомогою такої команди:

kubectl create namespace <вставте-назву-простору-імен-тут>

Назва вашого простору імен повинна бути дійсною DNS-міткою.

Є необовʼязкове поле finalizers, яке дозволяє спостережувачам очищати ресурси кожного разу, коли простір імен видаляється. Майте на увазі, що якщо ви вказуєте неіснуючий finalizer, простір імен буде створений, але залишиться в стані Terminating, якщо користувач спробує його видалити.

Більше інформації про finalizers можна знайти в документі про проєктування просторів імен.

Видалення простору імен

Видаліть простір імен за допомогою команди:

kubectl delete namespaces <вставте-назву-простору-імен-тут>

Ця операція видалення є асинхронною, тому протягом певного часу ви будете бачити простір імен у стані Terminating.

Поділ кластера за допомогою просторів імен Kubernetes

Типово кластер Kubernetes створює простір імен default при його розгортанні, щоб утримувати стандартний набір Podʼів, Serviceʼів та Deploymentʼів, які використовуються в кластері.

Припускаючи, що у вас є новий кластер, ви можете перевірити наявні простори імен, виконавши наступне:

kubectl get namespaces
NAME      STATUS    AGE
default   Active    13m

Створення нових просторів імен

У цьому завданні ми створимо два додаткових простори імен Kubernetes, щоб утримувати наш контент.

У випадку, коли організація використовує спільний кластер Kubernetes для розробки та операційної діяльності:

  • Команда розробників хотіла б мати простір у кластері, де вони можуть переглядати список Podʼів, Serviceʼів та Deploymentʼів, які вони використовують для створення та запуску свого застосунку. У цьому просторі ресурси Kubernetes зʼявляються та зникають, і обмеження на те, хто може або не може змінювати ресурси є слабкими для забезпечення гнучкої розробки.

  • Команда операторів хотіла б мати простір у кластері, де вони можуть використовувати строгі процедури на те, хто може або не може маніпулювати набором Podʼів, Serviceʼів та Deploymentʼів, що працюють в операційному середовищі.

Одним з можливих варіантів для цієї організації є розподіл кластера Kubernetes на два простори імен: development та production. Створімо два нових простори імен для нашої роботи.

Створіть простір імен development за допомогою kubectl:

kubectl create -f https://k8s.io/examples/admin/namespace-dev.json

А потім створімо простір імен production за допомогою kubectl:

kubectl create -f https://k8s.io/examples/admin/namespace-prod.json

Щоб переконатися, що все в порядку, виведемо список всіх просторів імен у нашому кластері.

kubectl get namespaces --show-labels
NAME          STATUS    AGE       LABELS
default       Active    32m       <none>
development   Active    29s       name=development
production    Active    23s       name=production

Створення Podʼів в кожному просторі імен

Простір імен Kubernetes забезпечує область для Podʼів, Serviceʼів та Deploymentʼів у кластері. Користувачі, що взаємодіють з одним простором імен, не бачать вмісту іншого простору імен. Щоб продемонструвати це, створімо простий Deployment та Podʼи в просторі імен development.

kubectl create deployment snowflake \
  --image=registry.k8s.io/serve_hostname \
  -n=development --replicas=2

Ми створили Deployment з 2 реплік, що запускає Pod з назвою snowflake з базовим контейнером, який обслуговує імʼя хосту.

kubectl get deployment -n=development
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
snowflake    2/2     2            2           2m
kubectl get pods -l app=snowflake -n=development
NAME                         READY     STATUS    RESTARTS   AGE
snowflake-3968820950-9dgr8   1/1       Running   0          2m
snowflake-3968820950-vgc4n   1/1       Running   0          2m

Це чудово, розробники можуть робити те, що вони хочуть, і їм не потрібно хвилюватися про те, як це вплине на контент у просторі імен production.

Перейдімо до простору імен production і покажемо, як ресурси в одному просторі імен приховані від іншого. Простір імен production повинен бути порожнім, і наступні команди не повинні повертати нічого.

kubectl get deployment -n=production
kubectl get pods -n=production

Операційна діяльність вимагає догляду за худобою, тому створімо кілька Podʼів для худоби.

kubectl create deployment cattle --image=registry.k8s.io/serve_hostname -n=production
kubectl scale deployment cattle --replicas=5 -n=production

kubectl get deployment -n=production
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
cattle       5/5     5            5           10s
kubectl get pods -l app=cattle -n=production
NAME                      READY     STATUS    RESTARTS   AGE
cattle-2263376956-41xy6   1/1       Running   0          34s
cattle-2263376956-kw466   1/1       Running   0          34s
cattle-2263376956-n4v97   1/1       Running   0          34s
cattle-2263376956-p5p3i   1/1       Running   0          34s
cattle-2263376956-sxpth   1/1       Running   0          34s

На цьому етапі має бути зрозуміло, що ресурси, які користувачі створюють в одному просторі імен, приховані від іншого простору імен.

Поки політика підтримки в Kubernetes розвивається, ми розширимо цей сценарій, щоб показати, як можна надавати різні правила авторизації для кожного простору імен.

Розуміння мотивації використання просторів імен

Один кластер повинен задовольняти потреби кількох користувачів або груп користувачів (далі в цьому документі спільнота користувачів).

Простори імен Kubernetes допомагають різним проєктам, командам або клієнтам спільно використовувати кластер Kubernetes.

Це робиться за допомогою такого:

  1. Області для імен.
  2. Механізму для прикріплення авторизації та політики до підрозділу кластера.

Використання кількох просторів імен не є обовʼязковим.

Кожна спільнота користувачів хоче мати можливість працювати в ізоляції від інших спільнот користувачів. У кожної спільноти користувачів свої:

  1. ресурси (pods, services, replication controllers тощо)
  2. політики (хто може або не може виконувати дії у своїй спільноті)
  3. обмеження (цій спільноті дозволено стільки квоти тощо)

Оператор кластера може створити простір імен для кожної унікальної спільноти користувачів.

Простір імен забезпечує унікальну область для:

  1. іменованих ресурсів (щоб уникнути базових конфліктів імен)
  2. делегування прав управління довіреним користувачам
  3. здатності обмежувати використання ресурсів спільнотою

Сценарії використання включають такі:

  1. Як оператор кластера, я хочу підтримувати кілька спільнот користувачів на одному кластері.
  2. Як оператор кластера, я хочу делегувати владу управління підрозділам кластера довіреним користувачам у цих спільнотах.
  3. Як оператор кластера, я хочу обмежити кількість ресурсів, які кожна спільнота може споживати, щоб обмежити вплив на інші спільноти, що використовують кластер.
  4. Як користувач кластера, я хочу взаємодіяти з ресурсами, які є важливими для моєї спільноти користувачів в ізоляції від того, що роблять інші спільноти користувачів на кластері.

Розуміння просторів імен та DNS

Коли ви створюєте Service, він створює відповідний DNS-запис. Цей запис має вигляд <імʼя-сервісу>.<імʼя-простору-імен>.svc.cluster.local, що означає, що якщо контейнер використовує <імʼя-сервісу>, воно буде розпізнано як сервіс, який знаходиться в межах простору імен. Це корисно для використання однакової конфігурації в кількох просторах імен, таких як Development, Staging та Production. Якщо ви хочете отримати доступ за межі просторів імен, вам потрібно використовувати повністю кваліфіковане доменне імʼя (FQDN).

Що далі

2.37 - Оновлення кластера

Ця сторінка надає огляд кроків, які вам слід виконати для оновлення кластера Kubernetes.

Спосіб оновлення кластера залежить від того, як ви спочатку розгорнули його та від будь-яких наступних змін.

На високому рівні кроки, які ви виконуєте, такі:

  • Оновити панель управління
  • Оновити вузли в вашому кластері
  • Оновити клієнтів, такі як kubectl
  • Відредагувати маніфести та інші ресурси на основі змін API, які супроводжують нову версію Kubernetes

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

Вам потрібно мати кластер. Ця сторінка присвячена оновленню з Kubernetes 1.30 до Kubernetes 1.31. Якщо ваш кластер зараз працює на Kubernetes 1.30, тоді, будь ласка, перевірте документацію для версії Kubernetes, на яку ви плануєте оновити.

Підходи до оновлення

kubeadm

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

Після того, як ви оновили кластер, не забудьте встановити останню версію kubectl.

Ручне розгортання

Вам слід вручну оновити панель управління наступним чином:

  • etcd (всі екземпляри)
  • kube-apiserver (всі хости панелі управління)
  • kube-controller-manager
  • kube-scheduler
  • контролер управління хмари, якщо ви використовуєте його

На цьому етапі вам слід встановити останню версію kubectl.

Для кожного вузла в вашому кластері, очистіть цей вузол, а потім або замініть його новим вузлом, який використовує 1.31 kubelet, або оновіть kubelet на цьому вузлі та відновіть його Service.

Інші розгортання

Дивіться документацію для вашого інструменту розгортання кластера, щоб дізнатися рекомендовані кроки для підтримки.

Завдання після оновлення

Перемикання версії API зберігання кластера

Обʼєкти, які серіалізуються в etcd для внутрішнього представлення кластера ресурсів Kubernetes, записуються за допомогою певної версії API.

Коли підтримуване API змінюється, ці обʼєкти можуть потребувати перезаписування в новому API. Невиконання цього призведе до того, що ресурси не можна буде декодувати або використовувати за допомогою сервера API Kubernetes.

Для кожного ураженого обʼєкта, отримайте його, використовуючи останню підтримувану версію API, а потім записуйте його також, використовуючи останню підтримувану версію API.

Оновлення маніфестів

Оновлення до нової версії Kubernetes може надати нові API.

Ви можете використовувати команду kubectl convert для конвертації маніфестів між різними версіями API. Наприклад:

kubectl convert -f pod.yaml --output-version v1

Інструмент kubectl замінює вміст pod.yaml на маніфест, який встановлює kind на Pod (незмінно), але з оновленим apiVersion.

Втулки пристроїв

Якщо ваш кластер використовує втулки пристроїв і вузол потребує оновлення до випуску Kubernetes з новішою версією API втулка пристроїв, втулки пристроїв повинні бути оновлені для підтримки обох версій перед оновленням вузла, щоб гарантувати, що виділення пристроїв продовжує успішно завершуватися під час оновлення.

Дивіться Сумісність API та Версії API керуючого пристрою Kubelet для отримання додаткової інформації.

2.38 - Використання каскадного видалення у кластері

Ця сторінка показує, як вказати тип каскадного видалення у вашому кластері під час збору сміття.

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

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

Вам також потрібно створити приклад Deployment, щоб експериментувати з різними типами каскадного видалення. Вам доведеться перестворити Deployment для кожного типу.

Перевірка власників у ваших Podʼах

Перевірте, що поле ownerReferences присутнє у ваших Podʼах:

kubectl get pods -l app=nginx --output=yaml

Вивід має поле ownerReferences, схоже на це:

apiVersion: v1
    ...
    ownerReferences:
    - apiVersion: apps/v1
      blockOwnerDeletion: true
      controller: true
      kind: ReplicaSet
      name: nginx-deployment-6b474476c4
      uid: 4fdcd81c-bd5d-41f7-97af-3a3b759af9a7
    ...

Використання каскадного видалення на видності

Стандартно Kubernetes використовує фонове каскадне видалення для видалення залежностей обʼєкта. Ви можете переключитися на каскадне видалення на видноті за допомогою kubectl або за допомогою API Kubernetes, залежно від версії Kubernetes вашого кластера. Для перевірки версії введіть kubectl version.

Ви можете видаляти обʼєкти за допомогою каскадного видалення, використовуючи kubectl або API Kubernetes.

За допомогою kubectl

Виконайте наступну команду:

kubectl delete deployment nginx-deployment --cascade=foreground

За допомогою API Kubernetes

  1. Запустіть локальний проксі:

    kubectl proxy --port=8080
    
  2. Використовуйте curl для виклику видалення:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
        -H "Content-Type: application/json"
    

    Вивід містить foregroundDeletion finalizer подібно до цього:

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "metadata": {
        "name": "nginx-deployment",
        "namespace": "default",
        "uid": "d1ce1b02-cae8-4288-8a53-30e84d8fa505",
        "resourceVersion": "1363097",
        "creationTimestamp": "2021-07-08T20:24:37Z",
        "deletionTimestamp": "2021-07-08T20:27:39Z",
        "finalizers": [
          "foregroundDeletion"
        ]
        ...
    

Використання фонового каскадного видалення

  1. Створіть приклад Deployment.
  2. Використовуйте або kubectl, або API Kubernetes для видалення Deployment, залежно від версії Kubernetes вашого кластера. Для перевірки версії введіть kubectl version.

Ви можете видаляти обʼєкти за допомогою фонового каскадного видалення за допомогою kubectl або API Kubernetes.

Kubernetes типово використовує фонове каскадне видалення, і робить це навіть якщо ви виконуєте наступні команди без прапорця --cascade або аргументу propagationPolicy.

За допомогою kubectl

Виконайте наступну команду:

kubectl delete deployment nginx-deployment --cascade=background

За допомогою API Kubernetes

  1. Запустіть локальний проксі:

    kubectl proxy --port=8080
    
  2. Використовуйте curl для виклику видалення:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Background"}' \
        -H "Content-Type: application/json"
    

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

    "kind": "Status",
    "apiVersion": "v1",
    ...
    "status": "Success",
    "details": {
        "name": "nginx-deployment",
        "group": "apps",
        "kind": "deployments",
        "uid": "cc9eefb9-2d49-4445-b1c1-d261c9396456"
    }
    

Видалення власних обʼєктів та загублених залежностей

Типово, коли ви вказуєте Kubernetes видалити обʼєкт, controller також видаляє залежні обʼєкти. Ви можете загубити залежності використовуючи kubectl або API Kubernetes, залежно від версії Kubernetes вашого кластера. Для перевірки версії введіть kubectl version.

За допомогою kubectl

Виконайте наступну команду:

kubectl delete deployment nginx-deployment --cascade=orphan

За допомогою API Kubernetes

  1. Запустіть локальний проксі:

    kubectl proxy --port=8080
    
  2. Використовуйте curl для виклику видалення:

    curl -X DELETE localhost:8080/apis/apps/v1/namespaces/default/deployments/nginx-deployment \
        -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
        -H "Content-Type: application/json"
    

    Вивід містить orphan у полі finalizers, подібно до цього:

    "kind": "Deployment",
    "apiVersion": "apps/v1",
    "namespace": "default",
    "uid": "6f577034-42a0-479d-be21-78018c466f1f",
    "creationTimestamp": "2021-07-09T16:46:37Z",
    "deletionTimestamp": "2021-07-09T16:47:08Z",
    "deletionGracePeriodSeconds": 0,
    "finalizers": [
      "orphan"
    ],
    ...
    

Ви можете перевірити, що Podʼи, керовані Deployment, все ще працюють:

kubectl get pods -l app=nginx

Що далі

2.39 - Використання постачальника KMS для шифрування даних

Ця сторінка показує, як налаштувати постачальника Служби керування ключами (KMS) та втулок для шифрування конфіденційних даних. У Kubernetes 1.31 існують дві версії шифрування даних за допомогою KMS у спокої. Якщо це можливо, вам слід використовувати KMS v2, оскільки KMS v1 застарів (починаючи з Kubernetes v1.28) та є типово вимкненим (починаючи з Kubernetes v1.29). KMS v2 пропонує значно кращі характеристики продуктивності, ніж KMS v1.

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

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

Версія Kubernetes, яка вам потрібна, залежить від того, яку версію API KMS ви вибрали. Kubernetes рекомендує використовувати KMS v2.

  • Якщо ви вибрали KMS API v1 для підтримки кластерів до версії v1.27 або якщо у вас є застарілий плагін KMS, який підтримує лише KMS v1, будь-яка підтримувана версія Kubernetes буде працювати. Цей API є застарілим починаючи з Kubernetes v1.28. Kubernetes не рекомендує використовувати цей API.
Для перевірки версії введіть kubectl version.

KMS v1

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.28 [deprecated]
  • Потрібна версія Kubernetes 1.10.0 або пізніше.

  • Починаючи з версії 1.29, реалізація v1 KMS типово вимкнена. Щоб увімкнути функцію, встановіть --feature-gates=KMSv1=true, щоб налаштувати постачальника KMS v1.

  • Ваш кластер повинен використовувати etcd v3 або пізніше.

KMS v2

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.29 [stable]
  • Ваш кластер повинен використовувати etcd v3 або пізніше.

Шифрування KMS та ключі для кожного обʼєкта

Постачальник шифрування KMS використовує схему шифрування конвертів для шифрування даних в etcd. Дані шифруються за допомогою ключа шифрування даних (data encryption key — DEK). DEK шифруються за допомогою ключа шифрування ключа (key encryption key — KEK), який зберігається та обслуговується на віддаленому KMS.

Якщо ви використовуєте (застарілу) реалізацію v1 KMS, для кожного шифрування генерується новий DEK.

З KMS v2 для кожного шифрування генерується новий DEK: API-сервер використовує функцію похідного ключа для генерації ключів шифрування даних з одноразовим використанням секретного початкового елемента (seed — насіння) у поєднанні з випадковими даними. Ротація насіння відбувається при кожній зміні KEK (див. розділ Розуміння key_id та Ротація ключів нижче для докладніших відомостей).

Постачальник KMS використовує gRPC для звʼязку з певним втулком KMS через сокет UNIX-домену. Втулок KMS, який реалізований як сервер gRPC та розгорнутий на тих самих вузлах що і панель управління Kubernetes, відповідає за всі комунікації з віддаленим KMS.

Налаштування постачальника KMS

Для налаштування постачальника KMS на API-сервері включіть постачальника типу kms у масиві providers у файлі конфігурації шифрування і встановіть наступні властивості:

KMS v1

  • apiVersion: Версія API для постачальника KMS. Залиште це значення порожнім або встановіть його на v1.
  • name: Показує назву втулка KMS. Не може бути змінено після встановлення.
  • endpoint: Адреса прослуховування сервера gRPC (втулка KMS). Точка доступу — це сокет UNIX-домену.
  • cachesize: Кількість ключів шифрування даних (DEK), які будуть збережені в кеші у відкритому вигляді. Коли ключі DEK збережені в кеші, вони можуть бути використані без додаткового звертання до KMS; тоді як ключі DEK, які не збережені в кеші, потребують звертання до KMS для розшифрування.
  • timeout: Скільки часу kube-apiserver має чекати на відповідь від втулка kms, перш ніж сповістити про помилку (типово — 3 секунди).

KMS v2

  • apiVersion: Версія API для постачальника KMS. Встановіть це на v2.
  • name:Показує назву втулка KMS. Не може бути змінено після встановлення.
  • endpoint: Адреса прослуховування сервера gRPC (втулка KMS). Точка доступу — це сокет UNIX-домену.
  • timeout: Скільки часу kube-apiserver має чекати на відповідь від втулка kms, перш ніж сповістити про помилку (типово — 3 секунди).

KMS v2 не підтримує властивість cachesize. Всі ключі шифрування даних (DEK) будуть збережені в кеші у відкритому вигляді, як тільки сервер розкриє їх за допомогою звертання до KMS. Після збереження в кеші, ключі DEK можуть бути використані для виконання розшифрування нескінченно довго без звертання до KMS.

Дивіться Розуміння налаштування шифрування в спокої.

Реалізація втулка KMS

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

Увімкнення KMS, що підтримується вашим постачальником хмарних послуг

Зверніться до вашого постачальника хмарних послуг для отримання інструкцій щодо увімкнення втулка KMS, який він надає.

Розробка сервера gRPC втулка KMS

Ви можете розробити сервер gRPC втулка KMS, використовуючи файл шаблону, доступний для мови програмування Go. Для інших мов програмування ви можете використовувати файл proto для створення файлу шаблону, який ви можете використовувати для написання коду сервера gRPC.

KMS v1

  • Використовуючи Go: Використовуйте функції та структури даних у файлі шаблону: api.pb.go для написання коду сервера gRPC.

  • Використовуючи інших мов програмування: Використовуйте компілятор protoc з файлом proto: api.proto для генерації файлу шаблону для конкретної мови програмування.

KMS v2

  • Використовуючи Go: Надається високорівнева бібліотека для спрощення процесу. Низькорівневі реалізації можуть використовувати функції та структури даних у файлі шаблону: api.pb.go для написання коду сервера gRPC.

  • Використовуючи інших мов програмування: Використовуйте компілятор protoc з файлом proto: api.proto для генерації файлу шаблону для конкретної мови програмування.

Потім використовуйте функції та структури даних у файлі шаблону для написання коду сервера.

Примітки

KMS v1
  • Версія втулка kms: v1beta1

    У відповідь на виклик процедури Version сумісний втулок KMS повинен повернути v1beta1 як VersionResponse.version.

  • Версія повідомлення: v1beta1

    Усі повідомлення від постачальника KMS мають поле версії встановлене на v1beta1.

  • Протокол: UNIX доменний сокет (unix)

    Втулок реалізований як сервер gRPC, який слухає UNIX доменний сокет. Розгортання втулка повинно створити файл у файловій системі для запуску зʼєднання gRPC unix domain socket. API-сервер (клієнт gRPC) налаштований на постачальника KMS (сервер gRPC) за допомогою точки доступу UNIX доменного сокета для звʼязку з ним. Може використовуватися абстрактний Linux сокет, точка доступу якого починається з /@, наприклад, unix:///@foo. Слід бути обережним при використанні цього типу сокета, оскільки вони не мають концепції ACL (на відміну від традиційних файлових сокетів). Однак вони підпадають під простір імен мережі Linux, тому будуть доступні лише контейнерам у тому ж самому Podʼі, якщо не використовується мережа хосту.

KMS v2
  • Версія втулка KMS: v2

    У відповідь на віддалений виклик процедури Status сумісний втулок KMS повинен повернути свою версію сумісності KMS як StatusResponse.version. У цій відповіді на статус також повинен бути включений "ok" як StatusResponse.healthz і key_id (ідентифікатор KEK віддаленого KMS) як StatusResponse.key_id. Проєкт Kubernetes рекомендує зробити ваш втулок сумісним зі стабільним v2 API KMS. Kubernetes 1.31 також підтримує v2beta1 API для KMS; майбутні релізи Kubernetes, швидше за все, будуть продовжувати підтримувати цю бета-версію.

    API-сервер надсилає віддалений виклик процедури Status приблизно кожну хвилину, коли все в справному стані, і кожні 10 секунд, коли втулок не справний. Втулки повинні пильнувати за оптимізацією цього виклику, оскільки він буде під постійним навантаженням.

  • Шифрування

    Виклик процедури EncryptRequest надає текст для шифрування та UID для цілей логування. У відповіді повинен бути включений шифротекст, key_id для використаного KEK та, опціонально, будь-які метадані, які потрібні втулку KMS для допомоги в майбутніх викликах DecryptRequest (через поле annotations). Втулок повинен гарантувати, що будь-який різний текст призводить до різної відповіді (шифротекст, key_id, annotations).

    Якщо втулок повертає непорожній масив annotations, всі ключі масиву повинні бути повністю кваліфікованими доменними іменами, такими як example.com. Приклад використання annotations — {"kms.example.io/remote-kms-auditid":"<audit ID використаний віддаленим KMS>"}

    API-сервер не виконує виклик процедури EncryptRequest на високому рівні. Реалізації втулків повинні все ж старатися, щоб зберегти час очікування кожного запиту менше 100 мілісекунд.

  • Розшифрування

    Виклик процедури DecryptRequest надає (ciphertext, key_id, annotations) з EncryptRequest та UID для цілей логування. Як і очікується, це протилежне виклику EncryptRequest. Втулки повинні перевірити, що key_id є тим, що вони розуміють — вони не повинні намагатися розшифрувати дані, якщо вони не впевнені, що вони були зашифровані ними раніше.

    API-сервер може виконати тисячі викликів процедури DecryptRequest під час запуску для заповнення свого кешу перегляду. Таким чином, реалізації втулків повинні виконати ці виклики якнайшвидше, і повинні старатися зберегти час очікування кожного запиту менше 10 мілісекунд.

  • Розуміння key_id та Ротації ключа

    key_id є публічним, незасекреченим імʼям віддаленого KEK KMS, який в цей момент використовується. Він може бути зареєстрований під час регулярної роботи API-сервера та, отже, не повинен містити ніяких приватних даних. Реалізації втулків повинні використовувати хеш, щоб уникнути витоку будь-яких даних. Метрики KMS v2 пильнують за хешуванням цього значення перед показом його через точку доступу /metrics.

    API-сервер вважає, що key_id, отриманий з виклику процедури Status, є авторитетним. Отже, зміна цього значення сигналізує API-серверу, що віддалений KEK змінився, і дані, зашифровані старим KEK, повинні бути позначені як застарілі при виконанні операція запису без операції (як описано нижче). Якщо виклик процедури EncryptRequest повертає key_id, який відрізняється від Status, відповідь відкидається, і втулок вважається несправним. Таким чином, реалізації повинні гарантувати, що key_id, що повертається зі Status, буде таким самим, як той, що повертається з EncryptRequest. Крім того, втулки повинні забезпечити стабільність key_id і не допускати перемикання між значеннями (тобто під час ротації віддаленого KEK).

    Втулки не повинні повторно використовувати key_id, навіть у ситуаціях, коли раніше використаний віддалений KEK був відновлений. Наприклад, якщо втулок використовував key_id=A, перемикнувся на key_id=B, а потім повернувся до key_id=A — замість того, щоб повідомити key_id=A, втулок повинен повідомити якесь похідне значення, таке як key_id=A_001 або використовувати нове значення, наприклад key_id=C.

    Оскільки API-сервер опитує Status приблизно кожну хвилину, ротація key_id не є миттєвою. Крім того, API-сервер буде користуватися останнім дійсним станом протягом приблизно трьох хвилин. Таким чином, якщо користувач хоче здійснити пасивний підхід до міграції зберігання (тобто, чекаючи), він повинен запланувати міграцію на 3 + N + M хвилин після ротації віддаленого KEK (N — це час, необхідний для того, щоб втулок помітив зміну key_id, і M — бажаний буфер, щоб дозволити обробку змін конфігурації, рекомендується мінімальне значення M в пʼять хвилин). Зазначте, що для виконання ротації KEK перезапуск API-сервера не потрібен.

  • Протокол: UNIX доменний сокет (unix)

    Втулок реалізований як сервер gRPC, який слухає UNIX доменний сокет. Розгортання втулка повинно створити файл у файловій системі для запуску зʼєднання gRPC unix domain socket. API-сервер (клієнт gRPC) налаштований на постачальника KMS (сервер gRPC) за допомогою точки доступу UNIX доменного сокета для звʼязку з ним. Може використовуватися абстрактний Linux сокет, точка доступу якого починається з /@, наприклад, unix:///@foo. Слід бути обережним при використанні цього типу сокета, оскільки вони не мають концепції ACL (на відміну від традиційних файлових сокетів). Однак вони підпадають під простір імен мережі Linux, тому будуть доступні лише контейнерам у тому ж самому Podʼі, якщо не використовується мережа хосту.

Інтеграція втулка KMS з віддаленим KMS

Втулок KMS може спілкуватися з віддаленим KMS за допомогою будь-якого протоколу, який підтримується KMS. Усі дані конфігурації, включаючи облікові дані автентифікації, які використовує втулок KMS для спілкування з віддаленим KMS, зберігаються і керуються втулком KMS незалежно. Втулок KMS може кодувати шифротекст з додатковими метаданими, які можуть бути потрібні перед відправленням його в KMS для розшифрування (KMS v2 робить цей процес простішим, надаючи спеціальне поле annotations).

Розгортання втулка KMS

Переконайтеся, що втулок KMS працює на тих самих хостах, що і сервер(и) API Kubernetes.

Шифрування даних за допомогою постачальника KMS

Для шифрування даних виконайте наступні кроки:

  1. Створіть новий файл EncryptionConfiguration, використовуючи відповідні властивості для постачальника kms, щоб шифрувати ресурси, такі як Secrets та ConfigMaps. Якщо ви хочете зашифрувати розширення API, яке визначено у визначенні CustomResourceDefinition, ваш кластер повинен працювати на Kubernetes v1.26 або новіше.

  2. Встановіть прапорець --encryption-provider-config на kube-apiserver, щоб вказати місце розташування файлу конфігурації.

  3. Аргумент --encryption-provider-config-automatic-reload типу boolean визначає, чи слід автоматично перезавантажувати файл, встановлений за допомогою --encryption-provider-config, у разі зміни вмісту на диску.

  4. Перезапустіть свій API-сервер.

KMS v1

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - kms:
          name: myKmsPluginFoo
          endpoint: unix:///tmp/socketfile-foo.sock
          cachesize: 100
          timeout: 3s
      - kms:
          name: myKmsPluginBar
          endpoint: unix:///tmp/socketfile-bar.sock
          cachesize: 100
          timeout: 3s

KMS v2

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
      - configmaps
      - pandas.awesome.bears.example
    providers:
      - kms:
          apiVersion: v2
          name: myKmsPluginFoo
          endpoint: unix:///tmp/socketfile-foo.sock
          timeout: 3s
      - kms:
          apiVersion: v2
          name: myKmsPluginBar
          endpoint: unix:///tmp/socketfile-bar.sock
          timeout: 3s

Встановлення --encryption-provider-config-automatic-reload в true обʼєднує всі перевірки стану в одну точку доступу перевірки стану. Індивідуальні перевірки стану доступні тільки тоді, коли використовуються постачальники KMS v1 і конфігурація шифрування не перезавантажується автоматично.

Наступна таблиця містить підсумки точок доступу перевірки стану для кожної версії KMS:

Конфігурації KMSБез автоматичного перезавантаженняЗ автоматичним перезавантаженням
Тільки KMS v1Індивідуальні перевіркиОдна перевірка
Тільки KMS v2Одна перевіркаОдна перевірка
KMS v1 та KMS v2Індивідуальні перевіркиОдна перевірка
Без KMSНемаєОдна перевірка

Одна перевірка означає, що єдиною точкою доступу перевірки стану є /healthz/kms-providers.

Індивідуальні перевірки означає, що для кожного втулка KMS є асоційована точка доступу перевірки стану на основі його місця в конфігурації шифрування: /healthz/kms-provider-0, /healthz/kms-provider-1 тощо.

Ці шляхи точок доступу перевірки стану жорстко закодовані та генеруються/контролюються сервером. Індекси для індивідуальних перевірок відповідають порядку, у якому обробляється конфігурація шифрування KMS.

До виконання кроків, визначених у Забезпечення шифрування всіх секретів, список providers повинен закінчуватися постачальником identity: {}, щоб можна було читати незашифровані дані. Після шифрування всіх ресурсів постачальника identity слід видалити, щоб запобігти обробці незашифрованих даних сервером API.

Для отримання деталей про формат EncryptionConfiguration, будь ласка, перегляньте довідник API шифрування API сервера.

Перевірка того, що дані зашифровані

Коли шифрування даних у стані спокою правильно налаштовано, ресурси шифруються під час запису. Після перезапуску вашого kube-apiserver, будь-які новостворені або оновлені Secret чи інші типи ресурсів, налаштовані в EncryptionConfiguration, мають бути зашифровані при зберіганні. Щоб перевірити, ви можете використовувати програму командного рядка etcdctl для отримання вмісту ваших секретних даних.

  1. Створіть новий Secret з назвою secret1 в просторі імен default:

    kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
    
  2. Використовуючи командний рядок etcdctl, прочитайте цей Secret з etcd:

    ETCDCTL_API=3 etcdctl get /kubernetes.io/secrets/default/secret1 [...] | hexdump -C
    

    де [...] містить додаткові аргументи для підключення до сервера etcd.

  3. Переконайтеся, що збережений Secret починається з префікса k8s:enc:kms:v1: для KMS v1 або з префікса k8s:enc:kms:v2: для KMS v2, що вказує на те, що постачальник kms зашифрував результати даних.

  4. Перевірте, що Secret правильно розшифровується при отриманні через API:

    kubectl describe secret secret1 -n default
    

    Secret повинен містити mykey: mydata.

Забезпечення шифрування всіх секретів

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

Наведена нижче команда читає всі Secret, а потім оновлює їх для застосування шифрування на сервері. У разі помилки через конфліктний запис, повторіть команду. Для більших кластерів вам може знадобитися поділити Secret за просторами імен або написати скрипт для оновлення.

kubectl get secrets --all-namespaces -o json | kubectl replace -f -

Перехід від локального постачальника шифрування до постачальника KMS

Щоб перейти від локального постачальника шифрування до постачальника kms та перешифрувати всі секрети:

  1. Додайте постачальника kms як перший запис у файлі конфігурації, як показано в наступному прикладі.

    apiVersion: apiserver.config.k8s.io/v1
    kind: EncryptionConfiguration
    resources:
      - resources:
          - secrets
        providers:
          - kms:
              apiVersion: v2
              name: myKmsPlugin
              endpoint: unix:///tmp/socketfile.sock
          - aescbc:
              keys:
                - name: key1
                  secret: <BASE 64 ENCODED SECRET>
    
  2. Перезапустіть усі процеси kube-apiserver.

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

    kubectl get secrets --all-namespaces -o json | kubectl replace -f -
    

Що далі

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

2.40 - Використання CoreDNS для виявлення Service

Ця сторінка описує процес оновлення CoreDNS та як встановити CoreDNS замість kube-dns.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.9. Для перевірки версії введіть kubectl version.

Про CoreDNS

CoreDNS — це гнучкий, розширюваний DNS-сервер, який може обслуговувати DNS кластера Kubernetes. Як і Kubernetes, проєкт CoreDNS є проєктом CNCF.

Ви можете використовувати CoreDNS замість kube-dns у своєму кластері, замінивши kube-dns у наявному розгортанні, або використовуючи інструменти, такі як kubeadm, які встановлюють і оновлюють кластер за вас.

Встановлення CoreDNS

Для ручного розгортання або заміни kube-dns, дивіться документацію на вебсайті CoreDNS.

Міграція на CoreDNS

Оновлення наявного кластера за допомогою kubeadm

У версії Kubernetes 1.21, kubeadm припинив підтримку kube-dns як застосунку DNS. Для kubeadm v1.31, єдиний підтримуваний DNS застосунок кластера — це CoreDNS.

Ви можете перейти на CoreDNS, використовуючи kubeadm для оновлення кластера, який використовує kube-dns. У цьому випадку, kubeadm генерує конфігурацію CoreDNS ("Corefile") на основі ConfigMap kube-dns, зберігаючи конфігурації для stub доменів та сервера імен вище в ієрархії.

Оновлення CoreDNS

Ви можете перевірити версію CoreDNS, яку kubeadm встановлює для кожної версії Kubernetes на сторінці Версія CoreDNS у Kubernetes.

CoreDNS можна оновити вручну, якщо ви хочете тільки оновити CoreDNS або використовувати власний кастомізований образ. Є корисні рекомендації та посібник, доступні для забезпечення плавного оновлення. Переконайтеся, що поточна конфігурація CoreDNS ("Corefile") зберігається при оновленні вашого кластера.

Якщо ви оновлюєте свій кластер за допомогою інструменту kubeadm, kubeadm може самостійно зберегти поточну конфігурацію CoreDNS.

Налаштування CoreDNS

Коли використання ресурсів є проблемою, може бути корисним налаштувати конфігурацію CoreDNS. Для детальнішої інформації перевірте документацію зі збільшення масштабу CoreDNS.

Що далі

Ви можете налаштувати CoreDNS для підтримки багатьох інших сценаріїв, ніж kube-dns, змінивши конфігурацію CoreDNS ("Corefile"). Для отримання додаткової інформації дивіться документацію для втулка kubernetes CoreDNS, або читайте Власні DNS записи для Kubernetes в блозі CoreDNS.

2.41 - Використання NodeLocal DNSCache в кластерах Kubernetes

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.18 [stable]

Ця сторінка надає огляд функції NodeLocal DNSCache в Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Вступ

NodeLocal DNSCache поліпшує продуктивність кластерного DNS, запускаючи агента кешування DNS на вузлах кластера як DaemonSet. За поточної архітектури, Podʼи в режимі DNS ClusterFirst звертаються до serviceIP kube-dns для DNS-запитів. Вони переводяться в точку доступу kube-dns/CoreDNS за допомогою прав iptables, доданих kube-proxy. За цією новою архітектурою Podʼи звертаються до агента кешування DNS, що працює на тому ж вузлі, тим самим уникнувши прав iptables DNAT та відстеження зʼєднань. Локальний агент кешування буде запитувати службу kube-dns про пропуски кешування для імен кластера (типовий суфікс "cluster.local").

Мотивація

  • За поточної архітектури DNS, є можливість того, що Podʼи з найвищим QPS DNS повинні звертатися до іншого вузла, якщо на цьому вузлі немає локального екземпляра kube-dns/CoreDNS. Наявність локального кешу допоможе покращити час відгуку в таких сценаріях.

  • Пропускаючи iptables DNAT та відстеження зʼєднань допоможе зменшити перегони з відстеженням зʼєднань та уникнути заповнення таблиці conntrack UDP DNS-записами.

  • Зʼєднання від локального агента кешування до служби kube-dns можуть бути підвищені до TCP. Записи conntrack для TCP будуть видалені при закритті зʼєднання, на відміну від записів UDP, які мусять перейти у режим очікування (типово nf_conntrack_udp_timeout становить 30 секунд)

  • Перехід DNS-запитів з UDP до TCP зменшить хвостовий час відповіді, спричинений втратою пакетів UDP та зазвичай таймаутами DNS до 30 секунд (3 повтори + 10 секунд таймауту). Оскільки кеш NodeLocal прослуховує UDP DNS-запити, застосунки не потребують змін.

  • Метрики та видимість DNS-запитів на рівні вузла.

  • Відʼємне кешування можна повторно включити, тим самим зменшивши кількість запитів до служби kube-dns.

Діаграма архітектури

Це шлях, яким йдуть DNS-запити після ввімкнення NodeLocal DNSCache:

Потік NodeLocal DNSCache

Потік NodeLocal DNSCache

Ця картинка показує, як NodeLocal DNSCache обробляє DNS-запити.

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

Цю функцію можна ввімкнути за допомогою таких кроків:

  • Підготуйте маніфест, схожий на зразок nodelocaldns.yaml та збережіть його як nodelocaldns.yaml.

  • Якщо використовуєте IPv6, файл конфігурації CoreDNS потрібно закрити всі IPv6-адреси у квадратні дужки, якщо вони використовуються у форматі 'IP:Port'. Якщо ви використовуєте зразок маніфесту з попереднього пункту, це потребує зміни рядка конфігурації L70 таким чином: "health [__PILLAR__LOCAL__DNS__]:8080"

  • Підставте змінні в маніфест з правильними значеннями:

    kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}`
    domain=<cluster-domain>
    localdns=<node-local-address>
    

    <cluster-domain> типово "cluster.local". <node-local-address> — це локальна IP-адреса прослуховування, обрана для NodeLocal DNSCache.

    • Якщо kube-proxy працює в режимі IPTABLES:

      sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
      

      __PILLAR__CLUSTER__DNS__ та __PILLAR__UPSTREAM__SERVERS__ будуть заповнені під час роботи підсистеми node-local-dns. У цьому режимі підсистеми node-local-dns прослуховують як IP-адресу служби kube-dns, так і <node-local-address>, тому Podʼи можуть шукати записи DNS, використовуючи будь-яку з цих IP-адрес.

    • Якщо kube-proxy працює в режимі IPVS:

      sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/,__PILLAR__DNS__SERVER__//g; s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns.yaml
      

      У цьому режимі підсистеми node-local-dns прослуховують лише <node-local-address>. Інтерфейс node-local-dns не може привʼязати кластерну IP kube-dns, оскільки інтерфейс, що використовується для IPVS-балансування навантаження, вже використовує цю адресу. __PILLAR__UPSTREAM__SERVERS__ буде заповнено підсистемами node-local-dns.

  • Запустіть kubectl create -f nodelocaldns.yaml.

  • Якщо використовуєте kube-proxy в режимі IPVS, прапорець --cluster-dns для kubelet потрібно змінити на <node-local-address>, який прослуховує NodeLocal DNSCache. В іншому випадку не потрібно змінювати значення прапорця --cluster-dns, оскільки NodeLocal DNSCache прослуховує як IP-адресу служби kube-dns, так і <node-local-address>.

Після ввімкнення, Podʼи node-local-dns будуть працювати у просторі імен kube-system на кожному з вузлів кластера. Цей Pod працює з CoreDNS у режимі кешування, тому всі метрики CoreDNS, що надаються різними втулками, будуть доступні на рівні кожного вузла.

Цю функцію можна вимкнути, видаливши DaemonSet, використовуючи kubectl delete -f <manifest>. Також слід скасувати будь-які зміни, внесені до конфігурації kubelet.

Конфігурація StubDomains та Upstream серверів

StubDomains та upstream сервери, вказані в ConfigMap kube-dns в просторі імен kube-system автоматично використовуються підсистемами node-local-dns. Вміст ConfigMap повинен відповідати формату, показаному у прикладі. ConfigMap node-local-dns також можна змінити безпосередньо з конфігурацією stubDomain у форматі Corefile. Деякі хмарні постачальники можуть не дозволяти змінювати ConfigMap node-local-dns безпосередньо. У цьому випадку ConfigMap kube-dns можна оновити.

Налаштування обмежень памʼяті

Podʼи node-local-dns використовують памʼять для зберігання записів кешу та обробки запитів. Оскільки вони не стежать за обʼєктами Kubernetes, розмір кластера або кількість Service / EndpointSlice не впливає на використання памʼяті безпосередньо. Використання памʼяті впливає на шаблон DNS-запитів. З документації CoreDNS:

Типовий розмір кешу — 10000 записів, що використовує приблизно 30 МБ, коли повністю заповнений.

Це буде використання памʼяті для кожного блоку сервера (якщо кеш буде повністю заповнений). Використання памʼяті можна зменшити, вказавши менші розміри кешу.

Кількість одночасних запитів повʼязана з попитом памʼяті, оскільки кожен додатковий goroutine, використаний для обробки запиту, потребує певної кількості памʼяті. Ви можете встановити верхню межу за допомогою опції max_concurrent у втулку forward.

Якщо Pod node-local-dns намагається використовувати більше памʼяті, ніж доступно (через загальні системні ресурси або через налаштовані обмеження ресурсів), операційна система може вимкнути контейнер цього Podʼа. У разі цього, контейнер, якого вимкнено ("OOMKilled"), не очищає власні правила фільтрації пакетів, які він раніше додавав під час запуску. Контейнер node-local-dns повинен бути перезапущений (оскільки він керується як частина DaemonSet), але це призведе до короткої перерви у роботі DNS кожного разу, коли контейнер зазнає збою: правила фільтрації пакетів направляють DNS-запити до локального Podʼа, який не є справним.

Ви можете визначити прийнятне обмеження памʼяті, запустивши Podʼи node-local-dns без обмеження та вимірявши максимальне використання. Ви також можете налаштувати та використовувати VerticalPodAutoscaler у recommender mode, а потім перевірити його рекомендації.

2.42 - Використання sysctl в кластері Kubernetes

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.21 [stable]

У цьому документі описано, як налаштовувати та використовувати параметри ядра в межах кластера Kubernetes, використовуючи інтерфейс sysctl.

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

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

Для деяких кроків вам також потрібно мати змогу змінювати параметри командного рядка для kubelet, що працюють на вашому кластері.

Перелік всіх параметрів Sysctl

У Linux інтерфейс sysctl дозволяє адміністратору змінювати параметри ядра під час виконання. Параметри доступні через віртуальну файлову систему /proc/sys/. Параметри охоплюють різні підсистеми, такі як:

  • ядро (загальний префікс: kernel.)
  • мережа (загальний префікс: net.)
  • віртуальна памʼять (загальний префікс: vm.)
  • MDADM (загальний префікс: dev.)
  • Більше підсистем описано у документації ядра.

Щоб отримати список всіх параметрів, ви можете виконати команду:

sudo sysctl -a

Безпечні та Небезпечні Sysctl

Kubernetes класифікує sysctl як безпечні або небезпечні. Крім належного просторового розмежування, безпечний sysctl повинен бути належним чином ізольованим між Podʼами на тому ж вузлі. Це означає, що встановлення безпечного sysctl для одного Podʼа

  • не повинно мати жодного впливу на інші Podʼи на вузлі
  • не повинно дозволяти шкодити справності вузла
  • не повинно дозволяти отримувати CPU або ресурси памʼяті поза межами обмежень ресурсів Podʼа.

Наразі більшість просторово розмежованих (по просторах імен) sysctl не обовʼязково вважаються безпечними. До набору безпечних sysctl входять наступні параметри:

  • kernel.shm_rmid_forced;
  • net.ipv4.ip_local_port_range;
  • net.ipv4.tcp_syncookies;
  • net.ipv4.ping_group_range (починаючи з Kubernetes 1.18);
  • net.ipv4.ip_unprivileged_port_start (починаючи з Kubernetes 1.22);
  • net.ipv4.ip_local_reserved_ports (починаючи з Kubernetes 1.27, потрібне ядро 3.16+);
  • net.ipv4.tcp_keepalive_time (починаючи з Kubernetes 1.29, потрібне ядро 4.5+);
  • net.ipv4.tcp_fin_timeout (починаючи з Kubernetes 1.29, потрібне ядро 4.6+);
  • net.ipv4.tcp_keepalive_intvl (починаючи з Kubernetes 1.29, потрібне ядро 4.5+);
  • net.ipv4.tcp_keepalive_probes (починаючи з Kubernetes 1.29, потрібне ядро 4.5+).

Цей список буде розширюватися у майбутніх версіях Kubernetes, коли kubelet буде підтримувати кращі механізми ізоляції.

Увімкнення небезпечних Sysctl

Всі безпечні sysctl є типово увімкненими.

Всі небезпечні sysctl типово вимкнені та повинні бути дозволені вручну адміністратором кластера на кожному вузлі окремо. Podʼи з вимкненими небезпечними sysctl будуть заплановані, але їх не вдасться запустити.

З врахуванням попередження вище, адміністратор кластера може дозволити певні небезпечні sysctl для дуже спеціальних ситуацій, таких як налаштування високопродуктивних або застосунків реального часу. Небезпечні sysctl вмикаються на основі вузла з прапорцем kubelet; наприклад:

kubelet --allowed-unsafe-sysctls \
  'kernel.msg*,net.core.somaxconn' ...

Для Minikube, це можна зробити за допомогою прапорця extra-config:

minikube start --extra-config="kubelet.allowed-unsafe-sysctls=kernel.msg*,net.core.somaxconn"...

Таким чином можна увімкнути лише просторово розмежовані sysctl.

Налаштування Sysctl для Podʼа

Численні sysctl просторово розмежовані в сучасних ядрах Linux. Це означає, що їх можна налаштовувати незалежно для кожного Pod на вузлі. Лише просторово розмежовані sysctl можна налаштовувати через securityContext Podʼа в межах Kubernetes.

Відомо, що наступні sysctl мають просторове розмежування. Цей список може змінитися в майбутніх версіях ядра Linux.

  • kernel.shm*
  • kernel.msg*
  • kernel.sem
  • fs.mqueue.*
  • Ті net.*, які можна налаштувати в просторі імен мережі контейнера. Однак є винятки (наприклад, net.netfilter.nf_conntrack_max та net.netfilter.nf_conntrack_expect_max можуть бути налаштовані в просторі імен мережі контейнера, але не мають просторового розмежування до Linux 5.12.2).

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

Використовуйте securityContext Podʼа для налаштування просторово розмежованих sysctl. securityContext застосовується до всіх контейнерів у тому ж Podʼі.

У цьому прикладі використовується securityContext Podʼа для встановлення безпечного sysctl kernel.shm_rmid_forced та двох небезпечних sysctl net.core.somaxconn та kernel.msgmax. В специфікації немає розрізнення між безпечними та небезпечними sysctl.

apiVersion: v1
kind: Pod
metadata:
  name: sysctl-example
spec:
  securityContext:
    sysctls:
    - name: kernel.shm_rmid_forced
      value: "0"
    - name: net.core.somaxconn
      value: "1024"
    - name: kernel.msgmax
      value: "65536"
  ...

Хорошою практикою є вважати вузли з особливими налаштуваннями sysctl як позначені taint в межах кластера і планувати на них лише ті Podʼи, яким потрібні ці налаштування sysctl. Рекомендується використовувати функцію Заплямованість та Толерантність кластера Kubernetes, щоб реалізувати це.

Pod з небезпечними sysctl не вдасться запустити на будь-якому вузлі, на якому не були явно увімкнені ці два небезпечні sysctl. Як і з вузловими sysctl, рекомендується використовувати функцію Заплямованість та Толерантність або заплямованість вузлів для планування цих Podʼів на відповідні вузли.

2.43 - Використання NUMA-орієнтованого менеджера памʼяті

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.22 [beta]

Менеджер памʼяті Kubernetes дозволяє функцію гарантованого виділення памʼяті (та великих сторінок) для Podʼів QoS класу Guaranteed.

Менеджер памʼяті використовує протокол генерації підказок для вибору найбільш відповідної спорідненості NUMA для точки доступу. Менеджер памʼяті передає ці підказки центральному менеджеру (Менеджеру топології). На основі як підказок, так і політики Менеджера топології, Pod відхиляється або допускається на вузол.

Крім того, Менеджер памʼяті забезпечує, що памʼять, яку запитує Pod, виділяється з мінімальної кількості NUMA-вузлів.

Менеджер памʼяті має відношення тільки до хостів на базі Linux.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.21. Для перевірки версії введіть kubectl version.

Для вирівнювання ресурсів памʼяті з іншими запитаними ресурсами у специфікації Podʼа:

Починаючи з версії v1.22, Менеджер памʼяті типово увімкнено за допомогою функціональної можливості MemoryManager.

Для версій до v1.22, kubelet повинен бути запущений з наступним прапорцем:

--feature-gates=MemoryManager=true

щоб увімкнути функцію Менеджера памʼяті.

Як працює Менеджер памʼяті?

Зараз Менеджер памʼяті пропонує виділення гарантованої памʼяті (та великих сторінок) для Podʼів у класі QoS Guaranteed. Щоб негайно ввести Менеджер памʼяті в роботу, слідкуйте вказівкам у розділі Налаштування Менеджера памʼяті, а потім підготуйте та розгорніть Pod Guaranteed, як показано в розділі Розміщення Podʼів класу QoS Guaranteed.

Менеджер памʼяті є постачальником підказок і надає підказки топології для Менеджера топології, який потім вирівнює запитані ресурси згідно з цими підказками топології. Він також застосовує cgroups (тобто cpuset.mems) для Podʼів. Повна схематична діаграма щодо процесу допуску та розгортання Podʼа показано у Memory Manager KEP: Design Overview та нижче:

Memory Manager у процесі прийняття та розгортання Podʼа

Під час цього процесу Менеджер памʼяті оновлює свої внутрішні лічильники, що зберігаються в Node Map та Memory Maps, для управління гарантованим виділенням памʼяті.

Менеджер памʼяті оновлює Node Map під час запуску та виконання наступним чином.

Запуск

Це відбувається один раз, коли адміністратор вузла використовує --reserved-memory (розділ Прапорець зарезервованої памʼяті). У цьому випадку Node Map оновлюється, щоб відображати це резервування, як показано в Memory Manager KEP: Memory Maps at start-up (with examples).

Адміністратор повинен надати прапорець --reserved-memory при налаштуванні політики Static.

Робота

Посилання Memory Manager KEP: Memory Maps at runtime (with examples) ілюструє, як успішне розгортання Podʼа впливає на Node Map, і також повʼязано з тим, як потенційні випадки вичерпання памʼяті (OOM) далі обробляються Kubernetes або операційною системою.

Важливою темою в контексті роботи Менеджера памʼяті є керування NUMA-групами. Кожного разу, коли запит памʼяті Podʼа перевищує місткість одного NUMA-вузла, Менеджер памʼяті намагається створити групу, яка включає декілька NUMA-вузлів і має розширений обсяг памʼяті. Проблему вирішено, як про це йдеться у Memory Manager KEP: How to enable the guaranteed memory allocation over many NUMA nodes?. Також, посилання Memory Manager KEP: Simulation - how the Memory Manager works? (by examples) показує, як відбувається управління групами.

Налаштування Менеджера памʼяті

Інші Менеджери повинні бути спочатку попередньо налаштовані. Далі, функцію Менеджера памʼяті слід увімкнути та запустити з політикою Static (розділ Політика Static). Опційно можна зарезервувати певну кількість памʼяті для системи або процесів kubelet, щоб збільшити стабільність вузла (розділ Прапорець зарезервованої памʼяті).

Політики

Менеджер памʼяті підтримує дві політики. Ви можете вибрати політику за допомогою прапорця kubelet --memory-manager-policy:

  • None (типово)
  • Static

Політика None

Це типова політика і вона не впливає на виділення памʼяті жодним чином. Вона працює так само як і коли Менеджер памʼяті взагалі відсутній.

Політика None повертає типову підказку топології. Ця спеціальна підказка позначає, що Hint Provider (в цьому випадку Менеджер памʼяті) не має переваги щодо спорідненості NUMA з будь-яким ресурсом.

Політика Static

У випадку Guaranteed Podʼа політика Менеджера памʼяті Static повертає підказки топології, що стосуються набору NUMA-вузлів, де памʼять може бути гарантованою, та резервує памʼять, оновлюючи внутрішній обʼєкт NodeMap.

У випадку Podʼа BestEffort або Burstable політика Менеджера памʼяті Static повертає назад типову підказку топології, оскільки немає запиту на гарантовану памʼять, і не резервує памʼять внутрішнього обʼєкта NodeMap.

Прапорець зарезервованої памʼяті

Механізм виділення ресурсів вузла (Node Allocatable) зазвичай використовується адміністраторами вузлів для резервування системних ресурсів вузла K8S для kubelet або процесів операційної системи, щоб підвищити стабільність вузла. Для цього можна використовувати відповідний набір прапорців, щоб вказати загальну кількість зарезервованої памʼяті для вузла. Це попередньо налаштоване значення далі використовується для розрахунку реальної кількості "виділеної" памʼяті вузла, доступної для Podʼів.

Планувальник Kubernetes використовує "виділену" памʼять для оптимізації процесу планування Podʼів. Для цього використовуються прапорці --kube-reserved, --system-reserved та --eviction-threshold. Сума їх значень враховує загальну кількість зарезервованої памʼяті.

Новий прапорець --reserved-memory було додано до Memory Manager, щоб дозволити цю загальну зарезервовану памʼять розділити (адміністратором вузла) і відповідно зарезервувати для багатьох вузлів NUMA.

Прапорець визначається як розділений комами список резервування памʼяті різних типів на NUMA-вузол. Резервації памʼяті по кількох NUMA-вузлах можна вказати, використовуючи крапку з комою як роздільник. Цей параметр корисний лише в контексті функції Менеджера памʼяті. Менеджер памʼяті не використовуватиме цю зарезервовану памʼять для виділення контейнерних робочих навантажень.

Наприклад, якщо у вас є NUMA-вузол "NUMA0" з доступною памʼяттю 10 ГБ, і було вказано --reserved-memory, щоб зарезервувати 1 ГБ памʼяті в "NUMA0", Менеджер памʼяті припускає, що для контейнерів доступно тільки 9 ГБ.

Ви можете не вказувати цей параметр, але слід памʼятати, що кількість зарезервованої памʼяті з усіх NUMA-вузлів повинна дорівнювати кількості памʼяті, вказаній за допомогою функції виділення ресурсів вузла. Якщо принаймні один параметр виділення вузла не дорівнює нулю, вам слід вказати --reserved-memory принаймні для одного NUMA-вузла. Фактично, порогове значення eviction-hard типово становить 100 Mi, отже, якщо використовується політика Static, --reserved-memory є обовʼязковим.

Також слід уникати наступних конфігурацій:

  1. дублікатів, тобто того самого NUMA-вузла або типу памʼяті, але з іншим значенням;
  2. встановлення нульового ліміту для будь-якого типу памʼяті;
  3. ідентифікаторів NUMA-вузлів, які не існують в апаратному забезпеченні машини;
  4. назв типів памʼяті відмінних від memory або hugepages-<size> (великі сторінки певного розміру <size> також повинні існувати).

Синтаксис:

--reserved-memory N:memory-type1=value1,memory-type2=value2,...

  • N (ціле число) — індекс NUMA-вузла, наприклад 0
  • memory-type (рядок) — представляє тип памʼяті:
    • memory — звичайна памʼять
    • hugepages-2Mi або hugepages-1Gi — великі сторінки
  • value (рядок) - кількість зарезервованої памʼяті, наприклад 1Gi.

Приклад використання:

--reserved-memory 0:memory=1Gi,hugepages-1Gi=2Gi

або

--reserved-memory 0:memory=1Gi --reserved-memory 1:memory=2Gi

або

--reserved-memory '0:memory=1Gi;1:memory=2Gi'

При вказанні значень для прапорця --reserved-memory слід дотримуватися налаштування, яке ви вказали раніше за допомогою прапорців функції виділення вузла. Іншими словами, слід дотримуватися такого правила для кожного типу памʼяті:

sum(reserved-memory(i)) = kube-reserved + system-reserved + eviction-threshold,

де i - це індекс NUMA-вузла.

Якщо ви не дотримуєтесь вищезазначеної формули, Менеджер памʼяті покаже помилку при запуску.

Іншими словами, у вищезазначеному прикладі показано, що для звичайної памʼяті (type=memory) ми загалом резервуємо 3 ГБ, а саме:

sum(reserved-memory(i)) = reserved-memory(0) + reserved-memory(1) = 1 ГБ + 2 ГБ = 3 ГБ

Приклад командних аргументів kubelet, що стосуються конфігурації виділення вузла:

  • --kube-reserved=cpu=500m,memory=50Mi
  • --system-reserved=cpu=123m,memory=333Mi
  • --eviction-hard=memory.available<500Mi

Нижче наведено приклад правильної конфігурації:

--feature-gates=MemoryManager=true
--kube-reserved=cpu=4,memory=4Gi
--system-reserved=cpu=1,memory=1Gi
--memory-manager-policy=Static
--reserved-memory '0:memory=3Gi;1:memory=2148Mi'

Перевірмо цю конфігурацію:

  1. kube-reserved + system-reserved + eviction-hard(за замовчуванням) = reserved-memory(0) + reserved-memory(1)
  2. 4GiB + 1GiB + 100MiB = 3GiB + 2148MiB
  3. 5120MiB + 100MiB = 3072MiB + 2148MiB
  4. 5220MiB = 5220MiB (що є правильним)

Розміщення Podʼа в класі QoS Guaranteed

Якщо вибрана політика відрізняється від None, Менеджер памʼяті ідентифікує Podʼи, які належать до класу обслуговування Guaranteed. Менеджер памʼяті надає конкретні підказки топології Менеджеру топології для кожного Podʼа з класом обслуговування Guaranteed. Для Podʼів, які належать до класу обслуговування відмінного від Guaranteed, Менеджер памʼяті надає Менеджеру топології типові підказки топології.

Наведені нижче уривки з маніфестів Podʼа призначають Pod до класу обслуговування Guaranteed.

Pod з цілим значенням CPU працює в класі обслуговування Guaranteed, коли requests дорівнюють limits:

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"

Також Pod, який спільно використовує CPU, працює в класі обслуговування Guaranteed, коли requests дорівнюють limits.

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"

Зверніть увагу, що для Podʼа потрібно вказати як запити CPU, так і памʼяті, щоб він належав до класу обслуговування Guaranteed.

Розвʼязання проблем

Для виявлення причин, чому Pod не вдалося розгорнути або він був відхилений на вузлі, можуть бути використані наступні засоби:

  • статус Podʼа — вказує на помилки топологічної спорідненості
  • системні логи — містять цінну інформацію для налагодження, наприклад, про згенеровані підказки
  • файл стану — вивід внутрішнього стану Менеджера памʼяті (включає Node Map та Memory Map)
  • починаючи з v1.22, можна використовувати API втулка ресурсів пристроїв, щоб отримати інформацію про памʼять, зарезервовану для контейнерів.

Статус Podʼа (TopologyAffinityError)

Ця помилка зазвичай виникає у наступних ситуаціях:

  • вузол не має достатньо ресурсів, щоб задовольнити запит Podʼа
  • запит Podʼа відхилено через обмеження певної політики Менеджера топології

Помилка показується у статусі Podʼа:

kubectl get pods
NAME         READY   STATUS                  RESTARTS   AGE
guaranteed   0/1     TopologyAffinityError   0          113s

Використовуйте kubectl describe pod <id> або kubectl get events, щоб отримати докладне повідомлення про помилку:

Warning  TopologyAffinityError  10m   kubelet, dell8  Resources cannot be allocated with Topology locality

Системні логи

Шукайте системні логи для певного Podʼа.

У логах можна знайти набір підказок, які згенерував Менеджер памʼяті для Podʼа. Також у логах повинен бути присутній набір підказок, згенерований Менеджером CPU.

Менеджер топології обʼєднує ці підказки для обчислення єдиної найкращої підказки. Найкраща підказка також повинна бути присутня в логах.

Найкраща підказка вказує, куди виділити всі ресурси. Менеджер топології перевіряє цю підказку за своєю поточною політикою і, залежно від вердикту, або допускає Pod до вузла, або відхиляє його.

Також шукайте логи для випадків, повʼязаних з Менеджером памʼяті, наприклад, для отримання інформації про оновлення cgroups та cpuset.mems.

Аналіз стану менеджера памʼяті на вузлі

Спочатку розгляньмо розгорнутий зразок Guaranteed Podʼа, специфікація якого виглядає так:

apiVersion: v1
kind: Pod
metadata:
  name: guaranteed
spec:
  containers:
  - name: guaranteed
    image: consumer
    imagePullPolicy: Never
    resources:
      limits:
        cpu: "2"
        memory: 150Gi
      requests:
        cpu: "2"
        memory: 150Gi
    command: ["sleep","infinity"]

Потім увійдімо на вузол, де він був розгорнутий, і розглянемо файл стану у /var/lib/kubelet/memory_manager_state:

{
   "policyName":"Static",
   "machineState":{
      "0":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":134987354112,
               "systemReserved":3221225472,
               "allocatable":131766128640,
               "reserved":131766128640,
               "free":0
            }
         },
         "nodes":[
            0,
            1
         ]
      },
      "1":{
         "numberOfAssignments":1,
         "memoryMap":{
            "hugepages-1Gi":{
               "total":0,
               "systemReserved":0,
               "allocatable":0,
               "reserved":0,
               "free":0
            },
            "memory":{
               "total":135286722560,
               "systemReserved":2252341248,
               "allocatable":133034381312,
               "reserved":29295144960,
               "free":103739236352
            }
         },
         "nodes":[
            0,
            1
         ]
      }
   },
   "entries":{
      "fa9bdd38-6df9-4cf9-aa67-8c4814da37a8":{
         "guaranteed":[
            {
               "numaAffinity":[
                  0,
                  1
               ],
               "type":"memory",
               "size":161061273600
            }
         ]
      }
   },
   "checksum":4142013182
}

З цього файлу стану можна дізнатись, що Pod був привʼязаний до обох NUMA вузлів, тобто:

"numaAffinity":[
   0,
   1
],

Термін "привʼязаний" означає, що споживання памʼяті Podʼом обмежено (через конфігурацію cgroups) цими NUMA вузлами.

Це автоматично означає, що Менеджер памʼяті створив нову групу, яка обʼєднує ці два NUMA вузли, тобто вузли з індексами 0 та 1.

Зверніть увагу, що управління групами виконується досить складним способом, і подальші пояснення надані в Memory Manager KEP в цьому та цьому розділах.

Для аналізу ресурсів памʼяті, доступних у групі, потрібно додати відповідні записи з NUMA вузлів, які належать до групи.

Наприклад, загальна кількість вільної "звичайної" памʼяті в групі може бути обчислена шляхом додавання вільної памʼяті, доступної на кожному NUMA вузлі в групі, тобто в розділі "memory" NUMA вузла 0 ("free":0) та NUMA вузла 1 ("free":103739236352). Таким чином, загальна кількість вільної "звичайної" памʼяті в цій групі дорівнює 0 + 103739236352 байт.

Рядок "systemReserved":3221225472 вказує на те, що адміністратор цього вузла зарезервував 3221225472 байти (тобто 3Gi) для обслуговування процесів kubelet та системи на NUMA вузлі 0, використовуючи прапорець --reserved-memory.

API втулка ресурсів пристроїв

Kubelet надає службу gRPC PodResourceLister для включення виявлення ресурсів та повʼязаних метаданих. Використовуючи його точку доступу List gRPC, можна отримати інформацію про зарезервовану памʼять для кожного контейнера, яка міститься у protobuf повідомленні ContainerMemory. Цю інформацію можна отримати лише для Podʼів у класі якості обслуговування Guaranteed.

Що далі

2.44 - Перевірка підписаних артефактів Kubernetes

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [beta]

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

Вам знадобиться мати встановлені наступні інструменти:

Перевірка підписів бінарних файлів

В процесі підготовки випуску Kubernetes підписує всі бінарні артефакти (tarballs, файли SPDX, окремі бінарні файли) за допомогою безключового підписування cosign. Щоб перевірити певний бінарний файл, отримайте його разом з підписом та сертифікатом:

URL=https://dl.k8s.io/release/v1.31.0/bin/linux/amd64
BINARY=kubectl

FILES=(
    "$BINARY"
    "$BINARY.sig"
    "$BINARY.cert"
)

for FILE in "${FILES[@]}"; do
    curl -sSfL --retry 3 --retry-delay 3 "$URL/$FILE" -o "$FILE"
done

Потім перевірте блоб, використовуючи cosign verify-blob:

cosign verify-blob "$BINARY" \
  --signature "$BINARY".sig \
  --certificate "$BINARY".cert \
  --certificate-identity krel-staging@k8s-releng-prod.iam.gserviceaccount.com \
  --certificate-oidc-issuer https://accounts.google.com

Перевірка підписів образів

Для повного списку образів, які підписані, дивіться Випуски.

Оберіть один образ з цього списку та перевірте його підпис, використовуючи команду cosign verify:

cosign verify registry.k8s.io/kube-apiserver-amd64:v1.31.0 \
  --certificate-identity krel-trust@k8s-releng-prod.iam.gserviceaccount.com \
  --certificate-oidc-issuer https://accounts.google.com \
  | jq .

Перевірка образів для всіх компонентів панелі управління

Щоб перевірити всі підписані образи компонентів панелі управління для останньої стабільної версії (v1.31.0), запустіть наступні команди:

curl -Ls "https://sbom.k8s.io/$(curl -Ls https://dl.k8s.io/release/stable.txt)/release" \
  | grep "SPDXID: SPDXRef-Package-registry.k8s.io" \
  | grep -v sha256 | cut -d- -f3- | sed 's/-/\//' | sed 's/-v1/:v1/' \
  | sort > images.txt
input=images.txt
while IFS= read -r image
do
  cosign verify "$image" \
    --certificate-identity krel-trust@k8s-releng-prod.iam.gserviceaccount.com \
    --certificate-oidc-issuer https://accounts.google.com \
    | jq .
done < "$input"

Після перевірки образу можна вказати його за його дайджестом у вашому маніфесті Podʼа, як у цьому прикладі:

registry-url/image-name@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2

Для отримання додаткової інформації, див. Розділ Політика отримання образів.

Перевірка підписів образів за допомогою контролера допуску

Для образів, що не є частиною компонентів панелі управління (наприклад, образ conformance), підписи також можна перевірити під час розгортання за допомогою контролера допуску sigstore policy-controller.

Ось кілька корисних ресурсів для початку роботи з policy-controller:

Перевірка специфікації на програмне забезпечення

Ви можете перевірити Kubernetes Software Bill of Materials (SBOM), використовуючи сертифікат та підпис sigstore, або відповідні файли SHA:

# Отримайте останню доступну версію релізу Kubernetes
VERSION=$(curl -Ls https://dl.k8s.io/release/stable.txt)

# Перевірте суму SHA512
curl -Ls "https://sbom.k8s.io/$VERSION/release" -o "$VERSION.spdx"
echo "$(curl -Ls "https://sbom.k8s.io/$VERSION/release.sha512") $VERSION.spdx" | sha512sum --check

# Перевірте суму SHA256
echo "$(curl -Ls "https://sbom.k8s.io/$VERSION/release.sha256") $VERSION.spdx" | sha256sum --check

# Отримайте підпис та сертифікат sigstore
curl -Ls "https://sbom.k8s.io/$VERSION/release.sig" -o "$VERSION.spdx.sig"
curl -Ls "https://sbom.k8s.io/$VERSION/release.cert" -o "$VERSION.spdx.cert"

# Перевірте підпис sigstore
cosign verify-blob \
    --certificate "$VERSION.spdx.cert" \
    --signature "$VERSION.spdx.sig" \
    --certificate-identity krel-staging@k8s-releng-prod.iam.gserviceaccount.com \
    --certificate-oidc-issuer https://accounts.google.com \
    "$VERSION.spdx"

3 - Налаштування Podʼів та контейнерів

Виконайте загальні завдання конфігурації для Pod і контейнерів.

3.1 - Виділення ресурсів памʼяті для контейнерів та Podʼів

Ця сторінка показує, як вказати запити та ліміти памʼяті для контейнерів. Контейнери гарантовано матимуть стільки памʼяті, скільки вказано у запиті, і не отримають більше, ніж вказано у ліміті.

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

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

Для перевірки версії введіть kubectl version.

Кожен вузол у вашому кластері повинен мати принаймні 300 МіБ памʼяті.

Деякі кроки на цій сторінці вимагають запуску служби metrics-server у вашому кластері. Якщо у вас запущено metrics-server, ви можете пропустити ці кроки.

Якщо ви використовуєте Minikube, виконайте таку команду, щоб увімкнути metrics-server:

minikube addons enable metrics-server

Щоб перевірити, чи запущений metrics-server, або інший постачальник API ресурсів метрик (metrics.k8s.io), виконайте таку команду:

kubectl get apiservices

Якщо API ресурсів метрик доступне, у виводі буде міститися посилання на metrics.k8s.io.

NAME
v1beta1.metrics.k8s.io

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були відокремлені від інших ресурсів у вашому кластері.

kubectl create namespace mem-example

Визначення запитів та лімітів памʼяті

Щоб вказати запит памʼяті для Контейнера, включіть поле resources:requests у маніфесті ресурсів Контейнера. Для вказівки ліміти памʼяті включіть resources:limits.

У цьому завданні ви створюєте Pod, який має один Контейнер. У Контейнера вказано запит памʼяті 100 МіБ і ліміти памʼяті 200 МіБ. Ось файл конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

Розділ args у файлі конфігурації надає аргументи для Контейнера при його запуску. Аргументи "--vm-bytes", "150M" вказують Контейнеру спробувати виділити 150 МіБ памʼяті.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit.yaml --namespace=mem-example

Перевірте, що Контейнер Podʼа працює:

kubectl get pod memory-demo --namespace=mem-example

Перегляньте детальну інформацію про Pod:

kubectl get pod memory-demo --output=yaml --namespace=mem-example

Вивід показує, що один Контейнер у Podʼі має запит памʼяті 100 МіБ та ліміти памʼяті 200 МіБ.

...
resources:
  requests:
    memory: 100Mi
  limits:
    memory: 200Mi
...

Виконайте команду kubectl top, щоб отримати метрики для Podʼа:

kubectl top pod memory-demo --namespace=mem-example

Вивід показує, що Pod використовує приблизно 162,900,000 байт памʼяті, що становить близько 150 МіБ. Це більше, ніж запит Podʼа на 100 МіБ, але в межах ліміти Podʼа на 200 МіБ.

NAME                        CPU(cores)   MEMORY(bytes)
memory-demo                 <something>  162856960

Видаліть свій Pod:

kubectl delete pod memory-demo --namespace=mem-example

Перевищення лімітів памʼяті Контейнера

Контейнер може перевищити свій запит на памʼять, якщо на вузлі є вільна памʼять. Але Контейнер не може використовувати памʼяті більше, ніж його ліміт памʼяті. Якщо Контейнер використовує більше памʼяті, ніж його ліміт, Контейнер стає кандидатом на зупинку роботи. Якщо Контейнер продовжує використовувати памʼять поза своїм лімітом, він зупиняється. Якщо Контейнер може бути перезапущений, kubelet перезапускає його, як із будь-яким іншим типом відмови під час роботи.

У цьому завданні ви створюєте Pod, який намагається виділити більше памʼяті, ніж його ліміт. Ось файл конфігурації для Podʼа, який має один Контейнер з запитом памʼяті 50 МіБ та лімітом памʼяті 100 МіБ:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-2-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

У розділі args файлу конфігурації видно, що Контейнер спробує використати 250 МіБ памʼяті, що значно перевищує ліміт в 100 МіБ.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-2.yaml --namespace=mem-example

Перегляньте детальну інформацію про Pod:

kubectl get pod memory-demo-2 --namespace=mem-example

На цей момент Контейнер може працювати або бути знищеним. Повторіть попередню команду, доки Контейнер не буде знищено:

NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          24s

Отримайте більш детальний огляд стану Контейнера:

kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example

Вивід показує, що Контейнер був знищений через вичерпання памʼяті (OOM):

lastState:
   terminated:
     containerID: 65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
     exitCode: 137
     finishedAt: 2017-06-20T20:52:19Z
     reason: OOMKilled
     startedAt: null

Контейнер у цьому завданні може бути перезапущений, тому kubelet перезапускає його. Повторіть цю команду кілька разів, щоб переконатися, що Контейнер постійно знищується та перезапускається:

kubectl get pod memory-demo-2 --namespace=mem-example

Вивід показує, що Контейнер знищується, перезапускається, знищується знову, знову перезапускається і так далі:

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          37s
kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-2   1/1       Running   2          40s

Перегляньте детальну інформацію про історію Podʼа:

kubectʼ describe pod лімітомo-2 ʼ-namespace=mem-example

Вивід показує, що Контейнер починається і знову не вдається:

... Normal  Created   Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff   Back-off restarting failed container

Перегляньте детальну інформацію про вузли вашого кластера:

kubectl describe nodes

Вивід містить запис про знищення Контейнера через умову вичерпання памʼяті:

Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child

Видаліть свій Pod:

kubectl delete pod memory-demo-2 --namespace=mem-example

Визначення запиту памʼяті, що є завеликим для вашого вузла

Запити та ліміт памʼяті повʼязані з Контейнерами, але корисно думати про Pod як про елемент, що має запит та ліміт памʼяті. Запит памʼяті для Podʼа — це сума запитів памʼяті для всіх Контейнерів у Podʼі. Аналогічно, ліміт памʼяті для Podʼа — це сума лімітів всіх Контейнерів у Podʼі.

Планування Podʼа ґрунтується на запитах. Pod планується кзапуску на вузлі лише у разі, якщо вузол має достатньо вільної памʼяті, щоб задовольнити запит памʼяті Podʼа.

У цьому завданні ви створюєте Pod, у якого запит памʼяті настільки великий, що перевищує можливості будь-якого вузла у вашому кластері. Ось файл конфігурації для Podʼа, у якого один Контейнер з запитом на 1000 ГіБ памʼяті, що, ймовірно, перевищує можливості будь-якого вузла у вашому кластері.

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-3-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "1000Gi"
      limits:
        memory: "1000Gi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-3.yaml --namespace=mem-example

Перегляньте статус Podʼа:

kubectl get pod memory-demo-3 --namespace=mem-example

Вивід показує, що статус Podʼа — PENDING. Це означає, що Pod не заплановано для запуску на жодному вузлі, і він залишатиметься у стані PENDING безстроково:

kubectl get pod memory-demo-3 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-3   0/1       Pending   0          25s

Перегляньте детальну інформацію про Pod, включаючи події:

kubectl describe pod memory-demo-3 --namespace=mem-example

Вивід показує, що Контейнер не може бути запланований через нестачу памʼяті на вузлах:

Events:
  ...  Reason            Message
       ------            -------
  ...  FailedScheduling  No nodes are available that match all of the following predicates:: Insufficient memory (3).

Одиниці памʼяті

Ресурс памʼяті вимірюється у байтах. Ви можете виразити памʼять як ціле число або ціле число з одним із наступних суфіксів: E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki. Наприклад, наступні значення приблизно однакові:

128974848, 129e6, 129M, 123Mi

Видаліть свій Pod:

kubectl delete pod memory-demo-3 --namespace=mem-example

Якщо ви не вказуєте ліміт памʼяті

Якщо ви не вказуєте ліміт памʼяті для Контейнера, відбувається одне з наступного:

  • Контейнер не має верхньої межі на кількість використаної памʼяті. Контейнер може використовувати всю доступну памʼять на вузлі, де він працює, що, своєю чергою, може спричинити активацію "OOM Killer". Крім того, в разі активації "OOM Kill" Контейнер без обмежень ресурсів матиме більше шансів на знищення.

  • Контейнер працює в просторі імен, який має стандартний ліміт памʼяті, і Контейнер автоматично отримує визначений стандартний ліміт. Адміністратори кластера можуть використовувати LimitRange для зазначення стандартного ліміту памʼяті.

Для чого вказувати запит та ліміт памʼяті

Налаштовуючи запити та ліміти памʼяті для Контейнерів, які працюють у вашому кластері, ви можете ефективно використовувати ресурси памʼяті, доступні на вузлах вашого кластера. Знижуючи запит памʼяті для Podʼа, ви даєте Podʼу хороший шанс на планування. Маючи ліміти памʼяті, який перевищує запит памʼяті, ви досягаєте двох цілей:

  • Pod може мати періоди сплеску активності, коли він використовує доступну памʼять.
  • Обсяг памʼяті, який Pod може використовувати під час сплесків, обмежений до якогось розумного значення.

Очищення

Видаліть простір імен. Це видалить всі Podʼи, які ви створили для цього завдання:

kubectl delete namespace mem-example

Що далі

Для розробників застосунків

Для адміністраторів кластера

3.2 - Виділення ресурсів CPU контейнерам та Podʼам

Ця сторінка показує, як вказати запит та ліміт CPU для контейнера. Контейнери не можуть використовувати більше CPU, ніж налаштований ліміт. При наявності вільного часу процесора контейнера гарантується виділення стільки CPU, скільки він запитує.

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

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

Для перевірки версії введіть kubectl version.

Кожен вузол у вашому кластері повинен мати принаймні 1 CPU.

Деякі кроки на цій сторінці вимагають запуску служби metrics-server у вашому кластері. Якщо у вас запущено metrics-server, ви можете пропустити ці кроки.

Якщо ви використовуєте Minikube, виконайте таку команду, щоб увімкнути metrics-server:

minikube addons enable metrics-server

Щоб перевірити, чи запущений metrics-server, або інший постачальник API ресурсів метрик (metrics.k8s.io), виконайте таку команду:

kubectl get apiservices

Якщо API ресурсів метрик доступне, у виводі буде міститися посилання на metrics.k8s.io.

NAME
v1beta1.metrics.k8s.io

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були відокремлені від інших ресурсів у вашому кластері.

kubectl create namespace cpu-example

Визначте запит ЦП та ліміт ЦП

Для вказання запиту ЦП для контейнера включіть поле resources:requests в маніфест ресурсів Контейнера. Щоб вказати ліміт ЦП, включіть resources:limits.

У цьому завданні ви створюєте Pod, у якого є один контейнер. Контейнер має запит на 0,5 CPU та ліміт 1 CPU. Ось файл конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr
    image: vish/stress
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"

Секція args у файлі конфігурації надає аргументи для контейнера при його запуску. Аргумент -cpus "2" каже Контейнеру спробувати використовувати 2 CPU.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit.yaml --namespace=cpu-example

Перевірте, що Pod працює:

kubectl get pod cpu-demo --namespace=cpu-example

Перегляньте детальну інформацію про Pod:

kubectl get pod cpu-demo --output=yaml --namespace=cpu-example

Вивід показує, що один контейнер у Podʼі має запит ЦП 500 міліCPU та ліміт ЦП 1 CPU.

resources:
  limits:
    cpu: "1"
  requests:
    cpu: 500m

Використовуйте kubectl top, щоб отримати метрики для Podʼа:

kubectl top pod cpu-demo --namespace=cpu-example

У цьому прикладі виводу показано, що Pod використовує 974 міліCPU, що незначно менше, ніж ліміт 1 CPU, вказане в конфігурації Podʼа.

NAME                        CPU(cores)   MEMORY(bytes)
cpu-demo                    974m         <something>

Нагадуємо, що, встановивши -cpu "2", ви налаштували контейнер на спробу використати 2 CPU, але контейнер може використовувати лише близько 1 CPU. Використання ЦП контейнером обмежується, оскільки контейнер намагається використовувати більше ресурсів ЦП, ніж його ліміт.

Одиниці ЦП

Ресурс ЦП вимірюється в одиницях ЦП. Одна одиниця ЦП в Kubernetes еквівалентна:

  • 1 AWS vCPU
  • 1 GCP Core
  • 1 Azure vCore
  • 1 Hyperthread на процесорі Intel з гіперпотоками

Дозволені дробові значення. Контейнер, який запитує 0,5 ЦП, гарантовано отримує половину ЦП порівняно з контейнером, який запитує 1 ЦП. Ви можете використовувати суфікс m для позначення мілі. Наприклад, 100m ЦП, 100 міліЦП і 0,1 ЦП — це все одне й те саме. Точність, більша за 1m, не допускається.

ЦП завжди запитується як абсолютна кількість, ніколи як відносна кількість; 0.1 — це та сама кількість ЦП на одноядерному, двоядерному або 48-ядерному компʼютері.

Видаліть свій Pod:

kubectl delete pod cpu-demo --namespace=cpu-example

Визначте запит ЦП, який перевищує можливості ваших вузлів

Запити та ліміти ЦП повʼязані з контейнерами, але корисно вважати Pod таким, що має запит ЦП та ліміти. Запит ЦП для Podʼа — це сума запитів ЦП для всіх контейнерів у Podʼі. Так само, ліміти ЦП для Podʼа — це сума обмежень ЦП для всіх контейнерів у Podʼі.

Планування Podʼа базується на запитах. Pod буде запланований для запуску на вузлі тільки у випадку, якщо на вузлі є достатньо ресурсів ЦП для задоволення запиту ЦП Podʼа.

У цьому завданні ви створюєте Pod, який має запит ЦП такий великий, що він перевищує можливості будь-якого вузла у вашому кластері. Ось файл конфігурації для Podʼа, який має один контейнер. Контейнер запитує 100 ЦП, що ймовірно перевищить можливості будь-якого вузла у вашому кластері.

apiVersion: v1
kind: Pod
metadata:
  name: cpu-demo-2
  namespace: cpu-example
spec:
  containers:
  - name: cpu-demo-ctr-2
    image: vish/stress
    resources:
      limits:
        cpu: "100"
      requests:
        cpu: "100"
    args:
    - -cpus
    - "2"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/cpu-request-limit-2.yaml --namespace=cpu-example

Перегляньте статус Podʼа:

kubectl get pod cpu-demo-2 --namespace=cpu-example

Вивід показує, що статус Podʼа — Pending. Тобто Pod не був запланований для запуску на будь-якому вузлі, і він буде залишатися в стані Pending нескінченно:

NAME         READY     STATUS    RESTARTS   AGE
cpu-demo-2   0/1       Pending   0          7m

Перегляньте детальну інформацію про Pod, включаючи події:

kubectl describe pod cpu-demo-2 --namespace=cpu-example

Вивід показує, що контейнер не може бути запланований через недостатні ресурси ЦП на вузлах:

Events:
  Reason                        Message
  ------                        -------
  FailedScheduling      No nodes are available that match all of the following predicates:: Insufficient cpu (3).

Видаліть свій Pod:

kubectl delete pod cpu-demo-2 --namespace=cpu-example

Якщо ви не вказуєте ліміт ЦП

Якщо ви не вказуєте ліміт ЦП для контейнера, то застосовується одне з наступного:

  • Контейнер не має верхньої межі ресурсів ЦП, які він може використовувати. Контейнер може використовувати всі доступні ресурси ЦП на вузлі, на якому він працює.

  • Контейнер працює в просторі імен, який має стандартний ліміт ЦП, і контейнеру автоматично призначається цей стандартний ліміт. Адміністратори кластера можуть використовувати LimitRange для зазначення стандартних значень лімітів ЦП.

Якщо ви вказуєте ліміт ЦП, але не вказуєте запит ЦП

Якщо ви вказуєте ліміт ЦП для контейнера, але не вказуєте запит ЦП, Kubernetes автоматично призначає запит ЦП, який збігається з лімітом. Аналогічно, якщо контейнер вказує свій власний ліміт памʼяті, але не вказує запит памʼяті, Kubernetes автоматично призначає запит памʼяті, який збігається з лімітом.

Для чого вказувати запит та ліміт ЦП

Налаштувавши запити та ліміти ЦП контейнерів, що працюють у вашому кластері, ви можете ефективно використовувати доступні ресурси ЦП на вузлах вашого кластера. Зберігаючи низький запит ЦП для Podʼа, ви забезпечуєте хороші шанси на його планування. Маючи ліміти ЦП, який перевищує запит ЦП, ви досягаєте двох речей:

  • Pod може мати періоди підвищеної активності, коли він використовує доступні ресурси ЦП.
  • Кількість ресурсів ЦП, які Pod може використовувати під час такої активності, обмежена розумною кількістю.

Очищення

Видаліть ваш простір імен:

kubectl delete namespace cpu-example

Що далі

Для розробників застосунків

Для адміністраторів кластерів

3.3 - Налаштування GMSA для Windows Podʼів та контейнерів

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.18 [stable]

Ця сторінка показує, як налаштувати Group Managed Service Accounts (GMSA) для Podʼів та контейнерів, які будуть запущені на вузлах Windows. Group Managed Service Accounts це специфічний тип облікового запису Active Directory, який забезпечує автоматичне управління паролями, спрощене управління іменами служб (SPN) та можливість делегування управління іншим адміністраторам на кількох серверах.

У Kubernetes специфікації облікових даних GMSA налаштовуються на рівні кластера Kubernetes як Custom Resources. Windows Podʼи, також як і окремі контейнери в Podʼі, можуть бути налаштовані для використання GMSA для доменних функцій (наприклад, автентифікація Kerberos) при взаємодії з іншими службами Windows.

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

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

Встановлення CRD для GMSACredentialSpec

CustomResourceDefinition(CRD) для ресурсів специфікації облікових даних GMSA потрібно налаштувати на кластері, щоб визначити тип настроюваного ресурсу GMSACredentialSpec. Завантажте GMSA CRD YAML і збережіть як gmsa-crd.yaml. Далі встановіть CRD за допомогою kubectl apply -f gmsa-crd.yaml.

Встановлення вебхуків для перевірки користувачів GMSA

Два вебхуки потрібно налаштувати на кластері Kubernetes для заповнення та перевірки посилань на специфікації облікових даних GMSA на рівні Podʼа або контейнера:

  1. Модифікаційний вебхук, який розширює посилання на GMSAs (за іменем зі специфікації Podʼа) до повної специфікації облікових даних у форматі JSON у специфікації Podʼа.

  2. Валідаційний вебхук забезпечує, що всі посилання на GMSAs уповноважені для використання обліковим записом служби Podʼа.

Встановлення зазначених вище вебхуків та повʼязаних обʼєктів вимагає наступних кроків:

  1. Створіть пару ключів сертифікатів (яка буде використовуватися для забезпечення звʼязку контейнера вебхука з кластером)

  2. Встановіть Secret із вищезазначеним сертифікатом.

  3. Створіть Deployment для основної логіки вебхука.

  4. Створіть конфігурації валідаційного та модифікаційного вебхуків, посилаючись на Deployment.

Скрипт можна використовувати для розгортання та налаштування вебхуків GMSA та повʼязаних з ними обʼєктів, зазначених вище. Скрипт можна запускати з опцією --dry-run=server, щоб ви могли переглянути зміни, які будуть внесені в ваш кластер.

YAML шаблон, що використовується скриптом, також може бути використаний для ручного розгортання вебхуків та повʼязаних обʼєктів (з відповідними замінами параметрів)

Налаштування GMSA та вузлів Windows в Active Directory

Перш ніж Podʼи в Kubernetes можуть бути налаштовані на використання GMSA, необхідно провести налаштування бажаних GMSA в Active Directory, як описано в документації Windows GMSA. Вузли робочих станцій Windows (які є частиною кластера Kubernetes) повинні бути налаштовані в Active Directory для доступу до секретних облікових даних, що повʼязані з бажаним GMSA, як описано в документації Windows GMSA.

Створення ресурсів GMSA Credential Spec

Після встановлення CRD GMSACredentialSpec (як описано раніше), можна налаштувати власні ресурси, що містять специфікації облікових даних GMSA. Специфікація облікових даних GMSA не містить конфіденційні або секретні дані. Це інформація, яку контейнерне середовище може використовувати для опису бажаної GMSA контейнера для Windows. Специфікації облікових даних GMSA можуть бути створені у форматі YAML за допомогою утиліти сценарію PowerShell.

Ось кроки для створення YAML-файлу специфікації облікових даних GMSA вручну у форматі JSON і подальше його конвертування:

  1. Імпортуйте модуль CredentialSpec module: ipmo CredentialSpec.psm1.

  2. Створіть специфікацію облікових даних у форматі JSON за допомогою команди New-CredentialSpec. Щоб створити специфікацію облікових даних GMSA з іменем WebApp1, викличте New-CredentialSpec -Name WebApp1 -AccountName WebApp1 -Domain $(Get-ADDomain -Current LocalComputer).

  3. Використайте команду Get-CredentialSpec, щоб показати шлях до файлу JSON.

  4. Конвертуйте файл credspec з формату JSON у YAML та застосуйте необхідні заголовкові поля apiVersion, kind, metadata та credspec, щоб зробити його специфікацією GMSACredentialSpec, яку можна налаштувати в Kubernetes.

Наступна конфігурація YAML описує специфікацію облікових даних GMSA з іменем gmsa-WebApp1:

apiVersion: windows.k8s.io/v1
kind: GMSACredentialSpec
metadata:
  name: gmsa-WebApp1  # Це довільне імʼя, але воно буде використовуватися як посилання
credspec:
  ActiveDirectoryConfig:
    GroupManagedServiceAccounts:
    - Name: WebApp1   # Імʼя користувача облікового запису GMSA
      Scope: CONTOSO  # Імʼя домену NETBIOS
    - Name: WebApp1   # Імʼя користувача облікового запису GMSA
      Scope: contoso.com # Імʼя домену DNS
  CmsPlugins:
  - ActiveDirectory
  DomainJoinConfig:
    DnsName: contoso.com  # Імʼя домену DNS
    DnsTreeName: contoso.com # Корінь імені домену DNS
    Guid: 244818ae-87ac-4fcd-92ec-e79e5252348a  # GUID для Domain
    MachineAccountName: WebApp1 # Імʼя користувача облікового запису GMSA
    NetBiosName: CONTOSO  # Імʼя домену NETBIOS
    Sid: S-1-5-21-2126449477-2524075714-3094792973 # SID для Domain

Вищезазначений ресурс специфікації облікових даних може бути збережений як gmsa-Webapp1-credspec.yaml і застосований до кластера за допомогою: kubectl apply -f gmsa-Webapp1-credspec.yaml.

Налаштування ролі кластера для включення RBAC у конкретні специфікації облікових даних GMSA

Для кожного ресурсу специфікації облікових даних GMSA потрібно визначити роль в кластері. Це авторизує дію use ("використовувати") на конкретному ресурсі GMSA певним субʼєктом, який, як правило, є службовим обліковим записом. Наведений нижче приклад показує роль в кластері, яка авторизує використання специфікації облікових даних gmsa-WebApp1 зверху. Збережіть файл як gmsa-webapp1-role.yaml і застосуйте його за допомогою kubectl apply -f gmsa-webapp1-role.yaml.

# Створення ролі для читання credspec
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: webapp1-role
rules:
- apiGroups: ["windows.k8s.io"]
  resources: ["gmsacredentialspecs"]
  verbs: ["use"]
  resourceNames: ["gmsa-WebApp1"]

Призначення ролі службовому обліковому запису для використання конкретних специфікацій облікових даних GMSA

Службовий обліковий запис (з якими будуть налаштовані Podʼи) повинен бути привʼязаний до ролі в кластері, створеної вище. Це авторизує службовий обліковий запис використовувати бажаний ресурс специфікації облікових даних GMSA. Нижче показано, як стандартний службовий обліковий запис привʼязується до ролі в кластера webapp1-role для використання ресурсу специфікації облікових даних gmsa-WebApp1, створеного вище.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: allow-default-svc-account-read-on-gmsa-WebApp1
  namespace: default
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: ClusterRole
  name: webapp1-role
  apiGroup: rbac.authorization.k8s.io

Налаштування посилання на специфікацію облікових даних GMSA в специфікації Pod

Поле securityContext.windowsOptions.gmsaCredentialSpecName у специфікації Pod використовується для вказівки посилань на бажані ресурси специфікації облікових даних GMSA в специфікаціях Pod. Це налаштовує всі контейнери в специфікації Pod на використання вказаного GMSA. Нижче наведено приклад специфікації Pod з анотацією, заповненою для посилання на gmsa-WebApp1:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      securityContext:
        windowsOptions:
          gmsaCredentialSpecName: gmsa-webapp1
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
      nodeSelector:
        kubernetes.io/os: windows

Індивідуальні контейнери в специфікації Pod також можуть вказати бажану специфікацію облікових даних GMSA, використовуючи поле securityContext.windowsOptions.gmsaCredentialSpecName на рівні окремого контейнера. Наприклад:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: with-creds
  name: with-creds
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: with-creds
  template:
    metadata:
      labels:
        run: with-creds
    spec:
      containers:
      - image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        imagePullPolicy: Always
        name: iis
        securityContext:
          windowsOptions:
            gmsaCredentialSpecName: gmsa-Webapp1
      nodeSelector:
        kubernetes.io/os: windows

Під час застосування специфікацій Pod з заповненими полями GMSA (як описано вище) в кластері відбувається наступна послідовність подій:

  1. Модифікаційний вебхук розвʼязує та розширює всі посилання на ресурси специфікації облікових даних GMSA до вмісту специфікації облікових даних GMSA.

  2. Валідаційний вебхук переконується, що обліковий запис служби, повʼязаний з Pod, авторизований для дії use на вказаній специфікації облікових даних GMSA.

  3. Контейнерне середовище налаштовує кожен контейнер Windows із вказаною специфікацією облікових даних GMSA, щоб контейнер міг прийняти елемент GMSA в Active Directory та отримати доступ до служб в домені, використовуючи цей елемент.

Автентифікація на мережевих ресурсах за допомогою імені хосту або повного доменного імені

Якщо ви маєте проблеми з підключенням до SMB-ресурсів з Podʼів за допомогою імені хосту або повного доменного імені, але можете отримати доступ до ресурсів за їх адресою IPv4, переконайтеся, що встановлений наступний ключ реєстру на вузлах Windows.

reg add "HKLM\SYSTEM\CurrentControlSet\Services\hns\State" /v EnableCompartmentNamespace /t REG_DWORD /d 1

Після цього потрібно перестворити Podʼи, щоб вони врахували зміни в поведінці. Додаткову інформацію про те, як використовується цей ключ реєстру, можна знайти тут.

Усунення несправностей

Якщо у вас виникають труднощі з налаштуванням роботи GMSA в вашому середовищі, існують кілька кроків усунення несправностей, які ви можете виконати.

Спочатку переконайтеся, що credspec був переданий до Podʼа. Для цього вам потрібно виконати команду exec в одному з ваших Podʼів і перевірити вивід команди nltest.exe /parentdomain.

У наступному прикладі Pod не отримав credspec правильно:

kubectl exec -it iis-auth-7776966999-n5nzr powershell.exe

Результат команди nltest.exe /parentdomain показує наступну помилку:

Getting parent domain failed: Status = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

Якщо ваш Pod отримав credspec правильно, наступним кроком буде перевірка звʼязку з доменом. Спочатку, зсередини вашого Podʼа, виконайте nslookup, щоб знайти корінь вашого домену.

Це дозволить нам визначити 3 речі:

  1. Pod може досягти контролера домену (DC).
  2. Контролер домену (DC) може досягти Podʼа.
  3. DNS працює правильно.

Якщо тест DNS та комунікації успішний, наступним буде перевірка, чи Pod встановив захищений канал звʼязку з доменом. Для цього, знову ж таки, виконайте команду exec в вашому Podʼі та запустіть команду nltest.exe /query.

nltest.exe /query

Результат буде наступним:

I_NetLogonControl failed: Статус = 1722 0x6ba RPC_S_SERVER_UNAVAILABLE

Це говорить нам те, що з якоїсь причини Pod не зміг увійти в домен, використовуючи обліковий запис, вказаний у credspec. Ви можете спробувати полагодити захищений канал за допомогою наступного:

nltest /sc_reset:domain.example

Якщо команда успішна, ви побачите подібний вивід:

Flags: 30 HAS_IP  HAS_TIMESERV
Trusted DC Name \\dc10.domain.example
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

Якщо дії вище виправили помилку, ви можете автоматизувати цей крок, додавши наступний виклик у час життєвого циклу до специфікації вашого Podʼа. Якщо це не виправило помилку, вам потрібно перевірити ваш credspec ще раз та підтвердити, що він правильний та повний.

        image: registry.domain.example/iis-auth:1809v1
        lifecycle:
          postStart:
            exec:
              command: ["powershell.exe","-command","do { Restart-Service -Name netlogon } while ( $($Result = (nltest.exe /query); if ($Result -like '*0x0 NERR_Success*') {return $true} else {return $false}) -eq $false)"]
        imagePullPolicy: IfNotPresent

Якщо ви додасте розділ lifecycle до специфікації вашого Podʼа, Pod виконає вказані команди для перезапуску служби netlogon до того, як команда nltest.exe /query вийде без помилок.

3.4 - Зміна обсягів CPU та памʼяті, призначених для контейнерів

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.27 [alpha]

Ця сторінка передбачає, що ви обізнані з Якістю обслуговування для Podʼів Kubernetes.

Ця сторінка показує, як змінити обсяги CPU та памʼяті, призначені для контейнерів працюючого Podʼа без перезапуску самого Podʼа або його контейнерів. Вузол Kubernetes виділяє ресурси для Podʼа на основі його запитів, і обмежує використання ресурсів Podʼа на основі лімітів, вказаних у контейнерах Podʼа.

Зміна розподілу ресурсів для запущеного Podʼа вимагає, що функціональна можливість InPlacePodVerticalScaling має бути увімкнено. Альтернативою може бути видалення Podʼа і ввімкнення параметра, щоб workload controller створив новий Pod з іншими вимогами до ресурсів.

Для зміни ресурсів Podʼа на місці:

  • Ресурси запитів та лімітів контейнера є змінними для ресурсів CPU та памʼяті.
  • Поле allocatedResources у containerStatuses статусу Podʼа відображає ресурси, виділені контейнерам Podʼа.
  • Поле resources у containerStatuses статусу Podʼа відображає фактичні ресурси запитів та лімітів, які налаштовані на запущених контейнерах відповідно до звіту контейнерного середовища.
  • Поле resize у статусі Podʼа показує статус останнього запиту очікуваної зміни розміру. Воно може мати наступні значення:
    • Proposed: Це значення показує, що було отримано підтвердження запиту на зміну розміру та що запит був перевірений та зареєстрований.
    • InProgress: Це значення вказує, що вузол прийняв запит на зміну розміру та знаходиться у процесі застосування його до контейнерів Podʼа.
    • Deferred: Це значення означає, що запитаної зміни розміру наразі не можна виконати, і вузол буде спробувати її виконати пізніше. Зміна розміру може бути виконана, коли інші Podʼи покинуть і звільнять ресурси вузла.
    • Infeasible: це сигнал того, що вузол не може задовольнити запит на зміну розміру. Це може статися, якщо запит на зміну розміру перевищує максимальні ресурси, які вузол може виділити для Podʼа.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.27. Для перевірки версії введіть kubectl version.

Має бути увімкнено функціональну можливість InPlacePodVerticalScaling для вашої панелі управління і для всіх вузлів вашого кластера.

Політики зміни розміру контейнера

Політики зміни розміру дозволяють більш детально керувати тим, як контейнери Podʼа змінюють свої ресурси CPU та памʼяті. Наприклад, застосунок з контейнера може використовувати ресурси CPU, змінені без перезапуску, але зміна памʼяті може вимагати перезапуску застосунку та відповідно контейнерів.

Для активації цього користувачам дозволяється вказати resizePolicy у специфікації контейнера. Наступні політики перезапуску можна вказати для зміни розміру CPU та памʼяті:

  • NotRequired: Змінити ресурси контейнера під час його роботи.
  • RestartContainer: Перезапустити контейнер та застосувати нові ресурси після перезапуску.

Якщо resizePolicy[*].restartPolicy не вказано, воно стандартно встановлюється в NotRequired.

У наведеному нижче прикладі Podʼа CPU контейнера може бути змінено без перезапуску, але змінювання памʼяті вимагає перезапуску контейнера.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-5
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr-5
    image: nginx
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: RestartContainer
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

Створення Podʼа із запитами та лімітами ресурсів

Ви можете створити Guaranteed або Burstable клас якості обслуговування Podʼу, вказавши запити та/або ліміти для контейнерів Podʼа.

Розгляньте наступний маніфест для Podʼа, який має один контейнер.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-5
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr-5
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

Створіть Pod у просторі імен qos-example:

kubectl create namespace qos-example
kubectl create -f https://k8s.io/examples/pods/qos/qos-pod-5.yaml

Цей Pod класифікується як Pod класу якості обслуговування Guaranteed, і має запит 700 мілі CPU та 200 мегабайтів памʼяті.

Перегляньте детальну інформацію про Pod:

kubectl get pod qos-demo-5 --output=yaml --namespace=qos-example

Також погляньте, що значення resizePolicy[*].restartPolicy типово встановлено в NotRequired, що вказує, що CPU та памʼять можна змінити, поки контейнер працює.

spec:
  containers:
    ...
    resizePolicy:
    - resourceName: cpu
      restartPolicy: NotRequired
    - resourceName: memory
      restartPolicy: NotRequired
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
...
  containerStatuses:
...
    name: qos-demo-ctr-5
    ready: true
...
    allocatedResources:
      cpu: 700m
      memory: 200Mi
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
    restartCount: 0
    started: true
...
  qosClass: Guaranteed

Оновлення ресурсів Podʼа

Скажімо, вимоги до CPU зросли, і тепер потрібно 0.8 CPU. Це можна вказати вручну, або визначити і застосувати програмно, наприклад, за допомогою таких засобів, як VerticalPodAutoscaler (VPA).

Тепер відредагуйте контейнер Podʼа, встановивши як запити, так і ліміти CPU на 800m:

kubectl -n qos-example patch pod qos-demo-5 --patch '{"spec":{"containers":[{"name":"qos-demo-ctr-5", "resources":{"requests":{"cpu":"800m"}, "limits":{"cpu":"800m"}}}]}}'

Отримайте докладну інформацію про Pod після внесення змін.

kubectl get pod qos-demo-5 --output=yaml --namespace=qos-example

Специфікація Podʼа нижче показує оновлені запити та ліміти CPU.

spec:
  containers:
    ...
    resources:
      limits:
        cpu: 800m
        memory: 200Mi
      requests:
        cpu: 800m
        memory: 200Mi
...
  containerStatuses:
...
    allocatedResources:
      cpu: 800m
      memory: 200Mi
    resources:
      limits:
        cpu: 800m
        memory: 200Mi
      requests:
        cpu: 800m
        memory: 200Mi
    restartCount: 0
    started: true

Зверніть увагу, що значення allocatedResources були оновлені, щоб відображати нові бажані запити CPU. Це вказує на те, що вузол зміг забезпечити потреби у збільшених ресурсах CPU.

У статусі контейнера оновлені значення ресурсів CPU показують, що нові CPU ресурси були застосовані. Значення restartCount контейнера залишається без змін, що вказує на те, що ресурси CPU контейнера були змінені без перезапуску контейнера.

Очищення

Видаліть ваш простір імен:

kubectl delete namespace qos-example

Для розробників застосунків

Для адміністраторів кластерів

3.5 - Налаштування RunAsUserName для Podʼів та контейнерів Windows

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.18 [stable]

Ця сторінка показує, як використовувати параметр runAsUserName для Podʼів та контейнерів, які будуть запущені на вузлах Windows. Це приблизно еквівалент параметра runAsUser, який використовується для Linux, і дозволяє виконувати програми в контейнері від імені іншого імені користувача, ніж типово.

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

Вам потрібно мати кластер Kubernetes, а також інструмент командного рядка kubectl повинен бути налаштований для взаємодії з вашим кластером. Очікується, що в кластері будуть використовуватися вузли Windows, де будуть запускатися Podʼи з контейнерами, що виконують робочі навантаження у Windows.

Встановлення імені користувача для Podʼа

Щоб вказати імʼя користувача, з яким потрібно виконати процеси контейнера Podʼа, включіть поле securityContext (PodSecurityContext) в специфікацію Podʼа, а всередині нього — поле windowsOptions (WindowsSecurityContextOptions), що містить поле runAsUserName.

Опції безпеки Windows, які ви вказуєте для Podʼа, застосовуються до всіх контейнерів та контейнерів ініціалізації у Podʼі.

Ось конфігураційний файл для Podʼа Windows зі встановленим полем runAsUserName:

apiVersion: v1
kind: Pod
metadata:
  name: run-as-username-pod-demo
spec:
  securityContext:
    windowsOptions:
      runAsUserName: "ContainerUser"
  containers:
  - name: run-as-username-demo
    image: mcr.microsoft.com/windows/servercore:ltsc2019
    command: ["ping", "-t", "localhost"]
  nodeSelector:
    kubernetes.io/os: windows

Створіть Pod:

kubectl apply -f https://k8s.io/examples/windows/run-as-username-pod.yaml

Перевірте, що Контейнер Podʼа працює:

kubectl get pod run-as-username-pod-demo

Отримайте доступ до оболонки контейнера:

kubectl exec -it run-as-username-pod-demo -- powershell

Перевірте, що оболонка працює від імені відповідного користувача:

echo $env:USERNAME

Вивід повинен бути:

ContainerUser

Встановлення імені користувача для контейнера

Щоб вказати імʼя користувача, з яким потрібно виконати процеси контейнера, включіть поле securityContext (SecurityContext) у маніфесті контейнера, а всередині нього — поле windowsOptions (WindowsSecurityContextOptions), що містить поле runAsUserName.

Опції безпеки Windows, які ви вказуєте для контейнера, застосовуються тільки до цього окремого контейнера, і вони перевизначають налаштування, зроблені на рівні Podʼа.

Ось конфігураційний файл для Podʼа, який має один Контейнер, а поле runAsUserName встановлене на рівні Podʼа та на рівні Контейнера:

apiVersion: v1
kind: Pod
metadata:
  name: run-as-username-container-demo
spec:
  securityContext:
    windowsOptions:
      runAsUserName: "ContainerUser"
  containers:
  - name: run-as-username-demo
    image: mcr.microsoft.com/windows/servercore:ltsc2019
    command: ["ping", "-t", "localhost"]
    securityContext:
        windowsOptions:
            runAsUserName: "ContainerAdministrator"
  nodeSelector:
    kubernetes.io/os: windows

Створіть Pod:

kubectl apply -f https://k8s.io/examples/windows/run-as-username-container.yaml

Перевірте, що Контейнер Podʼа працює:

kubectl get pod run-as-username-container-demo

Отримайте доступ до оболонки контейнера:

kubectl exec -it run-as-username-container-demo -- powershell

Перевірте, що оболонка працює від імені відповідного користувача (того, який встановлений на рівні контейнера):

echo $env:USERNAME

Вивід повинен бути:

ContainerAdministrator

Обмеження імен користувачів Windows

Для використання цієї функції значення, встановлене у полі runAsUserName, повинно бути дійсним імʼям користувача. Воно повинно мати наступний формат: DOMAIN\USER, де DOMAIN\ є необовʼязковим. Імена користувачів Windows регістронезалежні. Крім того, існують деякі обмеження стосовно DOMAIN та USER:

  • Поле runAsUserName не може бути порожнім і не може містити керуючі символи (ASCII значення: 0x00-0x1F, 0x7F)
  • DOMAIN може бути або NetBios-імʼям, або DNS-імʼям, кожне з власними обмеженнями:
    • NetBios імена: максимум 15 символів, не можуть починатися з . (крапка), і не можуть містити наступні символи: \ / : * ? " < > |
    • DNS-імена: максимум 255 символів, містять тільки буквено-цифрові символи, крапки та дефіси, і не можуть починатися або закінчуватися . (крапка) або - (дефіс).
  • USER може мати не більше 20 символів, не може містити тільки крапки або пробіли, і не може містити наступні символи: " / \ [ ] : ; | = , + * ? < > @.

Приклади припустимих значень для поля runAsUserName: ContainerAdministrator, ContainerUser, NT AUTHORITY\NETWORK SERVICE, NT AUTHORITY\LOCAL SERVICE.

Для отримання додаткової інформації про ці обмеження, перевірте тут та тут.

Що далі

3.6 - Створення Podʼа Windows HostProcess

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [stable]

Windows HostProcess контейнери дозволяють вам запускати контейнеризовані робочі навантаження на хості Windows. Ці контейнери працюють як звичайні процеси, але мають доступ до мережевого простору імен хосту, сховища та пристроїв, коли надані відповідні права користувача. Контейнери HostProcess можуть бути використані для розгортання мережевих втулків, сховищ конфігурацій, пристроїв, kube-proxy та інших компонентів на вузлах Windows без потреби у власних проксі або безпосереднього встановлення служб хосту.

Адміністративні завдання, такі як встановлення патчів безпеки, збір подій логів тощо, можна виконувати без потреби входу операторів кластера на кожен вузол Windows. Контейнери HostProcess можуть працювати як будь-який користувач, що доступний на хості або в домені машини хосту, що дозволяє адміністраторам обмежити доступ до ресурсів через дозволи користувача. Хоча і не підтримуються ізоляція файлової системи або процесу, при запуску контейнера на хості створюється новий том, щоб надати йому чисте та обʼєднане робоче середовище. Контейнери HostProcess також можуть бути побудовані на базі наявних образів базової системи Windows і не успадковують ті ж вимоги сумісності як контейнери Windows server, що означає, що версія базового образу не повинна відповідати версії хосту. Однак рекомендується використовувати ту ж версію базового образу, що й ваші робочі навантаження контейнерів Windows Server, щоб уникнути зайвого використання місця на вузлі. Контейнери HostProcess також підтримують монтування томів всередині тома контейнера.

Коли варто використовувати контейнери Windows HostProcess?

  • Коли потрібно виконати завдання, які потребують мережевого простору імен хосту. Контейнери HostProcess мають доступ до мережевих інтерфейсів хосту та IP-адрес.
  • Вам потрібен доступ до ресурсів на хості, таких як файлова система, події логів тощо.
  • Встановлення конкретних драйверів пристроїв або служб Windows.
  • Обʼєднання адміністративних завдань та політик безпеки. Це зменшує ступінь привілеїв, які потрібні вузлам Windows.

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

Цей посібник стосується конкретно Kubernetes v1.31. Якщо ви використовуєте іншу версію Kubernetes, перевірте документацію для цієї версії Kubernetes.

У Kubernetes 1.31 контейнери HostProcess є типово увімкненими. kubelet буде спілкуватися з containerd безпосередньо, передаючи прапорець hostprocess через CRI. Ви можете використовувати останню версію containerd (v1.6+) для запуску контейнерів HostProcess. Як встановити containerd.

Обмеження

Ці обмеження стосуються Kubernetes v1.31:

  • Контейнери HostProcess вимагають середовища виконання контейнерів containerd 1.6 або вище, рекомендується використовувати containerd 1.7.
  • Podʼи HostProcess можуть містити лише контейнери HostProcess. Це поточне обмеження ОС Windows; непривілейовані контейнери Windows не можуть спільно використовувати vNIC з простором імен IP хосту.
  • Контейнери HostProcess запускаються як процес на хості та не мають жодного рівня ізоляції, окрім обмежень ресурсів, накладених на обліковий запис користувача HostProcess. Ізоляція ні файлової системи, ні ізоляції Hyper-V не підтримуються для контейнерів HostProcess.
  • Монтування томів підтримуються і монтуватимуться як томом контейнера. Див. Монтування томів
  • Стандартно для контейнерів HostProcess доступний обмежений набір облікових записів користувачів хосту. Див. Вибір облікового запису користувача.
  • Обмеження ресурсів (диск, памʼять, кількість процесорів) підтримуються так само як і процеси на хості.
  • Як іменовані канали, так і сокети Unix-домену не підтримуються і замість цього слід отримувати доступ до них через їх шлях на хості (наприклад, \\.\pipe\*)

Вимоги до конфігурації HostProcess Pod

Для активації Windows HostProcess Pod необхідно встановити відповідні конфігурації у конфігурації безпеки Podʼа. З усіх політик, визначених у Стандартах безпеки Pod, HostProcess Podʼи заборонені за базовою та обмеженою політиками. Тому рекомендується, щоб HostProcess Podʼи працювали відповідно до привілейованого профілю.

Під час роботи з привілейованою політикою, ось конфігурації, які потрібно встановити для активації створення HostProcess Pod:

Специфікація привілейованої політики
ЕлементПолітика
securityContext.windowsOptions.hostProcess

Windows Podʼи надають можливість запуску контейнерів HostProcess, які дозволяють привілейований доступ до вузла Windows.

Дозволені значення

  • true
hostNetwork

Контейнери HostProcess Podʼи повинні використовувати мережевий простір хоста.

Дозволені значення

  • true
securityContext.windowsOptions.runAsUserName

Необхідно вказати, яким користувачем має виконуватися контейнер HostProcess в специфікації Podʼа.

Дозволені значення

  • NT AUTHORITY\SYSTEM
  • NT AUTHORITY\Local service
  • NT AUTHORITY\NetworkService
  • Назви локальних груп користувачів (див. нижче)
runAsNonRoot

Оскільки контейнери HostProcess мають привілейований доступ до хоста, поле runAsNonRoot не може бути встановлене в true.

Дозволені значення

  • Undefined/Nil
  • false

Приклад маніфесту (частково)

spec:
  securityContext:
    windowsOptions:
      hostProcess: true
      runAsUserName: "NT AUTHORITY\\Local service"
  hostNetwork: true
  containers:
  - name: test
    image: image1:latest
    command:
      - ping
      - -t
      - 127.0.0.1
  nodeSelector:
    "kubernetes.io/os": windows

Монтування томів

Контейнери HostProcess підтримують можливість монтування томів у просторі томів контейнера. Поведінка монтування томів відрізняється залежно від версії контейнерного середовища containerd, яке використовується на вузлі.

Containerd v1.6

Застосунки, що працюють усередині контейнера, можуть отримувати доступ до підключених томів безпосередньо за допомогою відносних або абсолютних шляхів. Під час створення контейнера встановлюється змінна середовища $CONTAINER_SANDBOX_MOUNT_POINT, яка містить абсолютний шлях хосту до тому контейнера. Відносні шляхи базуються на конфігурації .spec.containers.volumeMounts.mountPath.

Для доступу до токенів облікового запису служби (наприклад) у контейнері підтримуються такі структури шляхів:

  • .\var\run\secrets\kubernetes.io\serviceaccount\
  • $CONTAINER_SANDBOX_MOUNT_POINT\var\run\secrets\kubernetes.io\serviceaccount\

Containerd v1.7 (та новіші версії)

Застосунки, які працюють усередині контейнера, можуть отримувати доступ до підключених томів безпосередньо через вказаний mountPath тому (аналогічно Linux і не-HostProcess контейнерам Windows).

Для забезпечення зворотної сумісності зі старими версіями, доступ до томів також може бути здійснений через використання тих самих відносних шляхів, які були налаштовані у containerd v1.6.

Наприклад, для доступу до токенів службового облікового запису усередині контейнера ви можете використовувати один із таких шляхів:

  • c:\var\run\secrets\kubernetes.io\serviceaccount
  • /var/run/secrets/kubernetes.io/serviceaccount/
  • $CONTAINER_SANDBOX_MOUNT_POINT\var\run\secrets\kubernetes.io\serviceaccount\

Обмеження ресурсів

Обмеження ресурсів (диск, памʼять, кількість CPU) застосовуються до задачі і є загальними для всієї задачі. Наприклад, при обмеженні в 10 МБ памʼяті, памʼять, виділена для будь-якого обʼєкта задачі HostProcess, буде обмежена 10 МБ. Це така ж поведінка, як і в інших типах контейнерів Windows. Ці обмеження вказуються так само як і зараз для будь-якого середовища виконання контейнерів або оркестрування, яке використовується. Єдина відмінність полягає у розрахунку використання дискових ресурсів для відстеження ресурсів через відмінності у способі ініціалізації контейнерів HostProcess.

Вибір облікового запису користувача

Системні облікові записи

Типово контейнери HostProcess підтримують можливість запуску з одного з трьох підтримуваних облікових записів служб Windows:

Вам слід вибрати відповідний обліковий запис служби Windows для кожного контейнера HostProcess, спираючись на обмеження ступеня привілеїв, щоб уникнути випадкових (або навіть зловмисних) пошкоджень хосту. Обліковий запис служби LocalSystem має найвищий рівень привілеїв серед трьох і повинен використовуватися лише у разі абсолютної необхідності. Де це можливо, використовуйте обліковий запис служби LocalService, оскільки він має найнижчий рівень привілеїв серед цих трьох варіантів.

Локальні облікові записи

Якщо налаштовано, контейнери HostProcess також можуть запускатися як локальні облікові записи користувачів, що дозволяє операторам вузлів надавати деталізований доступ до робочих навантажень.

Для запуску контейнерів HostProcess як локального користувача, спершу на вузлі має бути створена локальна група користувачів, і імʼя цієї локальної групи користувачів повинно бути вказане у полі runAsUserName у Deployment. Перед ініціалізацією контейнера HostProcess має бути створено новий ефемерний локальний обліковий запис користувача та приєднано його до вказаної групи користувачів, з якої запускається контейнер. Це надає кілька переваг, включаючи уникнення необхідності управління паролями для локальних облікових записів користувачів. Початковий контейнер HostProcess, що працює як службовий обліковий запис, може бути використаний для підготовки груп користувачів для подальших контейнерів HostProcess.

Приклад:

  1. Створіть локальну групу користувачів на вузлі (це може бути зроблено в іншому контейнері HostProcess).

    net localgroup hpc-localgroup /add
    
  2. Надайте доступ до потрібних ресурсів на вузлі локальній групі користувачів. Це можна зробити за допомогою інструментів, таких як icacls.

  3. Встановіть runAsUserName на імʼя локальної групи користувачів для Podʼа або окремих контейнерів.

    securityContext:
      windowsOptions:
        hostProcess: true
        runAsUserName: hpc-localgroup
    
  4. Заплануйте Pod!

Базовий образ для контейнерів HostProcess

Контейнери HostProcess можуть бути побудовані з будь-яких наявних базових образів контейнерів Windows.

Крім того, був створений новий базовий образ спеціально для контейнерів HostProcess! Для отримання додаткової інформації перегляньте проєкт windows-host-process-containers-base-image на github.

Розвʼязання проблем з контейнерами HostProcess

  • Контейнери HostProcess не запускаються, помилка failed to create user process token: failed to logon user: Access is denied.: unknown.

    Переконайтеся, що containerd працює як службовий обліковий запис LocalSystem або LocalService. Облікові записи користувачів (навіть адміністраторські облікові записи) не мають дозволів на створення токенів входу для будь-яких підтримуваних облікових записів користувачів.

3.7 - Налаштування якості обслуговування для Podʼів

Ця сторінка показує, як налаштувати Podʼи так, щоб їм були призначені певні класи якості обслуговування (QoS). Kubernetes використовує класи QoS для прийняття рішень про видалення Podʼів, коли використання ресурсів вузла збільшується.

Коли Kubernetes створює Pod, він призначає один з таких класів QoS для Podʼа:

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

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

Також вам потрібно мати можливість створювати та видаляти простори імен.

Створення простору імен

Створіть простір імен, щоб ресурси, які ви створюєте у цьому завданні, були ізольовані від решти вашого кластера.

kubectl create namespace qos-example

Створення Podʼа, якому призначено клас QoS Guaranteed

Щоб Podʼа був наданий клас QoS Guaranteed:

  • Кожний контейнер у Pod повинен мати ліміт памʼяті та запит памʼяті.
  • Для кожного контейнера у Pod ліміт памʼяті повинен дорівнювати запиту памʼяті.
  • Кожний контейнер у Pod повинен мати ліміт CPU та запит CPU.
  • Для кожного контейнера у Pod ліміт CPU повинен дорівнювати запиту CPU.

Ці обмеження так само застосовуються до контейнерів ініціалізації і до контейнерів застосунків. Ефемерні контейнери не можуть визначати ресурси, тому ці обмеження не застосовуються до них.

Нижче подано маніфест для Podʼа з одним контейнером. Контейнер має ліміт памʼяті та запит памʼяті, обидва дорівнюють 200 MiB. Контейнер має ліміт CPU та запит CPU, обидва дорівнюють 700 міліCPU:

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod.yaml --namespace=qos-example

Перегляньте докладну інформацію про Pod:

kubectl get pod qos-demo --namespace=qos-example --output=yaml

Вивід показує, що Kubernetes призначив Podʼу клас QoS Guaranteed. Також вивід підтверджує, що у контейнера Podʼа є запит памʼяті, який відповідає його ліміту памʼяті, і є запит CPU, який відповідає його ліміту CPU.

spec:
  containers:
    ...
    resources:
      limits:
        cpu: 700m
        memory: 200Mi
      requests:
        cpu: 700m
        memory: 200Mi
    ...
status:
  qosClass: Guaranteed

Очищення

Видаліть свій Pod:

kubectl delete pod qos-demo --namespace=qos-example

Створення Podʼа, якому призначено клас QoS Burstable

Podʼу надається клас QoS Burstable, якщо:

  • Pod не відповідає критеріям для класу QoS Guaranteed.
  • Принаймні один контейнер у Podʼі має запит або ліміт памʼяті або CPU.

Нижче подано маніфест для Podʼа з одним контейнером. Контейнер має ліміт памʼяті 200 MiB та запит памʼяті 100 MiB.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-2.yaml --namespace=qos-example

Перегляньте докладну інформацію про Pod:

kubectl get pod qos-demo-2 --namespace=qos-example --output=yaml

Вивід показує, що Kubernetes призначив Podʼу клас QoS Burstable:

spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: qos-demo-2-ctr
    resources:
      limits:
        memory: 200Mi
      requests:
        memory: 100Mi
  ...
status:
  qosClass: Burstable

Очищення

Видаліть свій Pod:

kubectl delete pod qos-demo-2 --namespace=qos-example

Створення Podʼа, якому призначено клас QoS BestEffort

Для того, щоб Podʼу був призначений клас QoS BestEffort, контейнери у Podʼі не повинні мати жодних лімітів або запитів памʼяті чи CPU.

Нижче подано маніфест для Podʼа з одним контейнером. контейнер не має лімітів або запитів памʼяті чи CPU:

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-3-ctr
    image: nginx

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-3.yaml --namespace=qos-example

Перегляньте докладну інформацію про Pod:

kubectl get pod qos-demo-3 --namespace=qos-example --output=yaml

Вивід показує, що Kubernetes призначив Podʼа клас QoS BestEffort:

spec:
  containers:
    ...
    resources: {}
  ...
status:
  qosClass: BestEffort

Очищення

Видаліть свій Pod:

kubectl delete pod qos-demo-3 --namespace=qos-example

Створення Podʼа з двома контейнерами

Нижче подано маніфест для Podʼа з двома контейнерами. Один контейнер вказує запит памʼяті 200 MiB. Інший контейнер не вказує жодних запитів або лімітів.

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-4
  namespace: qos-example
spec:
  containers:

  - name: qos-demo-4-ctr-1
    image: nginx
    resources:
      requests:
        memory: "200Mi"

  - name: qos-demo-4-ctr-2
    image: redis

Зверніть увагу, що цей Pod відповідає критеріям класу QoS Burstable. Тобто, він не відповідає критеріям для класу QoS Guaranteed, і один з його контейнерів має запит памʼяті.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/qos/qos-pod-4.yaml --namespace=qos-example

Перегляньте докладну інформацію про Pod:

kubectl get pod qos-demo-4 --namespace=qos-example --output=yaml

Вивід показує, що Kubernetes призначив Podʼу клас QoS Burstable:

spec:
  containers:
    ...
    name: qos-demo-4-ctr-1
    resources:
      requests:
        memory: 200Mi
    ...
    name: qos-demo-4-ctr-2
    resources: {}
    ...
status:
  qosClass: Burstable

Отримання класу QoS Podʼа

Замість того, щоб бачити всі поля, ви можете переглянути лише поле, яке вам потрібно:

kubectl --namespace=qos-example get pod qos-demo-4 -o jsonpath='{ .status.qosClass}{"\n"}'
Burstable

Очищення

Видаліть ваш простір імен:

kubectl delete namespace qos-example

Що далі

Для розробників застосунків

Для адміністраторів кластера

3.8 - Призначення розширених ресурсів контейнеру

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [stable]

Ця сторінка показує, як призначити розширені ресурси контейнеру.

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

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

Для перевірки версії введіть kubectl version.

Перш ніж виконувати це завдання, виконайте завдання Оголошення розширених ресурсів для вузла. Це налаштує один з ваших вузлів для оголошення ресурсу "dongle".

Призначте розширений ресурс Podʼу

Щоб запитати розширений ресурс, включіть поле resources:requests у ваш маніфест контейнера. Розширені ресурси повністю кваліфікуються будь-яким доменом поза *.kubernetes.io/. Дійсні імена розширених ресурсів мають форму example.com/foo, де example.com замінено на домен вашої організації, а foo — це описове імʼя ресурсу.

Нижче подано конфігураційний файл для Podʼа з одним контейнером:

apiVersion: v1
kind: Pod
metadata:
  name: extended-resource-demo
spec:
  containers:
  - name: extended-resource-demo-ctr
    image: nginx
    resources:
      requests:
        example.com/dongle: 3
      limits:
        example.com/dongle: 3

У конфігураційному файлі можна побачити, що контейнер запитує 3 розширених ресурси "dongle".

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod.yaml

Перевірте, що Pod працює:

kubectl get pod extended-resource-demo

Опишіть Pod:

kubectl describe pod extended-resource-demo

Виведений текст показує запити донглів:

Limits:
  example.com/dongle: 3
Requests:
  example.com/dongle: 3

Спроба створення другого Podʼа

Нижче наведено конфігураційний файл для Podʼа з одним контейнером. Контейнер запитує два розширені ресурси "dongle".

apiVersion: v1
kind: Pod
metadata:
  name: extended-resource-demo-2
spec:
  containers:
  - name: extended-resource-demo-2-ctr
    image: nginx
    resources:
      requests:
        example.com/dongle: 2
      limits:
        example.com/dongle: 2

Kubernetes не зможе задовольнити запит на два донгли, оскільки перший Pod використав три з чотирьох доступних донглів.

Спроба створити Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/extended-resource-pod-2.yaml

Опишіть Pod:

kubectl describe pod extended-resource-demo-2

Текст виводу показує, що Pod не може бути запланованим, оскільки немає вузла, на якому було б доступно 2 донгли:

Conditions:
  Type    Status
  PodScheduled  False
...
Events:
  ...
  ... Warning   FailedScheduling  pod (extended-resource-demo-2) failed to fit in any node
fit failure summary on nodes : Insufficient example.com/dongle (1)

Перегляньте статус Podʼа:

kubectl get pod extended-resource-demo-2

Текст виводу показує, що Pod було створено, але не заплановано для виконання на вузлі. Він має статус Pending:

NAME                       READY     STATUS    RESTARTS   AGE
extended-resource-demo-2   0/1       Pending   0          6m

Очищення

Видаліть Podʼи, які ви створили для цього завдання:

kubectl delete pod extended-resource-demo
kubectl delete pod extended-resource-demo-2

Для розробників застосунків

Для адміністраторів кластера

3.9 - Налаштування Podʼа для використання тому для зберігання

Ця сторінка показує, як налаштувати Pod для використання тому для зберігання.

Файлова система контейнера існує лише поки існує сам контейнер. Отже, коли контейнер завершує роботу та перезавантажується, зміни в файловій системі втрачаються. Для більш стійкого зберігання, яке не залежить від контейнера, ви можете використовувати том. Це особливо важливо для застосунків, що зберігають стан, таких як бази даних і сховища ключ-значення (наприклад, Redis).

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

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

Для перевірки версії введіть kubectl version.

Налаштування тому для Podʼа

У цьому завданні ви створюєте Pod, який запускає один контейнер. У цьому Podʼі є том типу emptyDir, який існує протягом усього життєвого циклу Podʼа, навіть якщо контейнер завершиться та перезапуститься. Ось конфігураційний файл для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}
  1. Створіть Pod:

    kubectl apply -f https://k8s.io/examples/pods/storage/redis.yaml
    
  2. Перевірте, що контейнер Podʼа працює, а потім спостерігайте за змінами в Podʼі:

    kubectl get pod redis --watch
    

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

    NAME      READY     STATUS    RESTARTS   AGE
    redis     1/1       Running   0          13s
    
  3. В іншому терміналі отримайте доступ до оболонки запущеного контейнера:

    kubectl exec -it redis -- /bin/bash
    
  4. У вашій оболонці перейдіть до /data/redis, а потім створіть файл:

    root@redis:/data# cd /data/redis/
    root@redis:/data/redis# echo Hello > test-file
    
  5. У вашій оболонці виведіть список запущених процесів:

    root@redis:/data/redis# apt-get update
    root@redis:/data/redis# apt-get install procps
    root@redis:/data/redis# ps aux
    

    Вивід буде схожий на це:

    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    redis        1  0.1  0.1  33308  3828 ?        Ssl  00:46   0:00 redis-server *:6379
    root        12  0.0  0.0  20228  3020 ?        Ss   00:47   0:00 /bin/bash
    root        15  0.0  0.0  17500  2072 ?        R+   00:48   0:00 ps aux
    
  6. У вашій оболонці завершіть процес Redis:

    root@redis:/data/redis# kill <pid>
    

    де <pid> — це ідентифікатор процесу Redis (PID).

  7. У вашому початковому терміналі спостерігайте за змінами в Podʼі Redis. В кінцевому результаті ви побачите щось подібне:

    NAME      READY     STATUS     RESTARTS   AGE
    redis     1/1       Running    0          13s
    redis     0/1       Completed  0         6m
    redis     1/1       Running    1         6m
    

На цьому етапі контейнер завершився та перезапустився. Це тому, що Pod Redis має restartPolicy Always.

  1. Отримайте доступ до оболонки в перезапущеному контейнері:

    kubectl exec -it redis -- /bin/bash
    
  2. У вашій оболонці перейдіть до /data/redis та перевірте, що test-file все ще там.

    root@redis:/data/redis# cd /data/redis/
    root@redis:/data/redis# ls
    test-file
    
  3. Видаліть Pod, який ви створили для цього завдання:

    kubectl delete pod redis
    

Що далі

  • Дивіться Volume.

  • Дивіться Pod.

  • Крім локального сховища на диску, яке надає emptyDir, Kubernetes підтримує багато різних рішень для мережевого сховища, включаючи PD на GCE та EBS на EC2, які бажані для критичних даних та будуть обробляти деталі, такі як монтування та розмонтування пристроїв на вузлах. Дивіться Volumes для отримання додаткової інформації.

3.10 - Налаштування Podʼа для використання PersistentVolume для зберігання

Ця сторінка показує, як налаштувати Pod для використання PersistentVolumeClaim для зберігання. Ось короткий опис процесу:

  1. Ви, як адміністратор кластера, створюєте PersistentVolume на основі фізичного сховища. Ви не повʼязуєте том з жодним Podʼом.

  2. Ви, як розробник / користувач кластера, створюєте PersistentVolumeClaim, який автоматично привʼязується до відповідного PersistentVolume.

  3. Ви створюєте Pod, який використовує вищезгаданий PersistentVolumeClaim для зберігання.

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

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

  • Ознайомтеся з матеріалом в Постійні томи.

Створіть файл index.html на вашому вузлі

Відкрийте оболонку на єдиному вузлі вашого кластера. Спосіб відкриття оболонки залежить від того, як ви налаштували ваш кластер. Наприклад, якщо ви використовуєте Minikube, ви можете відкрити оболонку до вашого вузла, введенням minikube ssh.

У вашій оболонці на цьому вузлі створіть теку /mnt/data:

# Це передбачає, що ваш вузол використовує "sudo" для запуску команд
# як суперкористувач
sudo mkdir /mnt/data

У теці /mnt/data створіть файл index.html:

# Це також передбачає, що ваш вузол використовує "sudo" для запуску команд
# як суперкористувач
sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/index.html"

Перевірте, що файл index.html існує:

cat /mnt/data/index.html

Виведення повинно бути:

Hello from Kubernetes storage

Тепер ви можете закрити оболонку вашого вузла.

Створення PersistentVolume

У цьому завданні ви створюєте hostPath PersistentVolume. Kubernetes підтримує hostPath для розробки та тестування на одновузловому кластері. PersistentVolume типу hostPath використовує файл або теку на вузлі для емуляції мережевого сховища.

В операційному кластері ви не використовували б hostPath. Замість цього адміністратор кластера створив би мережевий ресурс, такий як постійний диск Google Compute Engine, розділ NFS або том Amazon Elastic Block Store. Адміністратори кластера також можуть використовувати StorageClasses для динамічного налаштування.

Ось файл конфігурації для PersistentVolume типу hostPath:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

Файл конфігурації вказує, що том знаходиться в /mnt/data на вузлі кластера. Конфігурація також вказує розмір 10 гібібайт та режим доступу ReadWriteOnce, що означає, що том може бути підключений як для читання-запису лише одним вузлом. Визначається імʼя StorageClass manual для PersistentVolume, яке буде використовуватися для привʼязки запитів PersistentVolumeClaim до цього PersistentVolume.

Створіть PersistentVolume:

kubectl apply -f https://k8s.io/examples/pods/storage/pv-volume.yaml

Перегляньте інформацію про PersistentVolume:

kubectl get pv task-pv-volume

Вивід показує, що PersistentVolume має STATUS Available. Це означає, що він ще не був привʼязаний до PersistentVolumeClaim.

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Available             manual                   4s

Створення PersistentVolumeClaim

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

Ось файл конфігурації для PersistentVolumeClaim:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

Створіть PersistentVolumeClaim:

kubectl apply -f https://k8s.io/examples/pods/storage/pv-claim.yaml

Після створення PersistentVolumeClaim панель управління Kubernetes шукає PersistentVolume, який відповідає вимогам заявки. Якщо панель управління знаходить відповідний PersistentVolume з тим самим StorageClass, вона привʼязує заявку до тому.

Знову подивіться на PersistentVolume:

kubectl get pv task-pv-volume

Тепер вивід показує STATUS як Bound.

NAME             CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                   STORAGECLASS   REASON    AGE
task-pv-volume   10Gi       RWO           Retain          Bound     default/task-pv-claim   manual                   2m

Подивіться на PersistentVolumeClaim:

kubectl get pvc task-pv-claim

Вивід показує, що PersistentVolumeClaim привʼязаний до вашого PersistentVolume, task-pv-volume.

NAME            STATUS    VOLUME           CAPACITY   ACCESSMODES   STORAGECLASS   AGE
task-pv-claim   Bound     task-pv-volume   10Gi       RWO           manual         30s

Створення Podʼа

Наступним кроком є створення Podʼа, який використовує ваш PersistentVolumeClaim як том.

Ось файл конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: task-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
        claimName: task-pv-claim
  containers:
    - name: task-pv-container
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage


Зверніть увагу, що файл конфігурації Podʼа вказує PersistentVolumeClaim, але не вказує PersistentVolume. З погляду Podʼа, заявка є томом.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/storage/pv-pod.yaml

Перевірте, що контейнер у Podʼі працює:

kubectl get pod task-pv-pod

Відкрийте оболонку для контейнера, що працює у вашому Podʼі:

kubectl exec -it task-pv-pod -- /bin/bash

У вашій оболонці перевірте, що nginx обслуговує файл index.html з тому hostPath:

# Обовʼязково запустіть ці 3 команди всередині кореневої оболонки, яка є результатом
# виконання "kubectl exec" на попередньому кроці
apt update
apt install curl
curl http://localhost/

Вивід показує текст, який ви записали у файл index.html у томі hostPath:

Hello from Kubernetes storage

Якщо ви бачите це повідомлення, ви успішно налаштували Pod для використання зберігання з PersistentVolumeClaim.

Очищення

Видаліть Pod, PersistentVolumeClaim та PersistentVolume:

kubectl delete pod task-pv-pod
kubectl delete pvc task-pv-claim
kubectl delete pv task-pv-volume

Якщо у вас ще не відкрито оболонку до вузла у вашому кластері, відкрийте нову оболонку так само як ви робили це раніше.

У оболонці на вашому вузлі видаліть файл і теку, які ви створили:

# Це передбачає, що ваш вузол використовує "sudo" для виконання команд
# з правами суперкористувача
sudo rm /mnt/data/index.html
sudo rmdir /mnt/data

Тепер ви можете закрити оболонку доступу до вашого вузла.

Монтування одного PersistentVolume у два місця


apiVersion: v1
kind: Pod
metadata:
  name: test
spec:
  containers:
    - name: test
      image: nginx
      volumeMounts:
        # a mount for site-data
        - name: config
          mountPath: /usr/share/nginx/html
          subPath: html
        # another mount for nginx config
        - name: config
          mountPath: /etc/nginx/nginx.conf
          subPath: nginx.conf
  volumes:
    - name: config
      persistentVolumeClaim:
        claimName: test-nfs-claim

Ви можете виконати монтування томуу двох місцях у вашому контейнері nginx:

  • /usr/share/nginx/html для статичного вебсайту
  • /etc/nginx/nginx.conf для стандартної конфігурації

Контроль доступу

Зберігання даних із використанням ідентифікатора групи (GID) дозволяє запис лише для Podʼів, які використовують той самий GID. Невідповідність або відсутність GID призводить до помилок доступу. Щоб зменшити необхідність координації з користувачами, адміністратор може анотувати PersistentVolume з GID. Після цього GID автоматично додається до будь-якого Podʼа, який використовує цей PersistentVolume.

Використовуйте анотацію pv.beta.kubernetes.io/gid наступним чином:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
  annotations:
    pv.beta.kubernetes.io/gid: "1234"

Коли Pod використовує PersistentVolume з анотацією GID, анотований GID застосовується до всіх контейнерів у Podʼі так само як GID, зазначені у контексті безпеки Podʼа. Кожен GID, незалежно від того, чи походить він з анотації PersistentVolume або специфікації Podʼа, застосовується до першого процесу, запущеного в кожному контейнері.

Що далі

Довідка

3.11 - Налаштування Pod для використання projected тому для зберігання

Ця сторінка показує, як використовувати projected том, щоб змонтувати декілька наявних джерел томів у одну теку. Наразі можна проєктувати томи типів secret, configMap, downwardAPI, та serviceAccountToken.

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

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

Для перевірки версії введіть kubectl version.

Налаштування projected тому для Pod

У цьому завданні ви створите Secrets із локальних файлів для імені користувача та пароля. Потім ви створите Pod, який запускає один контейнер, використовуючи projected том для монтування секретів у спільну теку.

Ось файл конфігурації для Pod:

apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume
spec:
  containers:
  - name: test-projected-volume
    image: busybox:1.28
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: all-in-one
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass
  1. Створіть Secrets:

    # Створіть файли, що містять імʼя користувача та пароль:
    echo -n "admin" > ./username.txt
    echo -n "1f2d1e2e67df" > ./password.txt
    
    # Запакуйте ці файли у секрети:
    kubectl create secret generic user --from-file=./username.txt
    kubectl create secret generic pass --from-file=./password.txt
    
  2. Створіть Pod:

    kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
    
  3. Перевірте, що контейнер Pod запущено, а потім слідкуйте за змінами в Podʼі:

    kubectl get --watch pod test-projected-volume
    

    Вивід буде виглядати так:

    NAME                    READY     STATUS    RESTARTS   AGE
    test-projected-volume   1/1       Running   0          14s
    
  4. В іншому терміналі отримайте оболонку до запущеного контейнера:

    kubectl exec -it test-projected-volume -- /bin/sh
    
  5. У вашій оболонці перевірте, що тека projected-volume містить ваші projected джерела:

    ls /projected-volume/
    

Очищення

Видаліть Pod та Secrets:

kubectl delete pod test-projected-volume
kubectl delete secret user pass

Що далі

  • Дізнайтеся більше про projected томи.
  • Прочитайте документ про проєктування all-in-one volume.

3.12 - Налаштування контексту безпеки для Podʼа або контейнера

Контекст безпеки визначає параметри привілеїв та контролю доступу для Podʼа або контейнера. Налаштування контексту безпеки включають, але не обмежуються:

  • Дискреційний контроль доступу: Дозвіл на доступ до обʼєкта, такого як файл, базується на ідентифікаторі користувача (UID) та ідентифікаторі групи (GID).

  • Security Enhanced Linux (SELinux): Обʼєкти призначаються мітки безпеки.

  • Виконання з привілеями або без них.

  • Linux Capabilities: Надає процесу деякі привілеї, але не всі привілеї користувача root.

  • AppArmor: Використовуйте профілі програм для обмеження можливостей окремих програм.

  • Seccomp: Фільтрує системні виклики процесу.

  • allowPrivilegeEscalation: Контролює, чи може процес отримувати більше привілеїв, ніж його батьківський процес. Ця логічна величина безпосередньо контролює, чи встановлюється прапорець no_new_privs для процесу контейнера. allowPrivilegeEscalation завжди true, коли контейнер:

    • запущений з привілеями, або
    • має CAP_SYS_ADMIN
  • readOnlyRootFilesystem: Підключає кореневу файлову систему контейнера тільки для читання.

Вищевказані пункти не є повним набором налаштувань контексту безпеки,докладну інформацію див. у SecurityContext для повного переліку.

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

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

Для перевірки версії введіть kubectl version.

Встановлення контексту безпеки для Pod

Щоб вказати параметри безпеки для Podʼа, включіть поле securityContext в специфікацію Pod. Поле securityContext є обʼєктом PodSecurityContext. Параметри безпеки, які ви вказуєте для Pod, застосовуються до всіх контейнерів в Podʼі. Ось файл конфігурації для Podʼа з securityContext та томом emptyDir:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
    supplementalGroups: [4000]
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox:1.28
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false

У файлі конфігурації поле runAsUser вказує, що для будь-яких контейнерів в Podʼі, всі процеси виконуються з ідентифікатором користувача 1000. Поле runAsGroup вказує основний ідентифікатор групи 3000 для усіх процесів у контейнерах Pod. Якщо це поле відсутнє, основний ідентифікатор групи контейнерів буде root(0). Будь-які створені файли також належатимуть користувачу 1000 та групі 3000 при вказанні runAsGroup. Оскільки вказано поле fsGroup, всі процеси контейнера також належать до додаткової групи ідентифікатора 2000. Власником тому /data/demo та всіх створених файлів у цьому томі буде груповий ідентифікатор 2000. Додатково, коли вказане поле supplementalGroups, всі процеси контейнера також є частиною вказаних груп. Якщо це поле пропущене, це означає, що воно порожнє.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context.yaml

Перевірте, що контейнер Pod запущений:

kubectl get pod security-context-demo

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo -- sh

У вашій оболонці перелічіть запущені процеси:

ps

Виведений результат показує, що процеси виконуються від імені користувача 1000, що є значенням runAsUser:

PID   USER     TIME  COMMAND
    1 1000      0:00 sleep 1h
    6 1000      0:00 sh
...

У вашій оболонці перейдіть до /data, та виведіть список тек:

cd /data
ls -l

Виведений результат показує, що тека /data/demo має ідентифікатор групи 2000, що є значенням fsGroup.

drwxrwsrwx 2 root 2000 4096 Jun  6 20:08 demo

У вашій оболонці перейдіть до /data/demo, та створіть файл:

cd demo
echo hello > testfile

Виведіть список файлів у теці /data/demo:

ls -l

Виведений результат показує, що testfile має ідентифікатор групи 2000, що є значенням fsGroup.

-rw-r--r-- 1 1000 2000 6 Jun  6 20:08 testfile

Виконайте наступну команду:

id

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

uid=1000 gid=3000 groups=2000,3000,4000

З результату видно, що gid дорівнює 3000, що є таким самим, як поле runAsGroup. Якби поле runAsGroup було пропущено, gid залишився б 0 (root), і процес зміг би взаємодіяти з файлами, які належать групі root(0) та групам, які мають необхідні права групи для групи root (0). Ви також можете побачити, що groups містить ідентифікатори груп, які вказані в fsGroup і supplementalGroups, поряд з gid.

Вийдіть з оболонки:

exit

Неявні членства груп, визначені в /etc/group в контейнерному образі

Стандартно Kubernetes обʼєднує інформацію про групи з Podʼа з інформацією, визначеною в /etc/group в контейнерному образі.

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    supplementalGroups: [4000]
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      allowPrivilegeEscalation: false

Цей контекст безпеки Podʼа містить runAsUser, runAsGroup та supplementalGroups. Проте ви можете побачити, що фактичні додаткові групи, що прикріплені до процесу контейнера, включатимуть ідентифікатори груп, які походять з /etc/group всередині контейнерного образу.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-5.yaml

Перевірте, чи контейнер Pod запущений:

kubectl get pod security-context-demo

Отримайте оболонку для запущеного контейнера:

kubectl exec -it security-context-demo -- sh

Перевірте ідентичність процесу:

$ id

Вивід буде схожий на:

uid=1000 gid=3000 groups=3000,4000,50000

Ви можете побачити, що groups включає ідентифікатор групи 50000. Це тому, що користувач (uid=1000), який визначений в образі, належить до групи (gid=50000), яка визначена в /etc/group всередині контейнерного образу.

Перевірте /etc/group в контейнерному образі:

$ cat /etc/group

Ви побачите, що uid 1000 належить до групи 50000.

...
user-defined-in-image:x:1000:
group-defined-in-image:x:50000:user-defined-in-image

Вийдіть з оболонки:

exit

Налаштування SupplementalGroups для Podʼа

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [alpha]

Цю функцію можна увімкнути, встановивши функціональну можливість SupplementalGroupsPolicy для kubelet та kube-apiserver, а також налаштувавши поле .spec.securityContext.supplementalGroupsPolicy для Podʼа.

Поле supplementalGroupsPolicy визначає політику для розрахунку додаткових груп для процесів контейнера в Podʼі. Для цього поля є два дійсних значення:

  • Merge: Членство в групах, визначене в /etc/group для основного користувача контейнера, буде обʼєднано. Це є стандартною політикою, якщо не зазначено інше.

  • Strict: Тільки ідентифікатори груп у полях fsGroup, supplementalGroups або runAsGroup прикріплюються як додаткові групи для процесів контейнера. Це означає, що жодне членство в групах з /etc/group для основного користувача контейнера не буде обʼєднано.

Коли функція увімкнена, вона також надає ідентичність процесу, прикріплену до першого процесу контейнера в полі .status.containerStatuses[].user.linux. Це буде корисно для виявлення, чи прикріплені неявні ідентифікатори груп.

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    supplementalGroups: [4000]
    supplementalGroupsPolicy: Strict
  containers:
  - name: sec-ctx-demo
    image: registry.k8s.io/e2e-test-images/agnhost:2.45
    command: [ "sh", "-c", "sleep 1h" ]
    securityContext:
      allowPrivilegeEscalation: false

Цей маніфест Podʼа визначає supplementalGroupsPolicy=Strict. Ви можете побачити, що жодне членство в групах, визначене в /etc/group, не обʼєднується в додаткові групи для процесів контейнера.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-6.yaml

Перевірте, що контейнер Podʼа працює:

kubectl get pod security-context-demo

Перевірте ідентичність процесу:

kubectl exec -it security-context-demo -- id

Вивід буде подібним до цього:

uid=1000 gid=3000 groups=3000,4000

Перегляньте статус Podʼа:

kubectl get pod security-context-demo -o yaml

Ви можете побачити, що поле status.containerStatuses[].user.linux надає ідентичність процесу, прикріплену до першого процесу контейнера.

...
status:
  containerStatuses:
  - name: sec-ctx-demo
    user:
      linux:
        gid: 3000
        supplementalGroups:
        - 3000
        - 4000
        uid: 1000
...

Реалізації

Відомо, що наступні середовища виконання контейнерів підтримують контроль додаткових груп з тонкою настройкою.

На рівні CRI:

Ви можете перевірити, чи підтримується функція в статусі вузла.

apiVersion: v1
kind: Node
...
status:
  features:
    supplementalGroupsPolicy: true

Налаштування політики зміни дозволів та прав власності тому для Pod

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.23 [stable]

Типово Kubernetes рекурсивно змінює права власності та дозволи для вмісту кожного тому так, щоб вони відповідали значенню fsGroup, вказаному в securityContext Podʼа при підключенні цього тому. Для великих томів перевірка та зміна власності та дозволів може займати багато часу, сповільнюючи запуск Podʼів. Ви можете використовувати поле fsGroupChangePolicy в securityContext для контролю способу, яким Kubernetes перевіряє та керує власністю та дозволами для тому.

fsGroupChangePolicy — fsGroupChangePolicy визначає поведінку зміни власності та дозволів тому перед тим, як він буде використаний в Pod. Це поле застосовується лише до типів томів, які підтримують контроль власності та дозволів за допомогою fsGroup. Це поле має два можливі значення:

  • OnRootMismatch: Змінювати дозволи та права власності тільки у випадку, якщо дозволи та права кореневої теки не відповідають очікуваним дозволам тому. Це може допомогти скоротити час зміни власності та дозволів тому.
  • Always: Завжди змінювати дозволи та права власності тому під час підключення.

Наприклад:

securityContext:
  runAsUser: 1000
  runAsGroup: 3000
  fsGroup: 2000
  fsGroupChangePolicy: "OnRootMismatch"

Делегування зміни прав власності та дозволів тому до драйвера CSI

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [stable]

Якщо ви розгортаєте драйвер Container Storage Interface (CSI), який підтримує VOLUME_MOUNT_GROUP NodeServiceCapability, процес встановлення права власності та дозволів файлу на основі fsGroup, вказаного в securityContext, буде виконуватися драйвером CSI, а не Kubernetes. У цьому випадку, оскільки Kubernetes не виконує жодної зміни права власності та дозволів, fsGroupChangePolicy не набуває чинності, і згідно з вказаним CSI, очікується, що драйвер монтує том з наданим fsGroup, що призводить до отримання тому, який є доступними для читаання/запису для fsGroup.

Встановлення контексту безпеки для контейнера

Для вказання параметрів безпеки для контейнера, включіть поле securityContext в маніфест контейнера. Поле securityContext є обʼєктом SecurityContext. Параметри безпеки, які ви вказуєте для контейнера, застосовуються тільки до окремого контейнера, і вони перевизначають налаштування, зроблені на рівні Podʼа, коли є перетин. Налаштування контейнера не впливають на томи Podʼів.

Ось файл конфігурації для Podʼа з одним контейнером. Як Pod, так і контейнер мають поле securityContext:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-2
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: sec-ctx-demo-2
    image: gcr.io/google-samples/hello-app:2.0
    securityContext:
      runAsUser: 2000
      allowPrivilegeEscalation: false

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-2.yaml

Перевірте, що контейнер Pod запущений:

kubectl get pod security-context-demo-2

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo-2 -- sh

У вашій оболонці перегляньте запущені процеси:

ps aux

Виведений результат показує, що процеси виконуються від імені користувача 2000. Це значення runAsUser, вказане для контейнера. Воно перевизначає значення 1000, вказане для Pod.

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
2000         1  0.0  0.0   4336   764 ?        Ss   20:36   0:00 /bin/sh -c node server.js
2000         8  0.1  0.5 772124 22604 ?        Sl   20:36   0:00 node server.js
...

Вийдіть з оболонки:

exit

Встановлення можливостей для контейнера

За допомогою можливостей Linux, ви можете надати певні привілеї процесу, не надаючи всі привілеї користувачеві з правами root. Щоб додати або видалити можливості Linux для контейнера, включіть поле capabilities в розділ securityContext маніфесту контейнера.

Спочатку подивімося, що відбувається, коли ви не включаєте поле capabilities. Ось файл конфігурації, який не додає або не видаляє жодних можливостей контейнера:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-3
spec:
  containers:
  - name: sec-ctx-3
    image: gcr.io/google-samples/hello-app:2.0

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-3.yaml

Перевірте, що контейнер Pod запущений:

kubectl get pod security-context-demo-3

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo-3 -- sh

У вашій оболонці перегляньте запущені процеси:

ps aux

Виведений результат показує ідентифікатори процесів (PID) для контейнера:

USER  PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND
root    1  0.0  0.0   4336   796 ?     Ss   18:17   0:00 /bin/sh -c node server.js
root    5  0.1  0.5 772124 22700 ?     Sl   18:17   0:00 node server.js

У вашій оболонці перегляньте статус для процесу 1:

cd /proc/1
cat status

Виведений результат показує bitmap можливостей для процесу:

...
CapPrm:	00000000a80425fb
CapEff:	00000000a80425fb
...

Запамʼятайте bitmap можливостей, а потім вийдіть з оболонки:

exit

Далі, запустіть контейнер, який є такий самий, як попередній контейнер, за винятком того, що він має додаткові можливості.

Ось файл конфігурації для Pod, який запускає один контейнер. Конфігурація додає можливості CAP_NET_ADMIN та CAP_SYS_TIME:

apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-4
spec:
  containers:
  - name: sec-ctx-4
    image: gcr.io/google-samples/hello-app:2.0
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/security/security-context-4.yaml

Отримайте оболонку до запущеного контейнера:

kubectl exec -it security-context-demo-4 -- sh

У вашій оболонці перегляньте можливості для процесу 1:

cd /proc/1
cat status

Виведений результат показує бітову карту можливостей для процесу:

...
CapPrm:	00000000aa0435fb
CapEff:	00000000aa0435fb
...

Порівняйте можливості двох контейнерів:

00000000a80425fb
00000000aa0435fb

У bitmap можливостей першого контейнера біти 12 і 25 не встановлені. У другому контейнері, біти 12 і 25 встановлені. Біт 12 — це CAP_NET_ADMIN, а біт 25 — це CAP_SYS_TIME. Дивіться capability.h для визначень констант можливостей.

Встановлення профілю Seccomp для контейнера

Щоб встановити профіль Seccomp для контейнера, включіть поле seccompProfile в розділ securityContext вашого маніфесту Pod або контейнера. Поле seccompProfile є обʼєктом SeccompProfile, який складається з type та localhostProfile. Допустимі варіанти для type включають RuntimeDefault, Unconfined та Localhost. localhostProfile повинен бути встановлений лише якщо type: Localhost. Він вказує шлях до попередньо налаштованого профілю на вузлі, повʼязаного з розташуванням налаштованого профілю Seccomp kubelet (налаштованого за допомогою прапорця --root-dir).

Ось приклад, який встановлює профіль Seccomp до стандартного профілю контейнера вузла:

...
securityContext:
  seccompProfile:
    type: RuntimeDefault

Ось приклад, який встановлює профіль Seccomp до попередньо налаштованого файлу за шляхом <kubelet-root-dir>/seccomp/my-profiles/profile-allow.json:

...
securityContext:
  seccompProfile:
    type: Localhost
    localhostProfile: my-profiles/profile-allow.json

Налаштування профілю AppArmor для контейнера

Щоб налаштувати профіль AppArmor для контейнера, включіть поле appArmorProfile в секцію securityContext вашого контейнера. Поле appArmorProfile є обʼєктом AppArmorProfile, що складається з type та localhostProfile. Дійсні опції для type включають RuntimeDefault (стандартно), Unconfined і Localhost. localhostProfile слід встановлювати тільки якщо type є Localhost. Це вказує на назву попередньо налаштованого профілю на вузлі. Профіль повинен бути завантажений на всіх вузлах, які підходять для Podʼа, оскільки ви не знаєте, де буде розгорнуто Pod. Підходи до налаштування власних профілів обговорюються в Налаштування вузлів з профілями.

Примітка: Якщо containers[*].securityContext.appArmorProfile.type явно встановлено на RuntimeDefault, то Pod не буде допущено, якщо AppArmor не включено на вузлі. Однак, якщо containers[*].securityContext.appArmorProfile.type не зазначено, то стандартне значення (що також є RuntimeDefault) буде застосовано тільки якщо вузол має увімкнений AppArmor. Якщо вузол має вимкнений AppArmor, Pod буде допущено, але контейнер не буде обмежено профілем RuntimeDefault.

Ось приклад, який встановлює профіль AppArmor на стандартний профіль контейнерного середовища вузла:

...
containers:
- name: container-1
  securityContext:
    appArmorProfile:
      type: RuntimeDefault

Ось приклад, який встановлює профіль AppArmor на попередньо налаштований профіль з назвою k8s-apparmor-example-deny-write:

...
containers:
- name: container-1
  securityContext:
    appArmorProfile:
      type: Localhost
      localhostProfile: k8s-apparmor-example-deny-write

Для отримання додаткової інформації дивіться Обмеження доступу контейнера до ресурсів з AppArmor.

Призначення міток SELinux контейнеру

Щоб призначити мітки SELinux контейнеру, включіть поле seLinuxOptions в розділ securityContext вашого маніфесту Podʼа або контейнера. Поле seLinuxOptions є обʼєктом SELinuxOptions. Ось приклад, який застосовує рівень SELinux:

...
securityContext:
  seLinuxOptions:
    level: "s0:c123,c456"

Ефективне переозначення обʼєктів SELinux в томах

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.28 [beta]

Стандартно, контейнерне середовище рекурсивно призначає мітку SELinux для всіх файлів на всіх томах Pod. Щоб прискорити цей процес, Kubernetes може миттєво змінити мітку SELinux тому за допомогою параметра монтування -o context=<мітка>.

Щоб скористатися цим прискоренням, мають бути виконані всі ці умови:

  • Функціональні можливості ReadWriteOncePod та SELinuxMountReadWriteOncePod повинні бути увімкнені.
  • Pod повинен використовувати PersistentVolumeClaim з відповідними accessModes та функціональною можливстю:
    • Або том має accessModes: ["ReadWriteOncePod"], і властивість включення SELinuxMountReadWriteOncePod увімкнена.
    • Або том може використовувати будь-які інші режими доступу та обидва SELinuxMountReadWriteOncePod та SELinuxMount повинні бути увімкнені.
  • Pod (або всі його контейнери, які використовують PersistentVolumeClaim) повинні мати встановлені параметри seLinuxOptions.
  • Відповідний PersistentVolume повинен бути або:
    • Том, який використовує старі типи томів iscsi, rbd або fc.
    • Або том, який використовує драйвер CSI CSI. Драйвер CSI повинен оголосити, що він підтримує монтування з -o context, встановивши spec.seLinuxMount: true у його екземплярі CSIDriver.

Для будь-яких інших типів томів переозначення SELinux відбувається іншим шляхом: контейнерне середовище рекурсивно змінює мітку SELinux для всіх inodes (файлів і тек) у томі. Чим більше файлів і тек у томі, тим довше відбувається це переозначення.

Управління доступом до файлової системи /proc

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.12 [alpha]

Для середовищ виконання, які слідують специфікації виконання OCI, контейнери типово запускаються у режимі, де є кілька шляхів, які промасковані та доступні тільки для читання. Результатом цього є те, що в межах простору імен монтування контейнера присутні ці шляхи, і вони можуть працювати подібно до того, якби контейнер був ізольованим хостом, але процес контейнера не може записувати до них. Список промаскованих і доступних тільки для читання шляхів такий:

  • Промасковані шляхи:

    • /proc/asound
    • /proc/acpi
    • /proc/kcore
    • /proc/keys
    • /proc/latency_stats
    • /proc/timer_list
    • /proc/timer_stats
    • /proc/sched_debug
    • /proc/scsi
    • /sys/firmware
    • /sys/devices/virtual/powercap
  • Шляхи доступні тільки для читання:

    • /proc/bus
    • /proc/fs
    • /proc/irq
    • /proc/sys
    • /proc/sysrq-trigger

Для деяких Podʼів вам може знадобитися обійти стандартний шлях маскування. Найбільш поширений контекст, коли це потрібно, — це спроба запуску контейнерів у межах контейнера Kubernetes (у межах Podʼа).

Поле procMount в securityContext дозволяє користувачеві запитати, щоб /proc контейнера був Unmasked, або міг бути підмонтований для читання-запису контейнерним процесом. Це також стосується /sys/firmware, який не знаходиться в /proc.

...
securityContext:
  procMount: Unmasked

Обговорення

Контекст безпеки для Pod застосовується до Контейнерів Pod і також до Томів Pod при необхідності. Зокрема, fsGroup та seLinuxOptions застосовуються до Томів наступним чином:

  • fsGroup: Томи, які підтримують управління власністю, модифікуються так, щоб бути власністю та доступними для запису за GID, вказаним у fsGroup. Докладніше див. Документ із проєктування управління власністю томів.

  • seLinuxOptions: Томи, які підтримують мітку SELinux, переозначаються так, щоб бути доступними за міткою, вказаною у seLinuxOptions. Зазвичай вам потрібно лише встановити розділ level. Це встановлює мітку Multi-Category Security (MCS), яку отримують всі Контейнери у Pod, а також Томи.

Очищення

Видаліть Pod:

kubectl delete pod security-context-demo
kubectl delete pod security-context-demo-2
kubectl delete pod security-context-demo-3
kubectl delete pod security-context-demo-4

Що далі

3.13 - Налаштування службових облікових записів для Podʼів

Kubernetes пропонує два відмінні способи автентифікації для клієнтів, які працюють у межах вашого кластера або мають взаємозвʼязок з панеллю управління вашого кластера для автентифікації в API-сервері.

Службовий обліковий запис надає ідентичність процесам, які працюють у Podʼі, і відповідає обʼєкту ServiceAccount. Коли ви автентифікуєтеся в API-сервері, ви ідентифікуєте себе як певного користувача. Kubernetes визнає поняття користувача, але сам Kubernetes не має API User.

Це завдання стосується Службових облікових записів, які існують в API Kubernetes. Керівництво показує вам деякі способи налаштування Службових облікових записів для Podʼів.

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

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

Використання стандартного службового облікового запису для доступу до API-сервера

Коли Podʼи звертаються до API-сервера, вони автентифікуються як певний Службовий обліковий запис (наприклад, default). В кожному просторі імен завжди є принаймні один Службовий обліковий запис.

У кожному просторі імен Kubernetes завжди міститься принаймні один Службовий обліковий запис: стандартний службовий обліковий запис для цього простору імен, з назвою default. Якщо ви не вказуєте Службовий обліковий запис при створенні Podʼа, Kubernetes автоматично призначає Службовий обліковий запис з назвою default у цьому просторі імен.

Ви можете отримати деталі для Podʼа, який ви створили. Наприклад:

kubectl get pods/<імʼя_пода> -o yaml

У виводі ви побачите поле spec.serviceAccountName. Kubernetes автоматично встановлює це значення, якщо ви не вказали його при створенні Podʼа.

Застосунок, який працює усередині Podʼа, може отримати доступ до API Kubernetes, використовуючи автоматично змонтовані облікові дані службового облікового запису. Для отримання додаткової інформації див. доступ до кластера.

Коли Pod автентифікується як Службовий обліковий запис, його рівень доступу залежить від втулка авторизації та політики, які використовуються.

Облікові дані API автоматично відкликаються, коли Pod видаляється, навіть якщо є завершувачі. Зокрема, облікові дані API відкликаються через 60 секунд після встановленого на Pod значення .metadata.deletionTimestamp (час видалення зазвичай дорівнює часу, коли запит на видалення був прийнятий плюс період належного завершення роботи Pod).

Відмова від автоматичного монтування облікових даних API

Якщо ви не бажаєте, щоб kubelet автоматично монтував облікові дані API ServiceAccount, ви можете відмовитися від такої стандартної поведінки. Ви можете відмовитися від автоматичного монтування облікових даних API у /var/run/secrets/kubernetes.io/serviceaccount/token для службового облікового запису, встановивши значення automountServiceAccountToken: false у ServiceAccount:

Наприклад:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...

Ви також можете відмовитися від автоматичного монтування облікових даних API для певного Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...

Якщо як ServiceAccount, так і .spec Podʼа вказують значення для automountServiceAccountToken, специфікація Podʼа має перевагу.

Використання більше ніж одного ServiceAccount

У кожному просторі імен є принаймні один ServiceAccount: типовий ServiceAccount, який називається default. Ви можете переглянути всі ресурси ServiceAccount у вашому поточному просторі імен за допомогою:

kubectl get serviceaccounts

Вихідні дані схожі на наступні:

NAME      SECRETS    AGE
default   1          1d

Ви можете створити додаткові обʼєкти ServiceAccount таким чином:

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
EOF

Імʼя обʼєкта ServiceAccount повинно бути дійсним DNS-піддоменним імʼям.

Якщо ви отримуєте повний дамп обʼєкта ServiceAccount, подібний до цього:

kubectl get serviceaccounts/build-robot -o yaml

Вихідні дані схожі на наступні:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2019-06-16T00:12:34Z
  name: build-robot
  namespace: default
  resourceVersion: "272500"
  uid: 721ab723-13bc-11e5-aec2-42010af0021e

Ви можете використовувати розширення дозволів для встановлення дозволів на облікові записи служб.

Щоб використовувати не-стандартний обліковий запис, встановіть поле spec.serviceAccountName Podʼа на імʼя ServiceAccount, який ви хочете використовувати.

Ви можете встановити лише поле serviceAccountName при створенні Podʼа або в шаблоні для нового Podʼа. Ви не можете оновлювати поле .spec.serviceAccountName Podʼа, який вже існує.

Очищення

Якщо ви спробували створити ServiceAccount build-robot з прикладу вище, ви можете видалити його виконавши:

kubectl delete serviceaccount/build-robot

Вручну створіть API-токен для ServiceAccount

Припустимо, у вас вже є службовий обліковий запис з назвою "build-robot", як зазначено вище.

Ви можете отримати тимчасовий API-токен для цього ServiceAccount за допомогою kubectl:

kubectl create token build-robot

Вихідні дані з цієї команди — це токен, який ви можете використовувати для автентифікації цього ServiceAccount. Ви можете запросити певний час дії токена, використовуючи аргумент командного рядка --duration для kubectl create token (фактичний час дії виданого токену може бути коротшим або навіть довшим).

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [beta]

Коли увімкнено функції ServiceAccountTokenNodeBinding і ServiceAccountTokenNodeBindingValidation, а також використовується kubectl версії 1.31 або пізнішої, можна створити токен службового облікового запису, який безпосередньо привʼязаний до Node.

kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456

Токен буде чинний до закінчення його терміну дії або до видалення відповідного вузла чи службового облікового запису.

Вручну створіть довговічний API-токен для ServiceAccount

Якщо ви бажаєте отримати API-токен для службового облікового запису, ви створюєте новий Secret з особливою анотацією kubernetes.io/service-account.name.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
EOF

Якщо ви переглянете Secret використовуючи:

kubectl get secret/build-robot-secret -o yaml

ви побачите, що тепер Secret містить API-токен для ServiceAccount "build-robot".

Через анотацію, яку ви встановили, панель управління автоматично генерує токен для цього службового облікового запису і зберігає їх у відповідному Secret. Крім того, панель управління також очищає токени для видалених облікових записів служб.

kubectl describe secrets/build-robot-secret

Вивід схожий на такий:

Name:           build-robot-secret
Namespace:      default
Labels:         <none>
Annotations:    kubernetes.io/service-account.name: build-robot
                kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1338 байтів
namespace:      7 байтів
token:          ...

При видаленні ServiceAccount, який має відповідний Secret, панель управління Kubernetes автоматично очищає довговічний токен з цього Secret.

Додайте ImagePullSecrets до ServiceAccount

Спочатку створіть imagePullSecret. Потім перевірте, чи він був створений. Наприклад:

  • Створіть imagePullSecret, як описано в Вказування ImagePullSecrets в контейнері.

    kubectl create secret docker-registry myregistrykey --docker-server=<імʼя реєстру> \
            --docker-username=ІМ'Я_КОРИСТУВАЧА --docker-password=ПАРОЛЬ_ДЛЯ_DOCKER \
            --docker-email=ЕЛЕКТРОННА_ПОШТА_ДЛЯ_DOCKER
    
  • Перевірте, чи він був створений.

    kubectl get secrets myregistrykey
    

    Вивід схожий на такий:

    NAME             TYPE                              DATA    AGE
    myregistrykey    kubernetes.io/.dockerconfigjson   1       1д
    

Додайте imagePullSecret до ServiceAccount

Далі, змініть типовий обліковий запис служби для цього простору імен так, щоб він використовував цей Secret як imagePullSecret.

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'

Ви можете досягти того ж самого результату, відредагувавши обʼєкт вручну:

kubectl edit serviceaccount/default

Вивід файлу sa.yaml буде схожий на такий:

Вибраний вами текстовий редактор відкриється з конфігурацією, що схожа на цю:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  resourceVersion: "243024"
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6

За допомогою вашого редактора видаліть рядок з ключем resourceVersion, додайте рядки для imagePullSecrets: та збережіть це. Залиште значення uid таким же, як ви його знайшли.

Після внесення змін, відредагований ServiceAccount виглядатиме схоже на це:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
imagePullSecrets:
  - name: myregistrykey

Перевірте, що imagePullSecrets встановлені для нових Podʼів

Тепер, коли створюється новий Pod у поточному просторі імен і використовується типовий ServiceAccount, у новому Podʼі автоматично встановлюється поле spec.imagePullSecrets:

kubectl run nginx --image=<імʼя реєстру>/nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'

Вивід:

myregistrykey

Проєцювання токенів ServiceAccount

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.20 [stable]

Kubelet також може проєцювати токен ServiceAccount в Pod. Ви можете вказати бажані властивості токена, такі як аудиторія та тривалість дії. Ці властивості не конфігуруються для типового токена ServiceAccount. Токен також стане недійсним щодо API, коли будь-який з Podʼів або ServiceAccount буде видалено.

Ви можете налаштувати цю поведінку для spec Podʼа за допомогою типу projected тому, що називається ServiceAccountToken.

Токен з цього projected тому — JSON Web Token (JWT). JSON-вміст цього токена слідує чітко визначеній схемі — приклад вмісту для токена, повʼязаного з Pod:

{
  "aud": [  # відповідає запитаним аудиторіям або стандартним аудиторіям API-сервера, якщо явно не запитано
    "https://kubernetes.default.svc"
  ],
  "exp": 1731613413,
  "iat": 1700077413,
  "iss": "https://kubernetes.default.svc",  # відповідає першому значенню, переданому прапорцю --service-account-issuer
  "jti": "ea28ed49-2e11-4280-9ec5-bc3d1d84661a",  # Функція ServiceAccountTokenJTI повинна бути активована для того, щоб вимагати присутності цього запиту
  "kubernetes.io": {
    "namespace": "kube-system",
    "node": {  # Функція ServiceAccountTokenPodNodeInfo повинна бути активована для того, щоб API-сервер додавав цей запит посилання на вузол
      "name": "127.0.0.1",
      "uid": "58456cb0-dd00-45ed-b797-5578fdceaced"
    },
    "pod": {
      "name": "coredns-69cbfb9798-jv9gn",
      "uid": "778a530c-b3f4-47c0-9cd5-ab018fb64f33"
    },
    "serviceaccount": {
      "name": "coredns",
      "uid": "a087d5a0-e1dd-43ec-93ac-f13d89cd13af"
    },
    "warnafter": 1700081020
  },
  "nbf": 1700077413,
  "sub": "system:serviceaccount:kube-system:coredns"
}

Запуск Podʼа з використанням проєцювання токену службового облікового запису

Щоб надати Podʼу токен з аудиторією vault та терміном дії дві години, ви можете визначити маніфест Podʼа, схожий на цей:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200
          audience: vault

Створіть Pod:

kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml

Kubelet буде: запитувати та зберігати токен від імені Podʼа; робити токен доступним для Podʼа за налаштованим шляхом до файлу; і оновлювати токен поблизу його закінчення. Kubelet активно запитує ротацію для токена, якщо він старший, ніж 80% від загального часу життя (TTL), або якщо токен старший, ніж 24 години.

Застосунок відповідає за перезавантаження токена при його ротації. Зазвичай для застосунку достатньо завантажувати токен за розкладом (наприклад: один раз кожні 5 хвилин), без відстеження фактичного часу закінчення.

Виявлення емітента службового облікового запису

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.21 [stable]

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

Якщо увімкнено, Kubernetes API-сервер публікує документ конфігурації постачальника OpenID через HTTP. Документ конфігурації публікується за адресою /.well-known/openid-configuration. Документ конфігурації OpenID постачальника іноді називається документом виявлення. Kubernetes API-сервер також публікує повʼязаний набір ключів JSON Web (JWKS), також через HTTP, за адресою /openid/v1/jwks.

Кластери, які використовують RBAC, включають типову роль кластера з назвою system:service-account-issuer-discovery. Типовий ClusterRoleBinding надає цю роль групі system:serviceaccounts, до якої неявно належать всі ServiceAccounts. Це дозволяє Podʼам, які працюють у кластері, отримувати доступ до документа виявлення службового облікового запису через їх змонтований токен службового облікового запису. Адміністратори також можуть вибрати привʼязку ролі до system:authenticated або system:unauthenticated залежно від їх вимог до безпеки та зовнішніх систем, з якими вони мають намір обʼєднуватись.

Відповідь JWKS містить публічні ключі, які може використовувати залежна сторона для перевірки токенів службових облікових записів Kubernetes. Залежні сторони спочатку запитують конфігурацію постачальника OpenID, а потім використовують поле jwks_uri у відповіді, щоб знайти JWKS.

У багатьох випадках API-сервери Kubernetes не доступні через глобальну мережу, але публічні точки доступу, які обслуговують кешовані відповіді від API-сервера, можуть бути доступні для користувачів або постачальників послуг. У таких випадках можливо перевизначити jwks_uri в конфігурації постачальника OpenID, щоб вона вказувала на глобальну точку доступу, а не на адресу API-сервера, передаючи прапорець --service-account-jwks-uri до API-сервера. Як і URL емітента, URI JWKS повинен використовувати схему https.

Що далі

Дивіться також:

3.14 - Отримання образів з приватного реєстру

Ця сторінка показує, як створити Pod, що використовує Secret для отримання образу з приватного реєстру або сховища контейнерних образів. Існує багато приватних реєстрів, які використовуються. У цьому завданні використовується Docker Hub як приклад реєстру.

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

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

  • Для виконання цієї вправи вам потрібно мати інструмент командного рядка docker, а також ідентифікатор Docker, та пароль до якого ви знаєте.

  • Якщо ви використовуєте інший приватний контейнерний реєстр, вам потрібен інструмент командного рядка для цього реєстру та будь-яка інформація для входу в реєстр.

Увійдіть до Docker Hub

На вашому компʼютері вам необхідно автентифікуватися в реєстрі, щоб отримати приватний образ.

Використовуйте інструмент docker, щоб увійти до Docker Hub. Докладніше про це дивіться у розділі log in на сторінці Docker ID accounts.

docker login

Коли буде запитано, введіть свій ідентифікатор Docker, а потім обрані вами облікові дані (токен доступу чи пароль до вашого Docker ID).

Процес входу створює або оновлює файл config.json, який містить токен авторизації. Ознайомтеся з тим, як Kubernetes інтерпретує цей файл.

Перегляньте файл config.json:

cat ~/.docker/config.json

Вивід містить секцію, подібну до цієї:

{
    "auths": {
        "https://index.docker.io/v1/": {
            "auth": "c3R...zE2"
        }
    }
}

Створення Secret на основі наявних облікових даних

Кластер Kubernetes використовує Secret типу kubernetes.io/dockerconfigjson для автентифікації в контейнерному реєстрі для отримання приватного образу.

Якщо ви вже виконали команду docker login, ви можете скопіювати ці облікові дані в Kubernetes:

kubectl create secret generic regcred \
    --from-file=.dockerconfigjson=<шлях/до/.docker/config.json> \
    --type=kubernetes.io/dockerconfigjson

Якщо вам потрібно більше контролю (наприклад, встановити простір імен чи мітку для нового Secret), то ви можете налаштувати Secret перед збереженням його. Переконайтеся, що:

  • встановлено назву елемента даних як .dockerconfigjson
  • файл конфігурації Docker закодовано у base64, а потім вставлено цей рядок без розривів як значення для поля data[".dockerconfigjson"]
  • встановлено type як kubernetes.io/dockerconfigjson

Приклад:

apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
  namespace: awesomeapps
data:
  .dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson

Якщо ви отримали повідомлення про помилку error: no objects passed to create, це може означати, що закодований у base64 рядок є недійсним. Якщо ви отримали повідомлення про помилку, подібне до Secret "myregistrykey" is invalid: data[.dockerconfigjson]: invalid value ..., це означає, що закодований у base64 рядок у даних успішно декодувався, але не може бути розпізнаний як файл .docker/config.json.

Створення Secret, за допомогою вводу облікових даних в командному рядку

Створіть цей Secret, назвавши його regcred:

kubectl create secret docker-registry regcred \
    --docker-server=<your-registry-server> \
    --docker-username=<your-name> \
    --docker-password=<your-pword> \
    --docker-email=<your-email>

де:

  • <your-registry-server> — це повна доменна назва вашого приватного реєстру Docker. Використовуйте https://index.docker.io/v1/ для DockerHub.
  • <your-name> — це ваше імʼя користувача Docker.
  • <your-pword> — це ваш пароль Docker.
  • <your-email> — це ваша електронна адреса Docker.

Ви успішно встановили ваші облікові дані Docker у кластері як Secret під назвою regcred.

Перегляд Secret regcred

Щоб зрозуміти вміст Secret regcred, який ви створили, спочатку перегляньте Secret у форматі YAML:

kubectl get secret regcred --output=yaml

Вивід подібний до такого:

apiVersion: v1
kind: Secret
metadata:
  ...
  name: regcred
  ...
data:
  .dockerconfigjson: eyJodHRwczovL2luZGV4L ... J0QUl6RTIifX0=
type: kubernetes.io/dockerconfigjson

Значення поля .dockerconfigjson — це представлення в base64 ваших облікових даних Docker.

Щоб зрозуміти, що знаходиться у полі .dockerconfigjson, конвертуйте дані Secret в читабельний формат:

kubectl get secret regcred --output="jsonpath={.data.\.dockerconfigjson}" | base64 --decode

Вивід подібний до такого:

{"auths":{"your.private.registry.example.com":{"username":"janedoe","password":"xxxxxxxxxxx","email":"jdoe@example.com","auth":"c3R...zE2"}}}

Щоб зрозуміти, що знаходиться у полі auth, конвертуйте дані, закодовані в base64, у читабельний формат:

echo "c3R...zE2" | base64 --decode

Вивід, імʼя користувача та пароль, зʼєднані через :, подібний до такого:

janedoe:xxxxxxxxxxx

Зверніть увагу, що дані Secret містять токен авторизації, аналогічний вашому локальному файлу ~/.docker/config.json.

Ви успішно встановили ваші облікові дані Docker як Secret з назвою regcred у кластері.

Створення Pod, який використовує ваш Secret

Нижче подано опис для прикладу Pod, який потребує доступу до ваших облікових даних Docker у regcred:

apiVersion: v1
kind: Pod
metadata:
  name: private-reg
spec:
  containers:
  - name: private-reg-container
    image: <your-private-image>
  imagePullSecrets:
  - name: regcred

Завантажте вищезазначений файл на свій компʼютер:

curl -L -o my-private-reg-pod.yaml https://k8s.io/examples/pods/private-reg-pod.yaml

У файлі my-private-reg-pod.yaml замініть <your-private-image> на шлях до образу у приватному реєстрі, наприклад:

your.private.registry.example.com/janedoe/jdoe-private:v1

Для отримання образу з приватного реєстру Kubernetes потрібні облікові дані. Поле imagePullSecrets у файлі конфігурації вказує, що Kubernetes повинен отримати облікові дані з Secret з назвою regcred.

Створіть Pod, який використовує ваш Secret, і перевірте, що Pod працює:

kubectl apply -f my-private-reg-pod.yaml
kubectl get pod private-reg

Також, якщо запуск Podʼа не вдається і ви отримуєте статус ImagePullBackOff, перегляньте події Pod:

kubectl describe pod private-reg

Якщо ви побачите подію з причиною, встановленою на FailedToRetrieveImagePullSecret, Kubernetes не може знайти Secret із назвою (regcred, у цьому прикладі). Якщо ви вказали, що Pod потребує облікових даних для отримання образів, kubelet перевіряє, чи може він отримати доступ до цього Secret, перед тим як спробувати отримати образ.

Переконайтеся, що вказаний вами Secret існує і що його назва вірно вказана.

Events:
  ...  Reason                           ...  Message
       ------                                -------
  ...  FailedToRetrieveImagePullSecret  ...  Unable to retrieve some image pull secrets (<regcred>); attempting to pull the image may not succeed.

Що далі

3.15 - Налаштування проб життєздатності, готовності та запуску

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

Для отримання додаткової інформації про проби див. Проби життєздатності, готовності та запуску

Kubelet використовує проби життєздатності для визначення моменту перезапуску контейнера. Наприклад, проби життєздатності можуть виявити проблеми, коли застосунок працює, але не може досягти результату. Перезапуск контейнера в такому стані може допомогти зробити застосунок більш доступним, попри помилки.

Загальний шаблон для проб життєздатності — використовувати той самий недорогу HTTP-точку доступу, що й для проб готовності, але з більшим значенням failureThreshold. Це гарантує, що Pod може спостерігатись як не готовий впродовж певного часу перед тим, як примусово завершити його роботу.

Kubelet використовує проби готовності для визначення моменту, коли контейнер готовий приймати трафік. Pod вважається готовим, коли всі його контейнери готові. Одним з використань цього сигналу є контроль за тим, які Podʼи використовуються як бекенди для Service. Коли Pod не готовий, його видаляють з балансувальників навантаження Serviceʼів.

Kubelet використовує проби запуску для визначення моменту запуску застосунку контейнера. Якщо така проба налаштована, проби життєздатності та готовності не починають працювати, поки вона не успішна, щоб переконатися, що ці проби не перешкоджають запуску застосунку. Це може бути використано для впровадження перевірки життєздатності для повільних контейнерів, що дозволяє уникати їхнього вимкнення kubelet перед тим, як вони будуть готові до роботи.

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

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

Визначте команду життєздатності

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

У цьому завданні ви створите Pod, який запускає контейнер на основі образу registry.k8s.io/busybox. Ось файл конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

У файлі конфігурації можна побачити, що у Podʼа є один Container. Поле periodSeconds вказує, що kubelet повинен виконувати пробу життєздатності кожні 5 секунд. Поле initialDelaySeconds повідомляє kubelet, що він повинен зачекати 5 секунд перед виконанням першої проби. Для виконання проби kubelet виконує команду cat /tmp/healthy у цільовому контейнері. Якщо команда успішно виконується, вона повертає 0, і kubelet вважає контейнер живим і справним. Якщо команда повертає ненульове значення, kubelet примусово зупиняє контейнер і перезапускає його.

Під час запуску контейнера виконується ця команда:

/bin/sh -c "touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600"

Протягом перших 30 секунд життя контейнера існує файл /tmp/healthy. Таким чином, протягом перших 30 секунд команда cat /tmp/healthy повертає код успіху. Після 30 секунд cat /tmp/healthy повертає код невдачі.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/probe/exec-liveness.yaml

Протягом 30 секунд перегляньте події Podʼа:

kubectl describe pod liveness-exec

Виведений текст показує, що жодна проба життєздатності ще не зазнав невдачі:

Type    Reason     Age   From               Message
----    ------     ----  ----               -------
Normal  Scheduled  11s   default-scheduler  Successfully assigned default/liveness-exec to node01
Normal  Pulling    9s    kubelet, node01    Pulling image "registry.k8s.io/busybox"
Normal  Pulled     7s    kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
Normal  Created    7s    kubelet, node01    Created container liveness
Normal  Started    7s    kubelet, node01    Started container liveness

Після 35 секунд знову перегляньте події Podʼа:

kubectl describe pod liveness-exec

У нижній частині виводу є повідомлення про те, що проби життєздатності зазнали невдачі, і непрацездатні контейнери були примусово зупинені та перезапущені.

Type     Reason     Age                From               Message
----     ------     ----               ----               -------
Normal   Scheduled  57s                default-scheduler  Successfully assigned default/liveness-exec to node01
Normal   Pulling    55s                kubelet, node01    Pulling image "registry.k8s.io/busybox"
Normal   Pulled     53s                kubelet, node01    Successfully pulled image "registry.k8s.io/busybox"
Normal   Created    53s                kubelet, node01    Created container liveness
Normal   Started    53s                kubelet, node01    Started container liveness
Warning  Unhealthy  10s (x3 over 20s)  kubelet, node01    Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal   Killing    10s                kubelet, node01    Container liveness failed liveness probe, will be restarted

Почекайте ще 30 секунд та перевірте, що контейнер був перезапущений:

kubectl get pod liveness-exec

Виведений текст показує, що RESTARTS було збільшено. Зауважте, що лічильник RESTARTS збільшується, як тільки непрацездатний контейнер знову переходить у стан виконання:

NAME            READY     STATUS    RESTARTS   AGE
liveness-exec   1/1       Running   1          1m

Визначення HTTP-запиту життєздатності

Ще один вид проб життєздатності використовує HTTP GET-запит. Ось файл конфігурації для Podʼа, який запускає контейнер на основі образу registry.k8s.io/e2e-test-images/agnhost.

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    args:
    - liveness
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

У файлі конфігурації можна побачити, що у Podʼа є один контейнер. Поле periodSeconds вказує, що kubelet повинен виконувати пробу життєздатності кожні 3 секунди. Поле initialDelaySeconds повідомляє kubelet, що він повинен зачекати 3 секунди перед виконанням першої проби. Для виконання проби kubelet надсилає HTTP GET-запит на сервер, який працює в контейнері та слухає порт 8080. Якщо обробник для шляху /healthz сервера повертає код успіху, kubelet вважає контейнер живим і справним. Якщо обробник повертає код невдачі, ubelet примусово зупиняє контейнер і перезапускає його.

Будь-який код, більший або рівний 200 і менший за 400, вказує на успіх. Будь-який інший код вказує на невдачу.

Ви можете переглянути вихідний код сервера в server.go.

Протягом перших 10 секунд, коли контейнер живий, обробник /healthz повертає статус 200. Після цього обробник повертає статус 500.

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

Kubelet починає виконувати перевірку стану справності через 3 секунди після запуску контейнера. Таким чином, перші кілька перевірок стану справності будуть успішними. Але після 10 секунд перевірки стану справності будуть невдалими, і kubelet зупинить та перезапустить контейнер.

Щоб спробувати перевірку стану справності через HTTP, створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/probe/http-liveness.yaml

Через 10 секунд перегляньте події Podʼа, щоб перевірити, що проби життєздатності зазнали невдачі, і контейнер був перезапущений:

kubectl describe pod liveness-http

У випусках після v1.13 налаштування локального HTTP-проксі не впливають на пробу життєздатності через HTTP.

Визначення проби життєздатності через TCP-сокет

Третій тип проби життєздатності використовує TCP сокет. З цією конфігурацією kubelet спробує відкрити зʼєднання з вашим контейнером на вказаному порту. Якщо він може встановити зʼєднання, контейнер вважається справним, якщо ні — це вважається невдачею.

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: registry.k8s.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 10

Як можна побачити, конфігурація для перевірки TCP досить схожа на перевірку через HTTP. У цьому прикладі використовуються як проби готовності, так і життєздатності. Kubelet надішле першу пробу життєздатності через 15 секунд після запуску контейнера. Ця проба спробує підʼєднатися до контейнера goproxy на порту 8080. Якщо проба на життєздатність не спрацює, контейнер буде перезапущено. Kubelet продовжить виконувати цю перевірку кожні 10 секунд.

Крім проби життєздатності, ця конфігурація включає пробу готовності. Kubelet запустить першу пробу готовності через 15 секунд після запуску контейнера. Аналогічно проби життєздатності, це спроба підʼєднатися до контейнера goproxy на порту 8080. Якщо проба пройде успішно, Pod буде позначений як готовий і отримає трафік від сервісів. Якщо перевірка готовності не вдасться, то Pod буде позначений як не готовий і не отримає трафік від жодного з сервісів.

Щоб спробувати перевірку життєздатності через TCP, створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/probe/tcp-liveness-readiness.yaml

Через 15 секунд перегляньте події Podʼа, щоб перевірити, що проби життєздатності:

kubectl describe pod goproxy

Визначення проби життєздатності через gRPC

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.27 [stable]

Якщо ваш застосунок реалізує Протокол gRPC перевірки стану справності, цей приклад показує, як налаштувати Kubernetes для його використання для перевірок життєздатності застосунку. Так само ви можете налаштувати проби готовності та запуску.

Ось приклад маніфесту:

apiVersion: v1
kind: Pod
metadata:
  name: etcd-with-grpc
spec:
  containers:
  - name: etcd
    image: registry.k8s.io/etcd:3.5.1-0
    command: [ "/usr/local/bin/etcd", "--data-dir",  "/var/lib/etcd", "--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://127.0.0.1:2379", "--log-level", "debug"]
    ports:
    - containerPort: 2379
    livenessProbe:
      grpc:
        port: 2379
      initialDelaySeconds: 10

Для використання проби gRPC має бути налаштований port. Якщо ви хочете розрізняти проби різних типів та проби для різних функцій, ви можете використовувати поле service. Ви можете встановити service у значення liveness та вказати вашій точці доступу gRPC перевірки стану справності відповідати на цей запит інакше, ніж коли ви встановлюєте service у значення readiness. Це дозволяє використовувати ту саму точку доступу для різних видів перевірки стану справності контейнера замість прослуховування двох різних портів. Якщо ви хочете вказати свою власну назву сервісу та також вказати тип проби, Kubernetes рекомендує використовувати імʼя, яке складається з цих двох частин. Наприклад: myservice-liveness (використовуючи - як роздільник).

Проблеми конфігурації (наприклад: неправильний порт чи Service, нереалізований протокол перевірки стану справності) вважаються невдачею проби, подібно до проб через HTTP та TCP.

Щоб спробувати перевірку життєздатності через gRPC, створіть Pod за допомогою наступної команди. У наведеному нижче прикладі, Pod etcd налаштований для використання проби життєздатності через gRPC.

kubectl apply -f https://k8s.io/examples/pods/probe/grpc-liveness.yaml

Через 15 секунд перегляньте події Podʼа, щоб перевірити, що перевірка життєздатності не зазнала невдачі:

kubectl describe pod etcd-with-grpc

При використанні проби через gRPC, є кілька технічних деталей, на які варто звернути увагу:

  • Проби запускаються для IP-адреси Podʼа або його імені хосту. Обовʼязково налаштуйте вашу кінецеву точку gRPC для прослуховування IP-адреси Podʼа.
  • Проби не підтримують жодних параметрів автентифікації (наприклад, -tls).
  • Немає кодів помилок для вбудованих проб. Усі помилки вважаються невдачами проби.
  • Якщо ExecProbeTimeout feature gate встановлено у false, grpc-health-probe не дотримується налаштування timeoutSeconds (яке стандартно становить 1 с), тоді як вбудована проба зазнає невдачі через тайм-аут.

Використання іменованого порту

Ви можете використовувати іменований port для проб HTTP та TCP. Проби gRPC не підтримують іменовані порти.

Наприклад:

ports:
- name: liveness-port
  containerPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port

Захист контейнерів, що повільно запускаються за допомогою проб запуску

Іноді вам доводиться мати справу з застосунками, які вимагають додаткового часу запуску при їх першій ініціалізації. У таких випадках може бути складно налаштувати параметри проби життєздатності без компромісів щодо швидкої відповіді на затримки, які мотивували використання такої проби. Рішення полягає в тому, щоб налаштувати пробу запуску з тою самою командою, перевіркою через HTTP або TCP, з failureThreshold * periodSeconds, достатньо довгим, щоб покрити найгірший випадок щодо часу запуску.

Отже, попередній приклад стане:

ports:
- name: liveness-port
  containerPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

Завдяки пробі запуску застосунок матиме максимум 5 хвилин (30 * 10 = 300 с), щоб завершити свій запуск. Як тільки проба запуску вдалася один раз, проба життєздатності бере роль на себе, щоб забезпечити швидку відповідь на затримки роботи контейнера. Якщо проба запуску ніколи не вдається, контейнер буде зупинений після 300 с і підпадатиме під restartPolicy Podʼа.

Визначення проб готовності

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

Проби готовності налаштовуються аналогічно пробам життєздатності. Єдина відмінність полягає в тому, що ви використовуєте поле readinessProbe замість поля livenessProbe.

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

Конфігурація для проб готовності через HTTP та TCP також залишається ідентичною конфігурації пробам життєздатності.

Проби готовності та життєздатності можуть використовуватися паралельно для того самого контейнера. Використання обох може забезпечити, що трафік не досягне контейнера, який не готовий до нього, та що контейнери будуть перезапускатися у разі виникнення несправностей.

Налаштування проб

Проби мають кілька полів, які можна використовувати для більш точного керування поведінкою перевірок запуску, життєздатності та готовності:

  • initialDelaySeconds: Кількість секунд після запуску контейнера, перед тим як будуть запущені перевірки запуску, життєздатності або готовності. Якщо визначено пробу запуску, проби життєздатності та готовності не починаються, поки проба запуску не вдалася. Якщо значення periodSeconds більше, ніж initialDelaySeconds, то значення initialDelaySeconds буде проігнороване. Стандартно — 0 секунд. Мінімальне значення — 0.
  • periodSeconds: Як часто (у секундах) виконувати пробу. Стандартно — 10 секунд. Мінімальне значення — 1. Поки контейнер не має статусу Ready, ReadinessProbe може бути виконаний у час, відмінний від налаштованого інтервалу periodSeconds. Це робиться для того, щоб пришвидшити готовність Podʼа.
  • timeoutSeconds: Кількість секунд, після яких проба завершиться по тайм-ауту. Стандартно — 1 секунда. Мінімальне значення — 1.
  • successThreshold: Мінімальна послідовна кількість успіхів для того, щоб проба вважалася успішною після невдачі. Стандартно — 1. Має бути 1 для проб життєздатності та запуску. Мінімальне значення — 1.
  • failureThreshold: Після того, як проба не вдається failureThreshold разів поспіль, Kubernetes вважає, що загальна перевірка невдала: контейнер не готовий/справний. У випадку проби запуску або життєздатності, якщо принаймні failureThreshold проб зазнали невдачі, Kubernetes розглядає контейнер як несправний та виконує перезапуск для цього конкретного контейнера. Kubelet дотримується налаштування terminationGracePeriodSeconds для цього контейнера. Для невдалих проб готовності kubelet продовжує запускати контейнер, який не пройшов перевірку, і також продовжує запускати додаткові проби; через те, що перевірка не пройшла, kubelet встановлює умову Ready для Podʼа у значення false.
  • terminationGracePeriodSeconds: налаштуйте період-допуск для kubelet, щоб чекати між тригером вимкнення невдалих контейнерів та примусовим зупиненням контейнера середовищем виконання. Стандартно — значення успадковується від рівня Podʼа для terminationGracePeriodSeconds (якщо не вказано, то 30 секунд), а мінімальне значення — 1. Див. terminationGracePeriodSeconds на рівні проби для більш детальної інформації.

HTTP проби

HTTP проби мають додаткові поля, які можна встановити в httpGet:

  • host: Імʼя хосту для підключення, стандартно — IP-адреса Podʼа. Ймовірно, ви захочете встановити "Host" в httpHeaders замість цього.
  • scheme: Схема для підключення до хосту (HTTP або HTTPS). Стандартно — "HTTP".
  • path: Шлях до доступу на HTTP сервері. Стандартно — "/".
  • httpHeaders: Власні заголовки, що встановлюються у запиті. HTTP дозволяє повторювані заголовки.
  • port: Імʼя або номер порту для доступу до контейнера. Номер повинен бути в діапазоні від 1 до 65535.

Для HTTP проби kubelet надсилає HTTP-запит на вказаний порт та шлях, щоб виконати перевірку. Kubelet надсилає пробу до IP-адреси Podʼа, якщо адреса не перевизначена необовʼязковим полем host у httpGet. Якщо поле scheme встановлено ​​на HTTPS, kubelet надсилає запит HTTPS, пропускаючи перевірку сертифіката. У більшості сценаріїв ви не хочете встановлювати поле host. Ось один сценарій, коли ви його встановлюєте. Припустимо, що контейнер слухає на 127.0.0.1, а поле hostNetwork Podʼа встановлене ​​на true. Тоді host у httpGet повинен бути встановлений ​​на 127.0.0.1. Якщо ваш Pod спирається на віртуальні хости, що, ймовірно, є більш поширеним випадком, ви не повинні використовувати host, але краще встановити заголовок Host в httpHeaders.

Для HTTP проби kubelet надсилає два заголовки запиту, крім обовʼязкового заголовка Host:

  • User-Agent: Стандартне значення — kube-probe/1.31, де 1.31 — версія kubelet.
  • Accept: Стандартне значення — */*.

Ви можете перевизначити стандартне значення цих двох заголовків, визначивши httpHeaders для проби. Наприклад:

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: application/json

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: MyUserAgent

Ви також можете видалити ці два заголовки, визначивши їх з порожнім значенням.

livenessProbe:
  httpGet:
    httpHeaders:
      - name: Accept
        value: ""

startupProbe:
  httpGet:
    httpHeaders:
      - name: User-Agent
        value: ""

TCP проби

Для TCP-проби kubelet встановлює зʼєднання проби на вузлі, а не в Podʼі, що означає, що ви не можете використовувати імʼя сервісу в параметрі host, оскільки kubelet не може його розпізнати.

terminationGracePeriodSeconds на рівні проб

СТАН ФУНКЦІОНАЛУ: Kubernetes 1.25 [stable]

У версіях 1.25 і вище користувачі можуть вказувати terminationGracePeriodSeconds на рівні проби в рамках специфікації проби. Коли одночасно встановлені terminationGracePeriodSeconds на рівні Podʼа і на рівні проби, kubelet використовуватиме значення на рівні проби.

При встановленні terminationGracePeriodSeconds слід звернути увагу на наступне:

  • Kubelet завжди враховує поле terminationGracePeriodSeconds на рівні проби, якщо воно присутнє в Podʼі.

  • Якщо у вас є поточні Podʼи, де поле terminationGracePeriodSeconds встановлене і ви більше не бажаєте використовувати індивідуальні терміни відповідного завершення роботи для пробт, вам слід видалити ці Podʼи.

Наприклад:

spec:
  terminationGracePeriodSeconds: 3600  # на рівні Podʼа
  containers:
  - name: test
    image: ...

    ports:
    - name: liveness-port
      containerPort: 8080

    livenessProbe:
      httpGet:
        path: /healthz
        port: liveness-port
      failureThreshold: 1
      periodSeconds: 60
      # Перевизначити `terminationGracePeriodSeconds` на рівні Podʼа #
      terminationGracePeriodSeconds: 60

terminationGracePeriodSeconds на рівні проби не може бути встановлене для проб готовності. Воно буде відхилене API-сервером.

Що далі

Також ви можете прочитати API-посилання на:

3.16 - Призначення Podʼів на вузли

Ця сторінка показує, як призначити Pod Kubernetes на певний вузол в кластері Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Додайте мітку до вузла

  1. Виведіть список вузлів у вашому кластері разом з їхніми мітками:

    kubectl get nodes --show-labels
    

    Вивід буде схожий на такий:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  2. Виберіть один з ваших вузлів і додайте до нього мітку:

    kubectl label nodes <your-node-name> disktype=ssd
    

    де <your-node-name> — це імʼя вашого обраного вузла.

  3. Перевірте, що ваш обраний вузол має мітку disktype=ssd:

    kubectl get nodes --show-labels
    

    Вивід буде схожий на такий:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    У попередньому виводі можна побачити, що вузол worker0 має мітку disktype=ssd.

Створіть Pod, який буде призначений на ваш обраний вузол

Цей файл конфігурації Podʼа описує Pod, який має селектор вузла disktype: ssd. Це означає, що Pod буде призначений на вузол, який має мітку disktype=ssd.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd
  1. Використайте файл конфігурації, щоб створити Pod, який буде призначений на ваш обраний вузол:

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx.yaml
    
  2. Перевірте, що Pod працює на вашому обраному вузлі:

    kubectl get pods --output=wide
    

    Вивід буде схожий на такий:

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

Створіть Pod, який буде призначений на конкретний вузол

Ви також можете призначити Pod на один конкретний вузол, встановивши nodeName.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeName: foo-node # призначення Podʼу конкретному вузлу
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

Використовуйте файл конфігурації, щоб створити Pod, який буде призначений тільки на foo-node.

Що далі

3.17 - Призначення Podʼів на вузли за допомогою спорідненості вузла

На цій сторінці показано, як призначити Pod Kubernetes на певний вузол за допомогою спорідненості вузла в кластері Kubernetes.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.10. Для перевірки версії введіть kubectl version.

Додайте мітку до вузла

  1. Виведіть список вузлів у вашому кластері разом з їхніми мітками:

    kubectl get nodes --show-labels
    

    Вивід буде схожий на такий:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    
  2. Виберіть один з ваших вузлів і додайте до нього мітку:

    kubectl label nodes <your-node-name> disktype=ssd
    

    де <your-node-name> — це імʼя вашого обраного вузла.

  3. Перевірте, що ваш обраний вузол має мітку disktype=ssd:

    kubectl get nodes --show-labels
    

    Вивід буде схожий на такий:

    NAME      STATUS    ROLES    AGE     VERSION        LABELS
    worker0   Ready     <none>   1d      v1.13.0        ...,disktype=ssd,kubernetes.io/hostname=worker0
    worker1   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker1
    worker2   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker2
    

    У попередньому виводі можна побачити, що вузол worker0 має мітку disktype=ssd.

Розмістіть Pod, використовуючи потрібну спорідненість вузла

Цей маніфест описує Pod, який має спорідненість вузла requiredDuringSchedulingIgnoredDuringExecution, disktype: ssd. Це означає, що Pod буде розміщений лише на вузлі, який має мітку disktype=ssd.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd            
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. Застосуйте маніфест, щоб створити Pod, який буде розміщений на вашому обраному вузлі:

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-required-affinity.yaml
    
  2. Перевірте, що Pod працює на вашому обраному вузлі:

    kubectl get pods --output=wide
    

    Вивід буде схожий на такий:

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

Розмістіть Pod, використовуючи бажану спорідненість вузла

Цей маніфест описує Pod, який має бажану спорідненість вузла preferredDuringSchedulingIgnoredDuringExecution, disktype: ssd. Це означає, що Pod надасть перевагу вузлу, який має мітку disktype=ssd.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd          
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  1. Застосуйте маніфест, щоб створити Pod, який буде розміщений на вашому обраному вузлі:

    kubectl apply -f https://k8s.io/examples/pods/pod-nginx-preferred-affinity.yaml
    
  2. Перевірте, що Pod працює на вашому обраному вузлі:

    kubectl get pods --output=wide
    

    Вивід буде схожий на такий:

    NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
    nginx    1/1       Running   0          13s    10.200.0.4   worker0
    

Що далі

Дізнайтеся більше про Спорідненість вузла.

3.18 - Налаштування ініціалізації Podʼа

На цій сторінці показано, як використовувати Init Container для ініціалізації Podʼа перед запуском контейнера застосунку.

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

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

Для перевірки версії введіть kubectl version.

Створіть Pod, який має Init Container

У цьому завданні ви створите Pod, який має один контейнер застосунку та один Init Container. Контейнер ініціалізації виконується до завершення перед тим, як розпочне виконання контейнер застосунку.

Ось файл конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  # Цей контейр виконуєть під час ініціалізації podʼу
  initContainers:
  - name: install
    image: busybox:1.28
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://info.cern.ch
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  dnsPolicy: Default
  volumes:
  - name: workdir
    emptyDir: {}

У файлі конфігурації ви бачите, що в Podʼі є Том, який обидва контейнери (ініціалізації та застосунку) спільно використовують.

Контейнер ініціалізації монтує спільний Том у /work-dir, а контейнер застосунку монтує спільний Том у /usr/share/nginx/html. Контейнер ініціалізації виконує наступну команду, а потім завершується:

wget -O /work-dir/index.html http://info.cern.ch

Зверніть увагу, що контейнер ініціалізації записує файл index.html в кореневу теку сервера nginx.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/init-containers.yaml

Перевірте, що контейнер nginx працює:

kubectl get pod init-demo

Вивід показує, що контейнер nginx працює:

NAME        READY     STATUS    RESTARTS   AGE
init-demo   1/1       Running   0          1m

Отримайте доступ до оболонки в контейнері nginx, що працює в Podʼі init-demo:

kubectl exec -it init-demo -- /bin/bash

У своїй оболонці надішліть запит GET на сервер nginx:

root@nginx:~# apt-get update
root@nginx:~# apt-get install curl
root@nginx:~# curl localhost

Вивід показує, що nginx обслуговує вебсторінку, яку записав контейнер ініціалізації:

<html><head></head><body><header>
<title>http://info.cern.ch</title>
</header>

<h1>http://info.cern.ch - home of the first website</h1>
  ...
  <li><a href="http://info.cern.ch/hypertext/WWW/TheProject.html">Browse the first website</a></li>
  ...

Що далі

3.19 - Обробники подій життєвого циклу контейнера

Ця сторінка показує, як прикріплювати обробники до подій життєвого циклу контейнера. Kubernetes підтримує події postStart та preStop. Kubernetes надсилає подію postStart безпосередньо після того, як контейнер стартує, і він надсилає подію preStop безпосередньо перед завершенням роботи контейнера. Контейнер може вказати один обробник для кожної події.

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

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

Для перевірки версії введіть kubectl version.

Визначте обробники postStart та preStop

У цьому завдані ви створите Pod, який має один контейнер. У контейнері встановлені обробники для подій postStart та preStop.

Ось файл конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]

У файлі конфігурації ви бачите, що команда postStart записує файл message в теку /usr/share контейнера. Команда preStop відповідним чином вимикає nginx. Це корисно, якщо контейнер перериває роботу через помилку.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/lifecycle-events.yaml

Перевірте, що контейнер у Podʼі працює:

kubectl get pod lifecycle-demo

Отримайте доступ до оболонки контейнера, який працює в Podʼі:

kubectl exec -it lifecycle-demo -- /bin/bash

У своїй оболонці перевірте, що обробник postStart створив файл message:

root@lifecycle-demo:/# cat /usr/share/message

Вивід показує текст, записаний обробником postStart:

Hello from the postStart handler

Обговорення

Kubernetes надсилає подію postStart безпосередньо після створення контейнера. Проте, немає гарантії, що обробник postStart буде викликаний перед тим, як буде викликано точку входу контейнера. Обробник postStart працює асинхронно відносно коду контейнера, але керування Kubernetes блокується до завершення обробника postStart. Статус контейнера не встановлюється як RUNNING до завершення обробника postStart.

Kubernetes надсилає подію preStop безпосередньо перед завершенням роботи контейнера. Керування Kubernetes контейнером блокується до завершення обробника preStop, якщо тайм-аут оновлення Podʼа не закінчився. Докладніше див. Життєвий цикл Podʼа.

Що далі

Довідка

3.20 - Налаштування Podʼів для використання ConfigMap

Велика кількість застосунків покладаються на налаштування, які використовуються під час їх ініціалізації та виконання. В більшості випадків ці налаштування можна визначити за допомогою конфігураційних параметрів. Kubernetes надає можливість додавати конфігураційні параметри до Podʼів за допомогою обʼєктів ConfigMap.

Концепція ConfigMap дозволяє виокремити конфігураційні параметри з образу контейнера, що робить застосунок більш переносним. Наприклад, ви можете завантажити та використовувати один й той самий образ контейнера для використання контейнера для потреб локальної розробки, тестування системи, або в операційному середовищі.

Ця сторінка надає ряд прикладів, які демонструють як створювати ConfigMap та налаштувати Podʼи для використання даних, що містяться в ConfigMap.

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

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

У вас має бути встановлено wget. Якщо ви використовуєте інший інструмент, такий як curl, замініть команди wget на відповідні команди для вашого інструменту.

Створення ConfigMap

Ви можете скористатись або kubectl create configmap або генератором ConfigMap в kustomization.yaml для створення ConfigMap.

Створення ConfigMap за допомогою kubectl create configmap

Скористайтесь командою kubectl create configmap, щоб створити ConfigMap з тек, файлів, або літералів:

kubectl create configmap <map-name> <data-source>

де, <map-name> — це імʼя ConfigMap, а <data-source> — це тека, файл чи літерал з даними, які ви хочете включити в ConfigMap. Імʼя обʼєкта ConfigMap повинно бути вірним імʼям субдомену DNS.

Коли ви створюєте ConfigMap на основі файлу, ключ в <data-source> визначається імʼям файлу, а значення — вмістом файлу.

Ви можете використовувати kubectl describe або kubectl get для отримання інформації про ConfigMap.

Створення ConfigMap з тек

Ви можете використовувати kubectl create configmap, щоб створити ConfigMap з кількох файлів у тій самій теці. Коли ви створюєте ConfigMap на основі теки, kubectl ідентифікує файли, імʼя яких є допустимим ключем у теці, та пакує кожен з цих файлів у новий ConfigMap. Всі записи теки, окрім звичайних файлів, ігноруються (наприклад: підтеки, символьні посилання, пристрої, канали тощо).

Створіть локальну теку:

mkdir -p configure-pod-container/configmap/

Тепер завантажте приклад конфігурації та створіть ConfigMap:

# Завантажте файли у теку `configure-pod-container/configmap/`
wget https://kubernetes.io/examples/configmap/game.properties -O configure-pod-container/configmap/game.properties
wget https://kubernetes.io/examples/configmap/ui.properties -O configure-pod-container/configmap/ui.properties

# Створіть ConfigMap
kubectl create configmap game-config --from-file=configure-pod-container/configmap/

Вказана вище команда упаковує кожен файл, у цьому випадку game.properties та ui.properties у теці configure-pod-container/configmap/ у ConfigMap game-config. Ви можете показати деталі ConfigMap за допомогою наступної команди:

kubectl describe configmaps game-config

Вивід буде приблизно таким:

Name:         game-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

Файли game.properties та ui.properties у теці configure-pod-container/configmap/ представлені у секції data ConfigMap.

kubectl get configmaps game-config -o yaml

Вивід буде приблизно таким:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2022-02-18T18:52:05Z
  name: game-config
  namespace: default
  resourceVersion: "516"
  uid: b4952dc3-d670-11e5-8cd0-68f728db1985
data:
  game.properties: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30    
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice    

Створення ConfigMaps з файлів

Ви можете використовувати kubectl create configmap для створення ConfigMap з окремого файлу або з декількох файлів.

Наприклад,

kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties

створить наступний ConfigMap:

kubectl describe configmaps game-config-2

де вивід буде схожий на це:

Name:         game-config-2
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

Ви можете вказати аргумент --from-file кілька разів, щоб створити ConfigMap із кількох джерел даних.

kubectl create configmap game-config-2 --from-file=configure-pod-container/configmap/game.properties --from-file=configure-pod-container/configmap/ui.properties

Можете переглянути деталі ConfigMap game-config-2 за допомогою наступної команди:

kubectl describe configmaps game-config-2

Вивід буде схожий на це:

Name:         game-config-2
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
ui.properties:
----
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

Використовуйте опцію --from-env-file для створення ConfigMap з env-файлу, наприклад:

# Env-файли містять список змінних оточення.
# На ці синтаксичні правила слід звертати увагу:
#   Кожен рядок у env-файлі повинен бути у форматі VAR=VAL.
#   Рядки, які починаються з # (тобто коментарі), ігноруються.
#   Порожні рядки ігноруються.
#   Особливого врахування лапок немає (тобто вони будуть частиною значення у ConfigMap).

# Завантажте приклад файлів у теку `configure-pod-container/configmap/`
wget https://kubernetes.io/examples/configmap/game-env-file.properties -O configure-pod-container/configmap/game-env-file.properties
wget https://kubernetes.io/examples/configmap/ui-env-file.properties -O configure-pod-container/configmap/ui-env-file.properties

# Env-файл `game-env-file.properties` виглядає так
cat configure-pod-container/configmap/game-env-file.properties
enemies=aliens
lives=3
allowed="true"

# Цей коментар та порожній рядок вище ігноруються
kubectl create configmap game-config-env-file \
       --from-env-file=configure-pod-container/configmap/game-env-file.properties

створить ConfigMap. Перегляньте ConfigMap:

kubectl get configmap game-config-env-file -o yaml

вивід буде схожий на:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2019-12-27T18:36:28Z
  name: game-config-env-file
  namespace: default
  resourceVersion: "809965"
  uid: d9d1ca5b-eb34-11e7-887b-42010a8002b8
data:
  allowed: '"true"'
  enemies: aliens
  lives: "3"

Починаючи з Kubernetes v1.23, kubectl підтримує аргумент --from-env-file, який може бути вказаний кілька разів для створення ConfigMap із кількох джерел даних.

kubectl create configmap config-multi-env-files \
        --from-env-file=configure-pod-container/configmap/game-env-file.properties \
        --from-env-file=configure-pod-container/configmap/ui-env-file.properties

створить наступний ConfigMap:

kubectl get configmap config-multi-env-files -o yaml

де вивід буде схожий на це:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2019-12-27T18:38:34Z
  name: config-multi-env-files
  namespace: default
  resourceVersion: "810136"
  uid: 252c4572-eb35-11e7-887b-42010a8002b8
data:
  allowed: '"true"'
  color: purple
  enemies: aliens
  how: fairlyNice
  lives: "3"
  textmode: "true"

Визначення ключа для створення ConfigMap з файлу

Ви можете визначити ключ, відмінний від імені файлу, який буде використаний у розділі data вашого ConfigMap під час використання аргументу --from-file:

kubectl create configmap game-config-3 --from-file=<my-key-name>=<path-to-file>

де <my-key-name> — це ключ, який ви хочете використовувати в ConfigMap, а <path-to-file> — це місцезнаходження файлу джерела даних, яке ключ має представляти.

Наприклад:

kubectl create configmap game-config-3 --from-file=game-special-key=configure-pod-container/configmap/game.properties

створить наступний ConfigMap:

kubectl get configmaps game-config-3 -o yaml

де вивід буде схожий на це:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2022-02-18T18:54:22Z
  name: game-config-3
  namespace: default
  resourceVersion: "530"
  uid: 05f8da22-d671-11e5-8cd0-68f728db1985
data:
  game-special-key: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30    

Створення ConfigMaps з літеральних значень

Ви можете використовувати kubectl create configmap з аргументом --from-literal, щоб визначити літеральне значення з командного рядка:

kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm

Ви можете передати декілька пар ключ-значення. Кожна пара, надана у командному рядку, представлена як окремий запис у розділі data ConfigMap.

kubectl get configmaps special-config -o yaml

Вивід буде схожий на це:

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2022-02-18T19:14:38Z
  name: special-config
  namespace: default
  resourceVersion: "651"
  uid: dadce046-d673-11e5-8cd0-68f728db1985
data:
  special.how: very
  special.type: charm

Створення ConfigMap за допомогою генератора

Ви також можете створити ConfigMap за допомогою генераторів, а потім застосувати його для створення обʼєкта на API сервері кластера. Ви повинні вказати генератори у файлі kustomization.yaml в межах теки.

Генерація ConfigMaps з файлів

Наприклад, для створення ConfigMap з файлів configure-pod-container/configmap/game.properties:

# Створіть файл kustomization.yaml з ConfigMapGenerator
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: game-config-4
  options:
    labels:
      game-config: config-4
  files:
  - configure-pod-container/configmap/game.properties
EOF

Застосуйте теку kustomization для створення обʼєкта ConfigMap:

kubectl apply -k .
configmap/game-config-4-m9dm2f92bt created

Ви можете перевірити, що ConfigMap був створений так:

kubectl get configmap
NAME                       DATA   AGE
game-config-4-m9dm2f92bt   1      37s

а також:

kubectl describe configmaps game-config-4-m9dm2f92bt
Name:         game-config-4-m9dm2f92bt
Namespace:    default
Labels:       game-config=config-4
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","data":{"game.properties":"enemies=aliens\nlives=3\nenemies.cheat=true\nenemies.cheat.level=noGoodRotten\nsecret.code.p...

Data
====
game.properties:
----
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30
Events:  <none>

Зверніть увагу, що до згенерованої назви ConfigMap додано суфікс хешування вмісту. Це забезпечує генерацію нового ConfigMap кожного разу, коли вміст змінюється.

Визначення ключа для використання при генерації ConfigMap з файлу

Ви можете визначити ключ, відмінний від імені файлу, для використання у генераторі ConfigMap. Наприклад, для генерації ConfigMap з файлів configure-pod-container/configmap/game.properties з ключем game-special-key:

# Створіть файл kustomization.yaml з ConfigMapGenerator
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: game-config-5
  options:
    labels:
      game-config: config-5
  files:
  - game-special-key=configure-pod-container/configmap/game.properties
EOF

Застосуйте теки kustomization для створення обʼєкта ConfigMap.

kubectl apply -k .
configmap/game-config-5-m67dt67794 created

Генерація ConfigMap з літералів

У цьому прикладі показано, як створити ConfigMap з двох пар ключ/значення: special.type=charm та special.how=very, використовуючи Kustomize та kubectl. Для досягнення цього, ви можете вказати генератор ConfigMap. Створіть (або замініть) kustomization.yaml, щоб мати наступний вміст:

---
# Вміст kustomization.yaml для створення ConfigMap з літералів
configMapGenerator:
- name: special-config-2
  literals:
  - special.how=very
  - special.type=charm

Застосуйте теку kustomization для створення обʼєкта ConfigMap:

kubectl apply -k .
configmap/special-config-2-c92b5mmcf2 created

Проміжна очистка

Перед продовженням, очистіть деякі з ConfigMaps, які ви створили:

kubectl delete configmap special-config
kubectl delete configmap env-config
kubectl delete configmap -l 'game-config in (config-4,config-5)'

Тепер, коли ви вивчили, як визначати ConfigMaps, ви можете перейти до наступного розділу і дізнатися, як використовувати ці обʼєкти з Pod.


Визначення змінних середовища контейнера за допомогою даних з ConfigMap

Визначення змінної середовища контейнера за допомогою даних з одного ConfigMap

  1. Визначте змінну середовища як пару ключ-значення в ConfigMap:

    kubectl create configmap special-config --from-literal=special.how=very
    
  2. Присвойте значення special.how, визначене в ConfigMap, змінній середовища SPECIAL_LEVEL_KEY у специфікації Pod.

    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: registry.k8s.io/busybox
          command: [ "/bin/sh", "-c", "env" ]
          env:
            # Визначення змінної середовища
            - name: SPECIAL_LEVEL_KEY
              valueFrom:
                configMapKeyRef:
                  # ConfigMap, що містить значення, яке потрібно присвоїти SPECIAL_LEVEL_KEY
                  name: special-config
                  # Вказує ключ, повʼязаний зі значенням
                  key: special.how
      restartPolicy: Never
    

    Створіть Pod:

    kubectl create -f https://kubernetes.io/examples/pods/pod-single-configmap-env-variable.yaml
    

    Тепер вивід Podʼа містить змінну середовища SPECIAL_LEVEL_KEY=very.

Визначення змінних середовища контейнера з даних з кількох ConfigMaps

Так само як у попередньому прикладі, спочатку створіть ConfigMaps. Ось маніфест, який ви будете використовувати:

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
  • Створіть ConfigMap:

    kubectl create -f https://kubernetes.io/examples/configmap/configmaps.yaml
    
  • Визначте змінні середовища у специфікації Pod.

    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: registry.k8s.io/busybox
          command: [ "/bin/sh", "-c", "env" ]
          env:
            - name: SPECIAL_LEVEL_KEY
              valueFrom:
                configMapKeyRef:
                  name: special-config
                  key: special.how
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: env-config
                  key: log_level
      restartPolicy: Never
    

    Створіть Pod:

    kubectl create -f https://kubernetes.io/examples/pods/pod-multiple-configmap-env-variable.yaml
    

    Тепер виведення Pod містить змінні середовища SPECIAL_LEVEL_KEY=very та LOG_LEVEL=INFO.

    Як тільки ви готові перейти далі, видаліть цей Pod:

    kubectl delete pod dapi-test-pod --now
    

Налаштування всіх пар ключ-значення в ConfigMap як змінних середовища контейнера

  • Створіть ConfigMap, який містить кілька пар ключ-значення.

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: special-config
      namespace: default
    data:
      SPECIAL_LEVEL: very
      SPECIAL_TYPE: charm
    

    Створіть ConfigMap:

    kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml
    
  • Використовуйте envFrom, щоб визначити всі дані ConfigMap як змінні середовища контейнера. Ключ з ConfigMap стає іменем змінної середовища в Pod.

    apiVersion: v1
    kind: Pod
    metadata:
      name: dapi-test-pod
    spec:
      containers:
        - name: test-container
          image: registry.k8s.io/busybox
          command: [ "/bin/sh", "-c", "env" ]
          envFrom:
          - configMapRef:
              name: special-config
      restartPolicy: Never
    

    Створіть Pod:

    kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-envFrom.yaml
    

    Тепер виведення Pod містить змінні середовища SPECIAL_LEVEL=very та SPECIAL_TYPE=charm.

    Як тільки ви готові перейти далі, видаліть цей Pod:

    kubectl delete pod dapi-test-pod --now
    

Використання змінних середовища, визначених у ConfigMap, у командах Pod

Ви можете використовувати змінні середовища, визначені у ConfigMap, у розділі command та args контейнера за допомогою синтаксису підстановки Kubernetes $(VAR_NAME).

Наприклад, у наступному маніфесті Pod:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/echo", "$(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_LEVEL
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_TYPE
  restartPolicy: Never

Створіть цей Pod, запустивши:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-env-var-valueFrom.yaml

Цей Pod видає наступний вивід від контейнера test-container:

kubectl logs dapi-test-pod
very charm

Як тільки ви готові перейти далі, видаліть цей Pod:

kubectl delete pod dapi-test-pod --now

Додавання даних ConfigMap до тому

Як пояснено у розділі Створення ConfigMap з файлів, коли ви створюєте ConfigMap, використовуючи --from-file, імʼя файлу стає ключем, збереженим у розділі data ConfigMap. Вміст файлу стає значенням ключа.

Приклади в цьому розділі відносяться до ConfigMap з іменем special-config:

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  SPECIAL_LEVEL: very
  SPECIAL_TYPE: charm

Створіть ConfigMap:

kubectl create -f https://kubernetes.io/examples/configmap/configmap-multikeys.yaml

Заповнення тому даними, збереженими в ConfigMap

Додайте імʼя ConfigMap у розділ volumes специфікації Pod. Це додасть дані ConfigMap до теки, вказаної як volumeMounts.mountPath (у цьому випадку, /etc/config). Розділ command перераховує файли теки з іменами, що відповідають ключам у ConfigMap.

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh", "-c", "ls /etc/config/" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # Надає назву ConfigMap, що містить файли, які ви бажаєте
        # додати до контейнера
        name: special-config
  restartPolicy: Never

Створіть Pod:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume.yaml

Коли Pod працює, команда ls /etc/config/ виводить наступне:

SPECIAL_LEVEL
SPECIAL_TYPE

Текстові дані показуються у вигляді файлів з використанням кодування символів UTF-8. Щоб використовувати інше кодування символів, скористайтеся binaryData (див. обʼєкт ConfigMap для докладніших відомостей).

Якщо ви готові перейти до наступного кроку, видаліть цей Pod:

kubectl delete pod dapi-test-pod --now

Додавання конфігурації ConfigMap до певного шляху у томі

Використовуйте поле path, щоб вказати бажаний шлях до файлів для конкретних елементів ConfigMap. У цьому випадку елемент SPECIAL_LEVEL буде змонтовано у томі config-volume за адресою /etc/config/keys.

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "/bin/sh","-c","cat /etc/config/keys" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: special-config
        items:
        - key: SPECIAL_LEVEL
          path: keys
  restartPolicy: Never

Створіть pod:

kubectl create -f https://kubernetes.io/examples/pods/pod-configmap-volume-specific-key.yaml

Коли pod запущено, команда cat /etc/config/keys видасть наведений нижче вивід:

very

Видаліть цей Pod:

kubectl delete pod dapi-test-pod --now

Спроєцюйте ключі на конкретні шляхи та встановлюйте права доступу до файлів

Ви можете спроєцювати ключі на конкретні шляхи. Зверніться до відповідного розділу в Посібнику Secret для ознайомлення з синтаксисом. Ви можете встановлювати права доступу POSIX для ключів. Зверніться до відповідного розділу в Посібнику Secret ознайомлення з синтаксисом.

Необовʼязкові посилання

Посилання на ConfigMap може бути позначене як необовʼязкове. Якщо ConfigMap не існує, змонтований том буде порожнім. Якщо ConfigMap існує, але посилання на ключ не існує, шлях буде відсутній під точкою монтування. Дивіться Опціональні ConfigMaps для отримання додаткових відомостей.

Змонтовані ConfigMap оновлюються автоматично

Коли змонтований ConfigMap оновлюється, спроєцьований вміст врешті-решт оновлюється також. Це стосується випадку, коли ConfigMap, на який посилатися необовʼязково, зʼявляється після того, як Pod вже почав працювати.

Kubelet перевіряє, чи змонтований ConfigMap є актуальним під час кожної періодичної синхронізації. Однак він використовує свій локальний кеш на основі TTL для отримання поточного значення ConfigMap. В результаті загальна затримка від моменту оновлення ConfigMap до моменту, коли нові ключі проєцюються у Pod може бути таким, як період синхронізації kubelet (стандартно — 1 хвилина) + TTL кешу ConfigMaps (стандартно — 1 хвилина) в kubelet. Ви можете викликати негайне оновлення, оновивши одну з анотацій Podʼа.

Розуміння ConfigMap та Podʼів

Ресурс ConfigMap API зберігає конфігураційні дані у вигляді пар ключ-значення. Дані можуть бути використані в Podʼах або надавати конфігураційні дані для системних компонентів, таких як контролери. ConfigMap схожий на Secret, але надає засоби для роботи з рядками, що не містять конфіденційної інформації. Користувачі та системні компоненти можуть зберігати конфігураційні дані в ConfigMap.

Поле data у ConfigMap містить дані конфігурації. Як показано у прикладі нижче, це може бути простим (наприклад, окремі властивості, визначені за допомогою --from-literal) або складним (наприклад, файли конфігурації або JSON-фрагменти, визначені за допомогою --from-file).

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T19:14:38Z
  name: example-config
  namespace: default
data:
  # приклад простої властивості, визначеної за допомогою --from-literal
  example.property.1: hello
  example.property.2: world
  # приклад складної властивості, визначеної за допомогою --from-file
  example.property.file: |-
    property.1=value-1
    property.2=value-2
    property.3=value-3    

Коли kubectl створює ConfigMap з вхідних даних, які не є ASCII або UTF-8, цей інструмент поміщає їх у поле binaryData ConfigMap, а не в data. Як текстові, так і бінарні дані можуть бути поєднані в одному ConfigMap.

Якщо ви хочете переглянути ключі binaryData (і їх значення) в ConfigMap, ви можете виконати kubectl get configmap -o jsonpath='{.binaryData}' <імʼя>.

Podʼи можуть завантажувати дані з ConfigMap, що використовують як data, так і binaryData.

Опціональні ConfigMaps

Ви можете позначити посилання на ConfigMap як опціональне в специфікації Pod. Якщо ConfigMap не існує, конфігурація, для якої вона надає дані в Pod (наприклад: змінна середовища, змонтований том), буде пустою. Якщо ConfigMap існує, але посилання на ключ не існує, дані також будуть пустими.

Наприклад, наступна специфікація Pod позначає змінну середовища з ConfigMap як опціональну:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: ["/bin/sh", "-c", "env"]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: a-config
              key: akey
              optional: true # позначає змінну як опціональну
  restartPolicy: Never

Якщо ви запустите цей Pod, і ConfigMap з імʼям a-config не існує, вивід буде пустим. Якщо ви запустите цей Pod, і ConfigMap з імʼям a-config існує, але в цьому ConfigMap немає ключа з імʼям akey, вивід також буде пустим. Якщо ж ви задасте значення для akey в ConfigMap a-config, цей Pod надрукує це значення і потім завершить роботу.

Ви також можете позначити томи та файли, надані ConfigMap, як опціональні. Kubernetes завжди створює шляхи для монтування томів, навіть якщо зазначений ConfigMap або ключ не існують. Наприклад, наступна специфікація Pod позначає том, який посилається на ConfigMap, як опціональний:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: ["/bin/sh", "-c", "ls /etc/config"]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: no-config
        optional: true # позначає ConfigMap як опціональний
  restartPolicy: Never

Обмеження

  • Ви повинні створити обʼєкт ConfigMap до того, як ви посилатиметесь на нього в специфікації Pod. Альтернативно, позначте посилання на ConfigMap як optional в специфікації Pod (див. Опціональні ConfigMaps). Якщо ви посилаєтесь на ConfigMap, який не існує, і ви не позначите посилання як optional, Podʼи не запуститься. Аналогічно, посилання на ключі, які не існують в ConfigMap, також перешкоджатимуть запуску Podʼа, якщо ви не позначите посилання на ключі як optional.

  • Якщо ви використовуєте envFrom для визначення змінних середовища з ConfigMaps, ключі, які вважаються недійсними, будуть пропущені. Podʼу буде дозволено запускатися, але недійсні імена будуть записані в лог подій (InvalidVariableNames). Повідомлення логу містить кожен пропущений ключ. Наприклад:

    kubectl get events
    

    Вивід буде схожий на цей:

    LASTSEEN FIRSTSEEN COUNT NAME          KIND  SUBOBJECT  TYPE      REASON                            SOURCE                MESSAGE
    0s       0s        1     dapi-test-pod Pod              Warning   InvalidEnvironmentVariableNames   {kubelet, 127.0.0.1}  Keys [1badkey, 2alsobad] from the EnvFrom configMap default/myconfig were skipped since they are considered invalid environment variable names.
    
  • ConfigMaps знаходяться в конкретному Namespace. Podʼи можуть посилатися лише на ConfigMaps, які знаходяться в тому ж контексті, що і сам Под.

  • Ви не можете використовувати ConfigMaps для статичних Podʼів, оскільки kubelet їх не підтримує.

Очищення

Вилучить ConfigMaps та Pod, які ви створили, використовуючи наступні команди:

kubectl delete configmaps/game-config configmaps/game-config-2 configmaps/game-config-3 \
               configmaps/game-config-env-file
kubectl delete pod dapi-test-pod --now

# Можливо, ви вже видалили наступний набір
kubectl delete configmaps/special-config configmaps/env-config
kubectl delete configmap -l 'game-config in (config-4,config-5)'

Якщо ви створили ntre configure-pod-container і вже не потребуєте її, вам слід також її видалити або перемістити в кошик або місце для видалених файлів.

Що далі

3.21 - Поділ простору імен процесів між контейнерами у Podʼі

На цій сторінці показано, як налаштувати поділ простору імен процесів для Podʼа. Коли поділ простору імен процесів увімкнено, процеси в контейнері стають видимими для всіх інших контейнерів у тому ж Podʼі.

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

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

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

Налаштування Podʼа

Поділ простору імен процесів увімкнено за допомогою поля shareProcessNamespace в розділі .spec Podʼа. Наприклад:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
  - name: shell
    image: busybox:1.28
    command: ["sleep", "3600"]
    securityContext:
      capabilities:
        add:
        - SYS_PTRACE
    stdin: true
    tty: true
  1. Створіть Pod nginx у вашому кластері:

    kubectl apply -f https://k8s.io/examples/pods/share-process-namespace.yaml
    
  2. Приєднайтеся до контейнера shell та запустіть команду ps:

    kubectl attach -it nginx -c shell
    

    Якщо ви не бачите символу командного рядка, спробуйте натиснути клавішу Enter. У оболонці контейнера:

    # виконайте це всередині контейнера "shell"
    ps ax
    

    Вивід схожий на такий:

    PID   USER     TIME  COMMAND
        1 root      0:00 /pause
        8 root      0:00 nginx: master process nginx -g daemon off;
       14 101       0:00 nginx: worker process
       15 root      0:00 sh
       21 root      0:00 ps ax
    

Ви можете відправляти сигнали процесам в інших контейнерах. Наприклад, відправте SIGHUP до nginx, щоб перезапустити робочий процес. Для цього потрібна можливість SYS_PTRACE.

# виконайте це всередині контейнера "shell"
kill -HUP 8   # змініть "8" на відповідний PID лідера процесу nginx, якщо потрібно
ps ax

Вивід схожий на такий:

PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    8 root      0:00 nginx: master process nginx -g daemon off;
   15 root      0:00 sh
   22 101       0:00 nginx: worker process
   23 root      0:00 ps ax

Навіть можливо отримати доступ до файлової системи іншого контейнера, використовуючи посилання /proc/$pid/root.

# виконайте це всередині контейнера "shell"
# змініть "8" на PID процесу Nginx, якщо потрібно
head /proc/8/root/etc/nginx/nginx.conf

Вивід схожий на такий:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;

Розуміння поділу простору імен процесів

Podʼи ділять багато ресурсів, тому логічно, що вони також будуть ділитися простором імен процесів. Деякі контейнери можуть очікувати ізоляції від інших, тому важливо розуміти відмінності:

  1. Процес контейнера вже не має PID 1. Деякі контейнери відмовляються запускатися без PID 1 (наприклад, контейнери, що використовують systemd) або виконують команди типу kill -HUP 1 для відправлення сигналу процесу контейнера. У Podʼах зі спільним простором імен процесів kill -HUP 1 відправить сигнал пісочниці Podʼа (/pause у вищезазначеному прикладі).

  2. Процеси видимі іншим контейнерам у Podʼі. Це включає всю інформацію, доступну у /proc, таку як паролі, що були передані як аргументи або змінні середовища. Ці дані захищені лише звичайними правами Unix.

  3. Файлові системи контейнерів видимі іншим контейнерам у Podʼі через посилання /proc/$pid/root. Це полегшує налагодження, але також означає, що секрети файлової системи захищені лише правами файлової системи.

3.22 - Використання простору імен користувача з Podʼом

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.30 [beta]

Ця сторінка показує, як налаштувати простір імен користувача для Podʼів. Це дозволяє ізолювати користувача, що працює всередині контейнера, від того, який працює на хості.

Процес, що працює як root у контейнері, може працювати як інший (не root) користувач на хості; іншими словами, процес має повні привілеї для операцій всередині простору імен користувача, але не має привілеїв для операцій за межами простору імен.

Ви можете використовувати цю функцію, щоб зменшити шкоду, яку скомпрометований контейнер може завдати хосту або іншим Podʼам на тому ж вузлі. Є кілька уразливостей безпеки, оцінених як ВИСОКІ або КРИТИЧНІ, які не були використовні при активному використанні просторів імен користувача. Очікується, що простори імен користувача захистять від деяких майбутніх уразливостей також.

Без використання просторів імен користувача контейнер, що працює як root, у випадку втечі з контейнера має привілеї root на вузлі. І якщо деякі можливості були надані контейнеру, то ці можливості також дійсні на хості. Цього не відбувається, коли використовуються простори імен користувача.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.25. Для перевірки версії введіть kubectl version.

  • ОС вузла повинна бути Linux
  • Ви повинні мати можливість виконувати команди на хості
  • Ви повинні мати можливість виконувати команди у Podʼах
  • Вам потрібно увімкнути функціональну можливість UserNamespacesSupport

Кластер, який ви використовуєте, обовʼязково повинен містити принаймні один вузол, який відповідає вимогам щодо використання просторів імен користувача з Podʼами.

Якщо у вас є суміш вузлів і лише деякі з них надають підтримку просторів імен користувача для Podʼів, вам також потрібно забезпечити те, що Podʼи з просторами імен користувача будуть заплановані на відповідні вузли.

Запуск Podʼа, що використовує простір імен користувача

Простір імен користувача для Podʼа вимкається, встановленням поля hostUsers в .spec на false. Наприклад:

apiVersion: v1
kind: Pod
metadata:
  name: userns
spec:
  hostUsers: false
  containers:
  - name: shell
    command: ["sleep", "infinity"]
    image: debian
  1. Створіть Pod у вашому кластері:

    kubectl apply -f https://k8s.io/examples/pods/user-namespaces-stateless.yaml
    
  2. Приєднайтеся до контейнера і виконайте readlink /proc/self/ns/user:

    kubectl attach -it userns bash
    

Виконайте цю команду:

read

link /proc/self/ns/user

Вивід схожий на:

user:[4026531837]

Також виконайте:

cat /proc/self/uid_map

Вивід схожий на:

0  833617920      65536

Потім відкрийте оболонку на хості та виконайте ті ж самі команди.

Команда readlink показує простір імен користувача, в якому працює процес. Він повинен бути різним, коли ви виконуєте його на хості і всередині контейнера.

Останнє число у файлі uid_map всередині контейнера повинно бути 65536, на хості це число повинно бути більшим.

Якщо ви запускаєте kubelet всередині простору імен користувача, вам потрібно порівняти вивід команди в Pod з виводом, отриманим на хості:

readlink /proc/$pid/ns/user

замінивши $pid на PID kubelet.

3.23 - Використання тому Image в Pod

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [alpha]

Ця сторінка демонструє, як налаштувати Pod для використання томів image. Це дозволяє монтувати вміст з OCI реєстрів всередині контейнерів.

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

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

Версія вашого Kubernetes сервера має бути v1.31. Для перевірки версії введіть kubectl version.

  • Середовище виконання контейнерів має підтримувати функцію томів image.
  • Вам потрібно мати можливість виконувати команди на хості.
  • Вам потрібно мати можливість підключатися до pod.
  • Вам потрібно увімкнути функціональну можливість ImageVolume.

Запуск Podʼа, що використовує том image

Том image для Podʼа активується шляхом налаштування поля volumes.[*].image у .spec на дійсне посилання та використання його в volumeMounts контейнера. Наприклад:

apiVersion: v1
kind: Pod
metadata:
  name: image-volume
spec:
  containers:
  - name: shell
    command: ["sleep", "infinity"]
    image: debian
    volumeMounts:
    - name: volume
      mountPath: /volume
  volumes:
  - name: volume
    image:
      reference: quay.io/crio/artifact:v1
      pullPolicy: IfNotPresent
  1. Створіть Pod у вашому кластері:

    kubectl apply -f https://k8s.io/examples/pods/image-volumes.yaml
    
  2. Приєднайтесь до контейнера:

    kubectl attach -it image-volume bash
    
  3. Перевірте вміст файлу в томі:

    cat /volume/dir/file
    

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

    1
    

    Ви також можете перевірити інший файл з іншим шляхом:

    cat /volume/file
    

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

    2
    

Додатково

3.24 - Створення статичних Podʼів

Статичні Podʼи керуються безпосередньо демоном kubelet на конкретному вузлі, без спостереження за ними з боку API сервера. На відміну від Podʼів, які керуються панеллю управління (наприклад, Deployment}), kubelet спостерігає за кожним статичним Podʼом (і перезапускає його у разі невдачі).

Статичні Podʼи завжди привʼязані до одного Kubelet на конкретному вузлі.

Kubelet автоматично намагається створити дзеркальний Pod на сервері Kubernetes API для кожного статичного Podʼа. Це означає, що Podʼи, які запущені на вузлі, є видимими на сервері API, але не можуть бути керовані звідти. Назви Podʼів будуть мати суфікс з іменем хосту вузла відділеним дефісом.

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

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

Для перевірки версії введіть kubectl version.

На цій сторінці передбачається, що ви використовуєте CRI-O для запуску Podʼів, а також що ваші вузли працюють під управлінням операційної системи Fedora. Інструкції для інших дистрибутивів або встановлень Kubernetes можуть відрізнятися.

Створення статичного Podʼа

Ви можете налаштувати статичний Pod з використанням файлу конфігурації, що зберігається в файловій системі або файлу конфігурації, що зберігається на вебсервері.

Статичний Pod з файлової системи

Маніфести — це стандартні визначення Podʼів у форматі JSON або YAML в певній теці. Використовуйте поле staticPodPath: <тека> у конфігураційному файлі kubelet, який періодично сканує теку і створює/видаляє статичні Podʼи, коли у цій теці зʼявляються/зникають файли YAML/JSON. Зверніть увагу, що kubelet ігнорує файли, що починаються з крапки при скануванні вказаної теки.

Наприклад, так можна запустити простий вебсервер як статичний Pod:

  1. Виберіть вузол, на якому ви хочете запустити статичний Pod. У цьому прикладі це my-node1.

    ssh my-node1
    
  2. Виберіть теку, наприклад /etc/kubernetes/manifests, і помістіть туди визначення Podʼа вебсервера, наприклад, /etc/kubernetes/manifests/static-web.yaml:

    # Виконайте цю команду на вузлі, де працює kubelet
    mkdir -p /etc/kubernetes/manifests/
    cat <<EOF >/etc/kubernetes/manifests/static-web.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    EOF
    
  3. Налаштуйте kubelet на тому вузлі, щоб встановити значення staticPodPath в конфігураційному файлі kubelet. Див. Встановлення параметрів kubelet через конфігураційний файл для отримання додаткової інформації.

    Альтернативний і застарілий метод полягає в налаштуванні kubelet на тому вузлі, щоб він шукав маніфести статичного Podʼа локально, використовуючи аргумент командного рядка. Щоб використовувати застарілий підхід, запустіть kubelet з аргументом --pod-manifest-path=/etc/kubernetes/manifests/.

  4. Перезапустіть kubelet. У Fedora ви виконаєте:

    # Виконайте цю команду на вузлі, де працює kubelet
    systemctl restart kubelet
    

Маніфест Podʼа, розміщений на вебсервері

Kubelet періодично завантажує файл, вказаний аргументом --manifest-url=<URL>, і розглядає його як файл JSON/YAML, який містить визначення Podʼів. Подібно до того, як працюють маніфести, розміщені в файловій системі, kubelet перевіряє маніфест за розкладом. Якщо відбулися зміни в списку статичних Podʼів, kubelet застосовує їх.

Щоб скористатися цим підходом:

  1. Створіть YAML-файл і збережіть його на веб-сервері, щоб ви могли передати URL цього файлу kubelet.

    apiVersion: v1
    kind: Pod
    metadata:
      name: static-web
      labels:
        role: myrole
    spec:
      containers:
        - name: web
          image: nginx
          ports:
            - name: web
              containerPort: 80
              protocol: TCP
    
  2. Налаштуйте kubelet на обраному вузлі для використання цього веб-маніфесту, запустивши його з аргументом --manifest-url=<URL-маніфесту>. У Fedora відредагуйте /etc/kubernetes/kubelet, щоб додати цей рядок:

    KUBELET_ARGS="--cluster-dns=10.254.0.10 --cluster-domain=kube.local --manifest-url=<URL-маніфесту>"
    
  3. Перезапустіть kubelet. У Fedora ви виконаєте:

    # Виконайте цю команду на вузлі, де працює kubelet
    systemctl restart kubelet
    

Спостереження за поведінкою статичного Podʼа

Після запуску kubelet автоматично запускає всі визначені статичні Podʼи. Оскільки ви визначили статичний Pod і перезапустили kubelet, новий статичний Pod вже має бути запущений.

Ви можете переглянути запущені контейнери (включно зі статичними Podʼами), виконавши (на вузлі):

# Виконайте цю команду на вузлі, де працює kubelet
crictl ps

Вивід може бути наступним:

CONTAINER       IMAGE                                 CREATED           STATE      NAME    ATTEMPT    POD ID
129fd7d382018   docker.io/library/nginx@sha256:...    11 minutes ago    Running    web     0          34533c6729106

Ви можете побачити дзеркальний Pod на сервері API:

kubectl get pods
NAME                  READY   STATUS    RESTARTS        AGE
static-web-my-node1   1/1     Running   0               2m

Мітки зі статичного Podʼа передаються в дзеркальний Pod. Ви можете використовувати ці мітки як зазвичай через селектори, і т.д.

Якщо ви спробуєте використовувати kubectl для видалення дзеркального Podʼа з сервера API, kubelet не видаляє статичний Pod:

kubectl delete pod static-web-my-node1
pod "static-web-my-node1" deleted

Ви побачите, що Pod все ще працює:

kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
static-web-my-node1   1/1     Running   0          4s

Поверніться на вузол, де працює kubelet, і спробуйте вручну зупинити контейнер. Ви побачите, що після певного часу kubelet помітить це та автоматично перезапустить Pod:

# Виконайте ці команди на вузлі, де працює kubelet
crictl stop 129fd7d382018 # замініть на ID вашого контейнера
sleep 20
crictl ps
CONTAINER       IMAGE                                 CREATED           STATE      NAME    ATTEMPT    POD ID
89db4553e1eeb   docker.io/library/nginx@sha256:...    19 seconds ago    Running    web     1          34533c6729106

Після того як ви визначите потрібний контейнер, ви можете отримати журнал для цього контейнера за допомогою crictl:

# Виконайте ці команди на вузлі, де працює контейнер
crictl logs <container_id>
10.240.0.48 - - [16/Nov/2022:12:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.48 - - [16/Nov/2022:12:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.48 - - [16/Nove/2022:12:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

Щоб дізнатися більше про те, як налагоджувати за допомогою crictl, відвідайте Налагодження вузлів Kubernetes за допомогою crictl.

Динамічне додавання та видалення статичних Podʼів

Запущений kubelet періодично сканує налаштовану теку (/etc/kubernetes/manifests у нашому прикладі) на предмет змін та додає/видаляє Podʼи при появі/зникненні файлів в цій теці.

# Це передбачає, що ви використовуєте файлову конфігурацію статичних Podʼів
# Виконайте ці команди на вузлі, де працює контейнер
#
mv /etc/kubernetes/manifests/static-web.yaml /tmp
sleep 20
crictl ps
# Ви бачите, що ніякий контейнер nginx не працює
mv /tmp/static-web.yaml  /etc/kubernetes/manifests/
sleep 20
crictl ps
CONTAINER       IMAGE                                 CREATED           STATE      NAME    ATTEMPT    POD ID
f427638871c35   docker.io/library/nginx@sha256:...    19 seconds ago    Running    web     1          34533c6729106

Що далі

3.25 - Конвертація файлу Docker Compose в ресурси Kubernetes

Що таке Kompose? Це інструмент конвертації для всього, що стосується композиції (зокрема Docker Compose) в ресурси систем оркестрування (Kubernetes або OpenShift).

Додаткову інформацію можна знайти на вебсайті Kompose за адресою http://kompose.io.

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

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

Для перевірки версії введіть kubectl version.

Встановлення Kompose

Є кілька способів встановлення Kompose. Наш спосіб - завантаження бінарного файлу з останнього релізу GitHub.

Kompose випускається через GitHub кожні три тижні, ви можете переглянути всі поточні релізи на сторінці релізів GitHub.

# Linux
curl -L https://github.com/kubernetes/kompose/releases/download/v1.34.0/kompose-linux-amd64 -o kompose

# macOS
curl -L https://github.com/kubernetes/kompose/releases/download/v1.34.0/kompose-darwin-amd64 -o kompose

# Windows
curl -L https://github.com/kubernetes/kompose/releases/download/v1.34.0/kompose-windows-amd64.exe -o kompose.exe

chmod +x kompose
sudo mv ./kompose /usr/local/bin/kompose

Також ви можете завантажити архів.

Встановлення за допомогою go get витягує дані з гілки master з останніми змінами розробки.

go get -u github.com/kubernetes/kompose

Kompose є в репозиторії EPEL для CentOS. Якщо у вас ще немає встановленого та увімкненого репозиторію EPEL, ви можете зробити це, виконавши sudo yum install epel-release.

Якщо у вас увімкнений репозиторій EPEL у вашій системі, ви можете встановити Kompose як будь-який інший пакунок.

sudo yum -y install kompose

Kompose є в репозиторіях Fedora 24, 25 та 26. Ви можете встановити його, як і будь-який інший пакунок.

sudo dnf -y install kompose

У macOS ви можете встановити останній реліз за допомогою Homebrew:

brew install kompose

Використання Kompose

За кілька кроків ми переведемо вас з Docker Compose до Kubernetes. Вам потрібен лише наявний файл docker-compose.yml.

  1. Перейдіть до теки, що містить ваш файл docker-compose.yml. Якщо у вас його немає, ви можете випробувати використовуючи цей.

    
    services:
    
      redis-leader:
        container_name: redis-leader
        image: redis
        ports:
          - "6379"
    
      redis-replica:
        container_name: redis-replica
        image: redis
        ports:
          - "6379"
        command: redis-server --replicaof redis-leader 6379 --dir /tmp
    
      web:
        container_name: web
        image: quay.io/kompose/web
        ports:
          - "8080:8080"
        environment:
          - GET_HOSTS_FROM=dns
        labels:
          kompose.service.type: LoadBalancer
    
  2. Щоб конвертувати файл docker-compose.yml у файли, які можна використовувати з kubectl, запустіть kompose convert, а потім kubectl apply -f <output file>.

    kompose convert
    

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

    INFO Kubernetes file "redis-leader-service.yaml" created
    INFO Kubernetes file "redis-replica-service.yaml" created
    INFO Kubernetes file "web-tcp-service.yaml" created
    INFO Kubernetes file "redis-leader-deployment.yaml" created
    INFO Kubernetes file "redis-replica-deployment.yaml" created
    INFO Kubernetes file "web-deployment.yaml" created
    
     kubectl apply -f web-tcp-service.yaml,redis-leader-service.yaml,redis-replica-service.yaml,web-deployment.yaml,redis-leader-deployment.yaml,redis-replica-deployment.yaml
    

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

    deployment.apps/redis-leader created
    deployment.apps/redis-replica created
    deployment.apps/web created
    service/redis-leader created
    service/redis-replica created
    service/web-tcp created
    

    Ваші розгортання, що працюють в Kubernetes.

  3. Доступ до вашого застосунку.

    Якщо ви вже використовуєте minikube для вашого процесу розробки:

    minikube service web-tcp
    

    В іншому випадку, подивімось, яку IP використовує ваш Service!

    kubectl describe svc web-tcp
    
    Name:                     web-tcp
    Namespace:                default
    Labels:                   io.kompose.service=web-tcp
    Annotations:              kompose.cmd: kompose convert
                            kompose.service.type: LoadBalancer
                            kompose.version: 1.33.0 (3ce457399)
    Selector:                 io.kompose.service=web
    Type:                     LoadBalancer
    IP Family Policy:         SingleStack
    IP Families:              IPv4
    IP:                       10.102.30.3
    IPs:                      10.102.30.3
    Port:                     8080  8080/TCP
    TargetPort:               8080/TCP
    NodePort:                 8080  31624/TCP
    Endpoints:                10.244.0.5:8080
    Session Affinity:         None
    External Traffic Policy:  Cluster
    Events:                   <none>
    

    Якщо ви використовуєте хмарного постачальника, ваша IP буде вказана поруч з LoadBalancer Ingress.

    curl http://192.0.2.89
    
  4. Прибирання.

    Після завершення тестування розгортання прикладного застосунку просто запустіть наступну команду в вашій оболонці, щоб видалити використані ресурси.

    kubectl delete -f web-tcp-service.yaml,redis-leader-service.yaml,redis-replica-service.yaml,web-deployment.yaml,redis-leader-deployment.yaml,redis-replica-deployment.yaml
    

Посібник користувача

Kompose підтримує двох провайдерів: OpenShift і Kubernetes. Ви можете вибрати відповідного постачальника, використовуючи глобальну опцію --provider. Якщо постачальник не вказаний, буде встановлено Kubernetes.

kompose convert

Kompose підтримує конвертацію файлів Docker Compose версій V1, V2 і V3 в обʼєкти Kubernetes та OpenShift.

Приклад kompose convert для Kubernetes

kompose --file docker-voting.yml convert
WARN Unsupported key networks - ignoring
WARN Unsupported key build - ignoring
INFO Kubernetes file "worker-svc.yaml" created
INFO Kubernetes file "db-svc.yaml" created
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "result-svc.yaml" created
INFO Kubernetes file "vote-svc.yaml" created
INFO Kubernetes file "redis-deployment.yaml" created
INFO Kubernetes file "result-deployment.yaml" created
INFO Kubernetes file "vote-deployment.yaml" created
INFO Kubernetes file "worker-deployment.yaml" created
INFO Kubernetes file "db-deployment.yaml" created
ls
db-deployment.yaml  docker-compose.yml         docker-gitlab.yml  redis-deployment.yaml  result-deployment.yaml  vote-deployment.yaml  worker-deployment.yaml
db-svc.yaml         docker-voting.yml          redis-svc.yaml     result-svc.yaml        vote-svc.yaml           worker-svc.yaml

Ви також можете надати кілька файлів docker-compose одночасно:

kompose -f docker-compose.yml -f docker-guestbook.yml convert
INFO Kubernetes file "frontend-service.yaml" created         
INFO Kubernetes file "mlbparks-service.yaml" created         
INFO Kubernetes file "mongodb-service.yaml" created          
INFO Kubernetes file "redis-master-service.yaml" created     
INFO Kubernetes file "redis-slave-service.yaml" created      
INFO Kubernetes file "frontend-deployment.yaml" created      
INFO Kubernetes file "mlbparks-deployment.yaml" created      
INFO Kubernetes file "mongodb-deployment.yaml" created       
INFO Kubernetes file "mongodb-claim0-persistentvolumeclaim.yaml" created
INFO Kubernetes file "redis-master-deployment.yaml" created  
INFO Kubernetes file "redis-slave-deployment.yaml" created   
ls
mlbparks-deployment.yaml  mongodb-service.yaml                       redis-slave-service.jsonmlbparks-service.yaml  
frontend-deployment.yaml  mongodb-claim0-persistentvolumeclaim.yaml  redis-master-service.yaml
frontend-service.yaml     mongodb-deployment.yaml                    redis-slave-deployment.yaml
redis-master-deployment.yaml

Коли надається кілька файлів docker-compose, конфігурація обʼєднується. Будь-яка конфігурація, яка є спільною, буде перевизначена наступним файлом.

Приклад kompose convert для OpenShift

kompose --provider openshift --file docker-voting.yml convert
WARN [worker] Service cannot be created because of missing port.
INFO OpenShift file "vote-service.yaml" created             
INFO OpenShift file "db-service.yaml" created               
INFO OpenShift file "redis-service.yaml" created            
INFO OpenShift file "result-service.yaml" created           
INFO OpenShift file "vote-deploymentconfig.yaml" created    
INFO OpenShift file "vote-imagestream.yaml" created         
INFO OpenShift file "worker-deploymentconfig.yaml" created  
INFO OpenShift file "worker-imagestream.yaml" created       
INFO OpenShift file "db-deploymentconfig.yaml" created      
INFO OpenShift file "db-imagestream.yaml" created           
INFO OpenShift file "redis-deploymentconfig.yaml" created   
INFO OpenShift file "redis-imagestream.yaml" created        
INFO OpenShift file "result-deploymentconfig.yaml" created  
INFO OpenShift file "result-imagestream.yaml" created  

Також підтримує створення buildconfig для директиви build в сервісі. Стандартно використовується віддалений репозиторій для поточної гілки git як джерело репозиторію, та поточну гілку як гілку джерела для збірки. Ви можете вказати інше джерело репозиторію та гілку джерела, використовуючи опції --build-repo та --build-branch відповідно.

kompose --provider openshift --file buildconfig/docker-compose.yml convert
WARN [foo] Service cannot be created because of missing port.
INFO OpenShift Buildconfig using git@github.com:rtnpro/kompose.git::master as source.
INFO OpenShift file "foo-deploymentconfig.yaml" created     
INFO OpenShift file "foo-imagestream.yaml" created          
INFO OpenShift file "foo-buildconfig.yaml" created

Альтернативні конвертації

Типово kompose перетворює файли у форматі yaml на обʼєкти Kubernetes Deployments та Services. У вас є альтернативна опція для генерації json за допомогою -j. Також, ви можете альтернативно згенерувати обʼєкти Replication Controllers, Daemon Sets, або Helm чарти.

kompose convert -j
INFO Kubernetes file "redis-svc.json" created
INFO Kubernetes file "web-svc.json" created
INFO Kubernetes file "redis-deployment.json" created
INFO Kubernetes file "web-deployment.json" created

Файли *-deployment.json містять обʼєкти Deployment.

kompose convert --replication-controller
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-replicationcontroller.yaml" created
INFO Kubernetes file "web-replicationcontroller.yaml" created

Файли *-replicationcontroller.yaml містять обʼєкти Replication Controller. Якщо ви хочете вказати кількість реплік (стандартно 1), використовуйте прапорець --replicas: kompose convert --replication-controller --replicas 3.

kompose convert --daemon-set
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-daemonset.yaml" created
INFO Kubernetes file "web-daemonset.yaml" created

Файли *-daemonset.yaml містять обʼєкти DaemonSet.

Якщо ви хочете згенерувати чарт для використання з Helm, виконайте:

kompose convert -c
INFO Kubernetes file "web-svc.yaml" created
INFO Kubernetes file "redis-svc.yaml" created
INFO Kubernetes file "web-deployment.yaml" created
INFO Kubernetes file "redis-deployment.yaml" created
chart created in "./docker-compose/"
tree docker-compose/
docker-compose
├── Chart.yaml
├── README.md
└── templates
    ├── redis-deployment.yaml
    ├── redis-svc.yaml
    ├── web-deployment.yaml
    └── web-svc.yaml

Структура чарту спрямована на надання каркаса для створення ваших чартів Helm.

Мітки

kompose підтримує специфічні для Kompose мітки в файлі docker-compose.yml, щоб явно визначити поведінку сервісу при конвертації.

  • kompose.service.type визначає тип сервісу, який потрібно створити.

    Наприклад:

    version: "2"
    services:
      nginx:
        image: nginx
        dockerfile: foobar
        build: ./foobar
        cap_add:
          - ALL
        container_name: foobar
        labels:
          kompose.service.type: nodeport
    
  • kompose.service.expose визначає, чи потрібно сервісу бути доступним ззовні кластера чи ні. Якщо значення встановлено на "true", постачальник автоматично встановлює точку доступу, і для будь-якого іншого значення, значення встановлюється як імʼя хосту. Якщо в сервісі визначено кілька портів, вибирається перший.

    • Для постачальника Kubernetes створюється ресурс Ingress, припускається, що контролер Ingress вже налаштований.
    • Для постачальника OpenShift створюється маршрут.

    Наприклад:

    version: "2"
    services:
      web:
        image: tuna/docker-counter23
        ports:
        - "5000:5000"
        links:
        - redis
        labels:
          kompose.service.expose: "counter.example.com"
      redis:
        image: redis:3.0
        ports:
        - "6379"
    

Наразі підтримуються наступні варіанти:

КлючЗначення
kompose.service.typenodeport / clusterip / loadbalancer
kompose.service.exposetrue / hostname

Перезапуск

Якщо ви хочете створити звичайні Podʼи без контролерів, ви можете використовувати конструкцію restart у docker-compose, щоб визначити це. Дивіться таблицю нижче, щоб побачити, що відбувається при значенні restart.

docker-compose restartстворений обʼєктrestartPolicy Podʼа
""обʼєкт контролераAlways
alwaysобʼєкт контролераAlways
on-failureКапсулаOnFailure
noКапсулаNever

Наприклад, сервіс pival стане Podʼом нижче. Цей контейнер обчислює значення pi.

version: '2'

services:
  pival:
    image: perl
    command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
    restart: "on-failure"

Попередження про конфігурації Deployment

Якщо в Docker Compose файлі вказано том для сервісу, стратегія Deployment (Kubernetes) або DeploymentConfig (OpenShift) змінюється на "Recreate" замість "RollingUpdate" (типово). Це робиться для того, щоб уникнути одночасного доступу кількох екземплярів сервісу до тому.

Якщо в Docker Compose файлі імʼя сервісу містить _ (наприклад, web_service), то воно буде замінено на -, і імʼя сервісу буде перейменовано відповідно (наприклад, web-service). Kompose робить це, оскільки "Kubernetes" не дозволяє _ в імені обʼєкта.

Зверніть увагу, що зміна назви сервісу може зіпсувати деякі файли docker-compose.

Версії Docker Compose

Kompose підтримує версії Docker Compose: 1, 2 та 3. Ми маємо обмежену підтримку версій 2.1 та 3.2 через їх експериментальний характер.

Повний список сумісності між усіма трьома версіями перераховано у нашому документі з конвертації, включаючи список всіх несумісних ключів Docker Compose.

3.26 - Забезпечення стандартів безпеки Podʼів шляхом конфігурування вбудованого контролера допуску

Kubernetes надає вбудований контролер допуску, щоб забезпечити дотримання стандартів безпеки Podʼів. Ви можете налаштувати цей контролер допуску, щоб встановити глобальні стандартні значення та винятки.

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

Після альфа-релізу в Kubernetes v1.22, Pod Security Admission став стандартно доступним в Kubernetes v1.23, у вигляді бета. Починаючи з версії 1.25, Pod Security Admissio доступний загалом.

Для перевірки версії введіть kubectl version.

Якщо у вас не запущено Kubernetes 1.31, ви можете перемикнутися на перегляд цієї сторінки в документації для версії Kubernetes, яку ви використовуєте.

Налаштування контролера допуску

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1 # див. примітку про сумісність
    kind: PodSecurityConfiguration
    # Стандартні значення, які застосовуються, коли мітка режиму не встановлена.
    #
    # Значення мітки рівня повинні бути одним з:
    # - "privileged" (станадртно)
    # - "baseline"
    # - "restricted"
    #
    # Значення мітки версії повинні бути одним з:
    # - "latest" (станадртно) 
    # - конкретна версія, наприклад "v1.31"
    defaults:
      enforce: "privileged"
      enforce-version: "latest"
      audit: "privileged"
      audit-version: "latest"
      warn: "privileged"
      warn-version: "latest"
    exemptions:
      # Масив імен користувачів для виключення.
      usernames: []
      # Масив імен класів виконання для виключення.
      runtimeClasses: []
      # Масив просторів імен для виключення.
      namespaces: []

3.27 - Забезпечення стандартів безпеки Podʼів за допомогою міток простору імен

Простори імен можуть бути позначені, щоб забезпечити стандарти безпеки Podʼів. Три політики privileged, baseline та restricted широко охоплюють спектр безпеки та реалізуються за допомогою контролера допуску безпеки Podʼа.

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

Pod Security Admission був доступний в Kubernetes v1.23, у вигляді бета. Починаючи з версії 1.25, Pod Security Admission доступний загалом.

Для перевірки версії введіть kubectl version.

Вимога стандарту безпеки Pod baseline за допомогою міток простору імен

Цей маніфест визначає Простір імен my-baseline-namespace, який:

  • Блокує будь-які Podʼи, які не відповідають вимогам політики baseline.
  • Генерує попередження для користувача та додає анотацію аудиту до будь-якого створеного Podʼа, який не відповідає вимогам політики restricted.
  • Фіксує версії політик baseline та restricted на v1.31.
apiVersion: v1
kind: Namespace
metadata:
  name: my-baseline-namespace
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.31

    # Ми встановлюємо ці рівні за нашим _бажаним_ `enforce` рівнем.
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: v1.31
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: v1.31

Додавання міток до наявних просторів імен за допомогою kubectl label

Корисно застосувати прапорець --dry-run при початковому оцінюванні змін профілю безпеки для просторів імен. Перевірки стандарту безпеки Pod все ще будуть виконуватися в режимі dry run, що дозволяє отримати інформацію про те, як нова політика буде обробляти наявні Podʼи, без фактичного оновлення політики.

kubectl label --dry-run=server --overwrite ns --all \
    pod-security.kubernetes.io/enforce=baseline

Застосування до всіх просторів імен

Якщо ви тільки починаєте зі Pod Security Standards, відповідний перший крок — налаштувати всі простори імен з анотаціями аудиту для строгого рівня, такого як baseline:

kubectl label --overwrite ns --all \
  pod-security.kubernetes.io/audit=baseline \
  pod-security.kubernetes.io/warn=baseline

Зверніть увагу, що це не встановлює рівень примусового виконання, щоб можна було відрізняти простори імен, для яких не було явно встановлено рівень примусового виконання. Ви можете перелічити простори імен без явно встановленого рівня примусового виконання за допомогою цієї команди:

kubectl get namespaces --selector='!pod-security.kubernetes.io/enforce'

Застосування до конкретного простору імен

Ви також можете оновити конкретний простір імен. Ця команда додає політику enforce=restricted до простору імен my-existing-namespace, закріплюючи версію обмеженої політики на v1.31.

kubectl label --overwrite ns my-existing-namespace \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/enforce-version=v1.31

3.28 - Міграція з PodSecurityPolicy до вбудованого контролера допуску PodSecurity

Ця сторінка описує процес міграції з PodSecurityPolicy до вбудованого контролера допуску PodSecurity. Це можна зробити ефективно за допомогою комбінації запуску dry-run та режимів audit та warn, хоча це стає складнішим, якщо використовуються мутуючі PSP.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.22. Для перевірки версії введіть kubectl version.

Якщо ви використовуєте відмінну від 1.31 версію Kubernetes, вам можливо захочеться перейти до перегляду цієї сторінки у документації для версії Kubernetes, яку ви фактично використовуєте.

Ця сторінка передбачає, що ви вже знайомі з основними концепціями Pod Security Admission.

Загальний підхід

Існує кілька стратегій для міграції з PodSecurityPolicy до Pod Security Admission. Наведені нижче кроки — це один з можливих шляхів міграції, з метою мінімізації ризиків виробничої перерви та пробілів у безпеці.

  1. Вирішіть, чи Pod Security Admission відповідає вашому випадку використання.
  2. Перегляньте дозволи простору імен.
  3. Спростіть та стандартизуйте PodSecurityPolicies.
  4. Оновіть простори імен
    1. Визначте відповідний рівень безпеки Podʼа.
    2. Перевірте рівень безпеки Podʼів.
    3. Застосуйте рівень безпеки Podʼів.
    4. Оминіть PodSecurityPolicy.
  5. Перегляньте процеси створення просторів імен.
  6. Вимкніть PodSecurityPolicy.

0. Вирішіть, чи Pod Security Admission підходить для вас

Pod Security Admission був розроблений для задоволення найбільш поширених потреб у безпеці з коробки, а також для забезпечення стандартного набору рівнів безпеки у всіх кластерах. Однак він менш гнучкий, ніж PodSecurityPolicy. Зокрема, наступні функції підтримуються за допомогою PodSecurityPolicy, але не за допомогою Pod Security Admission:

  • Встановлення типових обмежень безпеки — Pod Security Admission є немутуючим контролером допуску, що означає, що він не буде змінювати Podʼи перед їх перевіркою. Якщо ви покладалися на цей аспект PodSecurityPolicy, вам доведеться або модифікувати ваші робочі навантаження, щоб вони відповідали обмеженням безпеки Podʼів, або використовувати Мутуючий вебхук допуску для внесення цих змін. Дивіться Спрощення та стандартизація PodSecurityPolicies нижче для детальнішої інформації.
  • Докладний контроль над визначенням політики — Pod Security Admission підтримує тільки 3 стандартні рівні. Якщо вам потрібно більше контролю над конкретними обмеженнями, вам доведеться використовувати Вебхук допуску з перевіркою для виконання цих політик.
  • Деталізація політики на рівні підпросторів імен — PodSecurityPolicy дозволяє вам призначати різні політики різним обліковим записам служб або користувачам, навіть в межах одного простору імен. Цей підхід має багато підводних каменів і не рекомендується, але якщо вам все одно потрібна ця функція, вам потрібно буде використовувати сторонній вебхук. Виняток становить випадок, коли вам потрібно повністю виключити певних користувачів або RuntimeClasses, у цьому випадку Pod Security Admission все ж надає деякі статичні конфігурації для виключень.

Навіть якщо Pod Security Admission не відповідає всім вашим потребам, він був розроблений для доповнення інших механізмів контролю політики, і може бути корисним допоміжним засобом, який працює нарівно з іншими вебхуками допуску.

1. Перегляньте дозволи простору імен

Pod Security Admission керується мітками в просторах імен. Це означає, що будь-хто, хто може оновлювати (або накладати патчі, або створювати) простір імен, також може змінювати рівень безпеки Podʼів для цього простору імен, що може бути використано для обходу більш обмеженої політики. Перш ніж продовжувати, переконайтеся, що ці дозволи на простір імен мають лише довірені, привілейовані користувачі. Не рекомендується надавати ці могутні дозволи користувачам, які не повинні мати підвищені привілеї, але якщо вам доведеться це зробити, вам потрібно буде використовувати вебхук допуску, щоб накласти додаткові обмеження на встановлення міток безпеки Podʼів на обʼєкти Namespace.

2. Спрощення та стандартизація PodSecurityPolicies

На цьому етапі зменште кількість мутуючих PodSecurityPolicies і видаліть параметри, що виходять за межі Pod Security Standards. Зміни, рекомендовані тут, слід внести в офлайн-копію оригіналу PodSecurityPolicy, який ви змінюєте. Клонована PSP повинна мати іншу назву, яка в алфавітному порядку стоїть перед оригінальною (наприклад, додайте 0 до її назви). Не створюйте нові політики в Kubernetes зараз — це буде розглянуто в розділі Впровадження оновлених політик нижче.

2.а. Видалення явно мутуючих полів

Якщо PodSecurityPolicy змінює Podʼи, то ви можете опинитись з Podʼами, які не відповідають вимогам рівня безпеки Podʼів, коли ви нарешті вимкнете PodSecurityPolicy. Щоб уникнути цього, вам слід усунути всі мутації PSP перед переходом. На жаль, PSP чітко не розділяє мутуючі та валідуючі поля, тому цей перехід не є простим.

Ви можете почати з видалення полів, які є явно мутуючими та не мають жодного впливу на політику валлідації. Ці поля (також перераховані в довіднику Перенесення PodSecurityPolicies у Pod Security Standards) включають:

  • .spec.defaultAllowPrivilegeEscalation
  • .spec.runtimeClass.defaultRuntimeClassName
  • .metadata.annotations['seccomp.security.alpha.kubernetes.io/defaultProfileName']
  • .metadata.annotations['apparmor.security.beta.kubernetes.io/defaultProfileName']
  • .spec.defaultAddCapabilities — хоча це технічно мутуюче і валідуюче поле, його слід обʼєднати з .spec.allowedCapabilities, яке виконує ту саму валідацію без мутації.

2.б. Вилучення параметрів, що не підпадають під Pod Security Standards

Існують кілька полів в PodSecurityPolicy, які не входять до складу Pod Security Standards. Якщо вам потрібно забезпечити ці опції, вам доведеться доповнити Pod Security Admission за допомогою вебхуків допуску, що не входить в рамки цього посібника.

Спочатку ви можете видалити явно валідуючі поля, які не охоплюються Pod Security Standards. Ці поля (також перераховані в довіднику Перенесення PodSecurityPolicies у Pod Security Standards з поміткою "no opinion") включають:

  • .spec.allowedHostPaths
  • .spec.allowedFlexVolumes
  • .spec.allowedCSIDrivers
  • .spec.forbiddenSysctls
  • .spec.runtimeClass

Ви також можете видалити наступні поля, які стосуються управління групами POSIX / UNIX.

  • .spec.runAsGroup
  • .spec.supplementalGroups
  • .spec.fsGroup

Залишені мутуючі поля потрібні для належної підтримки Pod Security Standards і будуть оброблені в окремому порядку:

  • .spec.requiredDropCapabilities — Потрібно видалити ALL щоб зробити профіль Restricted.
  • .spec.seLinux — (Тільки мутуюче з правилом MustRunAs) потрібно для забезпечення вимог SELinux для профілів Baseline & Restricted.
  • .spec.runAsUser — (Не мутує з правилом RunAsAny) потрібно для забезпечення RunAsNonRoot для профілю Restricted.
  • .spec.allowPrivilegeEscalation — (Тільки мутується, якщо встановлено false) потрібно для профілю Restricted.

2.в. Впровадження оновлених PSP

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

Для кожної оновленої PodSecurityPolicy:

  1. Визначте, які Podʼи працюють під оригінальною PSP. Це можна зробити за допомогою анотації kubernetes.io/psp. Наприклад, використовуючи kubectl:

    PSP_NAME="original" # Встановіть назву PSP, яку ви перевіряєте
    kubectl get pods --all-namespaces -o jsonpath="{range .items[?(@.metadata.annotations.kubernetes\.io\/psp=='$PSP_NAME')]}{.metadata.namespace} {.metadata.name}{'\n'}{end}"
    
  2. Порівняйте ці робочі навантаження з оригінальною специфікацією Podаʼа, щоб визначити, чи змінила PodSecurityPolicy Pod. Для Podʼів, створених за ресурсами робочого навантаження, ви можете порівняти Pod з PodTemplate в ресурсі контролера. Якщо будь-які зміни виявлені, оригінальний Pod або PodTemplate слід оновити з необхідною конфігурацією. Поля для перегляду:

    • .metadata.annotations['container.apparmor.security.beta.kubernetes.io/*'] (замініть * на назву кожного контейнера)
    • .spec.runtimeClassName
    • .spec.securityContext.fsGroup
    • .spec.securityContext.seccompProfile
    • .spec.securityContext.seLinuxOptions
    • .spec.securityContext.supplementalGroups
    • У контейнерів, під .spec.containers[*] та .spec.initContainers[*]:
      • .securityContext.allowPrivilegeEscalation
      • .securityContext.capabilities.add
      • .securityContext.capabilities.drop
      • .securityContext.readOnlyRootFilesystem
      • .securityContext.runAsGroup
      • .securityContext.runAsNonRoot
      • .securityContext.runAsUser
      • .securityContext.seccompProfile
      • .securityContext.seLinuxOptions
  3. Створіть нові PodSecurityPolicies. Якщо будь-які Roles або ClusterRoles надають use на всі PSP, це може призвести до використання нових PSP замість їх мутуючих аналогів.

  4. Оновіть вашу авторизацію, щоб дозволити доступ до нових PSP. У RBAC це означає оновлення будь-яких Roles або ClusterRoles, які надають дозвіл use на оригінальну PSP, щоб також надати його оновленому PSP.

  5. Перевірте: після деякого часу повторно виконайте команду з кроку 1, щоб переглянути, чи використовуються будь-які Podʼи за оригінальними PSP. Зверніть увагу, що Podʼи повинні бути перестворені після того, як нові політики будуть впроваджені, перш ніж їх можна буде повністю перевірити.

  6. (опціонально) Після того, як ви перевірили, що оригінальні PSP більше не використовуються, ви можете їх видалити.

3. Оновлення просторів імен

Наступні кроки потрібно виконати для кожного простору імен у кластері. Команди, на які посилаються в цих кроках, використовують змінну $NAMESPACE для посилання на простір імен, який оновлюється.

3.а. Визначення відповідного рівня безпеки Podʼа

Почніть з ознайомлення зі Стандартами безпеки Podʼа та з трьома різними рівнями цих стандартів.

Існує кілька способів вибору рівня безпеки Podʼа для вашого простору імен:

  1. За вимогами безпеки для простору імен — Якщо ви знайомі з очікуваним рівнем доступу для простору імен, ви можете вибрати відповідний рівень, базуючись на цих вимогах, подібно до підходу, який можна застосувати в новому кластері.

  2. За поточними PodSecurityPolicies — Використовуючи довідник Перенесення PodSecurityPolicies у Pod Security Standards, ви можете віднести кожен PSP до рівня стандарту безпеки Podʼа. Якщо ваші PSP не базуються на Pod Security Standards, вам може знадобитися вирішити, обирати рівень, який є принаймні таким же дозвільним, як PSP, або рівень, який є принаймні таким же обмежувальним. Які PSP використовуються для Podʼів у даному просторі імен, ви можете побачити за допомогою цієї команди:

    kubectl get pods -n $NAMESPACE -o jsonpath="{.items[*].metadata.annotations.kubernetes\.io\/psp}" | tr " " "\n" | sort -u
    
  3. За поточними Podʼами — Використовуючи стратегії з розділу Перевірка рівня безпеки Podʼа, ви можете перевірити рівні Baseline і Restricted, щоб побачити, чи є вони достатньо дозвільними для наявних робочих навантажень, і вибрати найменш привілейований валідний рівень.

3.б. Перевірка рівня безпеки Podʼа

Після того як ви обрали рівень безпеки Podʼа для простору імен (або якщо ви пробуєте кілька), добре було б спершу його протестувати (цей крок можна пропустити, якщо використовується рівень Privileged). Безпека Podʼа включає кілька інструментів для тестування та безпечного впровадження профілів.

Спочатку ви можете виконати пробний запуск політики, який оцінить Podʼи, що вже працюють у просторі імен, відносно застосованої політики, не впроваджуючи нову політику в дію:

# $LEVEL - рівень для пробного запуску, або "baseline", або "restricted".
kubectl label --dry-run=server --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL

Ця команда поверне попередження для будь-яких наявних Podʼів, які не відповідають запропонованому рівню.

Другий варіант краще підходить для виявлення робочих навантажень, які в цей момент не запущені: режим аудиту. Коли запущено в режимі аудиту (на відміну від примусового впровадження), Podʼи, які порушують рівень політики, реєструються у логах аудиту, які можна переглянути пізніше після деякого часу, але вони не заборонені. Режим попередження працює подібно, але надсилає попередження користувачу негайно. Ви можете встановити рівень аудиту для простору імен за допомогою цієї команди:

kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/audit=$LEVEL

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

3.в. Впровадження рівня безпеки Podʼа

Коли ви переконаєтеся, що обраний рівень може бути безпечно впроваджений для простору імен, ви можете оновити простір імен, щоб впровадити бажаний рівень:

kubectl label --overwrite ns $NAMESPACE pod-security.kubernetes.io/enforce=$LEVEL

3.г. Оминання політики безпеки Pod

Наостанок, ви можете ефективно оминути PodSecurityPolicy на рівні простору імен, привʼязавши повністю привілейовану PSP до всіх облікових записів служб у просторі імен.

# Наступні команди, які виконуються на рівні кластера, потрібні лише один раз.
kubectl apply -f privileged-psp.yaml
kubectl create clusterrole privileged-psp --verb use --resource podsecuritypolicies.policy --resource-name privileged

# Вимкнення на рівні простору імен
kubectl create -n $NAMESPACE rolebinding disable-psp --clusterrole privileged-psp --group system:serviceaccounts:$NAMESPACE

Оскільки привілейована PSP не є мутуючою, а контролер прийняття рішення PSP завжди віддає перевагу немутуючим PSP, це гарантує, що Podʼи в цьому просторі імен більше не змінюються або не обмежуються PodSecurityPolicy.

Перевага вимкнення PodSecurityPolicy на рівні простору імен полягає в тому, що у разі виникнення проблеми ви можете легко скасувати зміну, видаливши RoleBinding. Просто переконайтеся, що попередньо наявні PodSecurityPolicy все ще застосовуються!

# Скасування вимкнення PodSecurityPolicy.
kubectl delete -n $NAMESPACE rolebinding disable-psp

4. Перегляд процесів створення просторів імен

Тепер, коли поточні простори імен оновлено для забезпечення прийняття рішень щодо Pod Security Admission, вам слід переконатися, що ваші процеси та/або політики для створення нових просторів імен оновлено, щоб гарантувати застосування відповідного профілю безпеки Pod для нових просторів імен.

Ви також можете статично налаштувати контролер Pod Security Admission для встановлення типового рівня впровадження, аудиту та/або попередження для просторів імен без міток. Див. Налаштування контролера допуску для отримання додаткової інформації.

5. Вимкнення PodSecurityPolicy

Нарешті, ви готові вимкнути PodSecurityPolicy. Для цього вам потрібно змінити конфігурацію допуску сервера API: Як я можу вимкнути контролер допуску?.

Щоб перевірити, що контролер допуску PodSecurityPolicy більше не активний, ви можете вручну запустити тест, видаючи себе за користувача без доступу до будь-яких PodSecurityPolicies (див. приклад PodSecurityPolicy), або перевірити логи сервера API. При запуску сервер API виводить рядки логу, що перераховують завантажені втулки контролера допуску:

I0218 00:59:44.903329      13 plugins.go:158] Loaded 16 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction,TaintNodesByCondition,Priority,DefaultTolerationSeconds,ExtendedResourceToleration,PersistentVolumeLabel,DefaultStorageClass,StorageObjectInUseProtection,RuntimeClass,DefaultIngressClass,MutatingAdmissionWebhook.
I0218 00:59:44.903350      13 plugins.go:161] Loaded 14 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurity,Priority,PersistentVolumeClaimResize,RuntimeClass,CertificateApproval,CertificateSigning,CertificateSubjectRestriction,DenyServiceExternalIPs,ValidatingAdmissionWebhook,ResourceQuota.

Ви маєте побачити PodSecurity (у списку контролерів допуску для перевірки валідності), і жоден зі списків не повинен містити PodSecurityPolicy.

Після того, як ви впевнені, що контролер допуску PSP вимкнуто (і після достатнього часу, щоб бути впевненим, що вам не потрібно буде відкочувати зміни), ви вільні видалити ваші PodSecurityPolicies та будь-які повʼязані Roles, ClusterRoles, RoleBindings та ClusterRoleBindings (просто переконайтеся, що вони не надають будь-яких інших неповʼязаних дозволів).

4 - Моніторинг, логування та налагодження

Налаштуйте моніторинг та логування для розвʼязання проблем кластера або налагодження контейнеризованих застосунків.

Іноді речі йдуть не так. Цей посібник призначений для допомоги у виправленні помилок. Він має два розділи:

Вам також слід перевірити відомі проблеми для випуску, який ви використовуєте.

Отримання допомоги

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

Питання

Документація на цьому сайті структурована таким чином, щоб надавати відповіді на широкий спектр питань. Концепції пояснюють архітектуру Kubernetes та роботу кожного компонента, в той час, як Налаштування надає практичні інструкції для початку роботи. Завдання показують, як виконати широке коло завдань, а Посібники — це більш комплексні огляди сценаріїв реального життя, специфічних для галузей або розробки з початку до кінця. Розділ Довідник надає детальну документацію з API Kubernetes та інтерфейсів командного рядка (CLI), таких як kubectl.

Допоможіть! Моє питання не розглянуто! Мені потрібна допомога зараз!

Stack Exchange, Stack Overflow або Server Fault

Якщо у вас є питання, повʼязане з розробкою програмного забезпечення для вашого контейнеризованого застосунку, ви можете задати його на Stack Overflow.

Якщо у вас є питання про Kubernetes, повʼязані з управлінням кластером або конфігурацією, ви можете задати їх на Server Fault.

Також існують кілька більш конкретних сайтів мережі Stack Exchange, які можуть бути відповідним місцем для питань про Kubernetes в таких областях, як DevOps, Software Engineering або InfoSec.

Хтось зі спільноти може вже ставив схоже питання або може допомогти з вашою проблемою.

Команда Kubernetes також слідкуватиме за постами з теґом Kubernetes. Якщо немає наявних питань, які можуть допомогти, будь ласка, переконайтеся, що ваше питання відповідає правилам Stack Overflow, Server Fault або сайту мережі Stack Exchange, на якому ви його ставите, і ознайомтеся з інструкцією як поставити нове питання, перед тим як задавати нове!

Slack

Багато людей зі спільноти Kubernetes збираються у Kubernetes Slack в каналі #kubernetes-users. Slack вимагає реєстрації; ви можете запросити запрошення, і реєстрація відкрита для всіх). Запрошуємо приходити та ставити будь-які питання. Після реєстрації ви отримайте доступ до організації Kubernetes у Slack у вебоглядачі або в застосунку Slack.

Після реєстрації переглядайте список каналів для різних тем. Наприклад, люди, які тільки вчаться Kubernetes, також можуть приєднатися до каналу #kubernetes-novice. Як ще один приклад, розробникам корисно приєднатися до каналу #kubernetes-contributors.

Також існують багато каналів, специфічних для країн / мов. Запрошуємо приєднатися до цих каналів для локалізованої підтримки та інформації:

Країна / канал Slack
КраїнаКанали
Китай#cn-users, #cn-events
Фінляндія#fi-users
Франція#fr-users, #fr-events
Німеччина#de-users, #de-events
Індія#in-users, #in-events
Італія#it-users, #it-events
Японія#jp-users, #jp-events
Корея#kr-users
Нідерланди#nl-users
Норвегія#norw-users
Польща#pl-users
Росія#ru-users
Іспанія#es-users
Швеція#se-users
Туреччина#tr-users, #tr-events

Форум

Вас раді бачити на офіційному форумі Kubernetes: discuss.kubernetes.io.

Помилки та запитання щодо функціонала

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

Перед тим як створити тікет, будь ласка, перегляньте наявні проблеми, щоб переконатися, чи є вже вирішення для вашої проблеми.

Якщо ви подаєте звіт про помилку, будь ласка, включіть детальні відомості про те, як відтворити проблему, таку як:

  • Версія Kubernetes: kubectl version
  • Хмарний провайдер, дистрибутив ОС, конфігурація мережі та версія оточення виконання контейнерів
  • Кроки для відтворення проблеми

4.1 - Налагодження застосунків

Виправлення загальних проблем з контейнеризованими застосунками.

Цей розділ містить набір ресурсів для виправлення проблем з контейнеризованими застосунками. Він охоплює такі речі, як загальні проблеми з ресурсами Kubernetes (наприклад, Pods, Services або StatefulSets), поради щодо розуміння повідомлень про припинення роботи контейнера та способи налагодження запущених контейнерів.

4.1.1 - Налагодження Podʼів

Цей посібник допоможе користувачам налагодити програми, які розгорнуті у Kubernetes та які поводяться некоректно. Це не настанова для людей, які хочуть налагоджувати свій кластер. Для цього вам слід ознайомитися з цим посібником.

Діагностика проблеми

Перший крок у розвʼязанні проблем — сортування. Що сталося? Проблема у ваших Podʼах, Контролері Реплікацій чи Service?

Налагодження Podʼів

Перший крок у налагоджені Podʼа — це його перевірка. Перевірте поточний стан Podʼа та останні події за допомогою наступної команди:

kubectl describe pods ${POD_NAME}

Огляньте стан контейнерів у Podʼі. Чи всі вони Running (працюють)? Чи були нещодавні перезапуски?

Продовжуйте налагодження, виходячи зі стану Podʼів.

Мій Pod залишається в стані Pending

Якщо Pod застряг у стані Pending, це означає, що його не можна запланувати на вузол. Зазвичай це відбувається через недостатні ресурси одного типу чи іншого, які заважають плануванню. Подивіться на вивід команди kubectl describe ... вище. Там мають бути повідомлення від планувальника про причини, чому він не може запланувати ваш Pod. Причини включають:

  • У вас недостатньо ресурсів: Можливо, ви вичерпали запас CPU або памʼяті у вашому кластері, у цьому випадку вам потрібно видалити Podʼи, налаштувати запити на ресурси, або додати нові вузли до вашого кластера. Дивіться документ про обчислювальні ресурси для отримання додаткової інформації.

  • Ви використовуєте hostPort: Коли ви привʼязуєте Pod до hostPort, існує обмежена кількість місць, де можна запланувати цей Pod. У більшості випадків, hostPort не є необхідним, спробуйте використати обʼєкт Service для відкриття доступу вашому Podʼу. Якщо вам все ж потрібен hostPort, то ви можете запланувати лише стільки Podʼів, скільки у вас вузлів у кластері Kubernetes.

Мій Pod залишається у стані Waiting

Якщо Pod застряг у стані Waiting, то він був запланований на робочий вузол, але не може працювати на цій машині. Знову ж таки, вивід команди kubectl describe ... має бути інформативним. Найпоширенішою причиною Podʼів у стані Waiting є неможливість завантаження образу. Тут є три речі, які потрібно перевірити:

  • Переконайтеся, що ви правильно вказали імʼя образу.
  • Чи ви завантажили образ у реєстр?
  • Спробуйте вручну завантажити образ, щоб перевірити, чи можна його завантажити. Наприклад, якщо ви використовуєте Docker на вашому ПК, виконайте docker pull <image>.

Мій Pod залишається у стані Terminating

Якщо Pod застряг у стані Terminating (завершення), це означає, що було видано наказ про видалення Podʼа, але планпанель управління не може видалити обʼєкт Pod.

Це зазвичай відбувається, якщо у Podʼа є завершувач та встановлений вубхук допуску у кластері, який перешкоджає панелі управління видалити завершувач.

Щоб виявити цей сценарій, перевірте, чи у вашому кластері є вебхуки ValidatingWebhookConfiguration або MutatingWebhookConfiguration, які спрямовані на операції UPDATE для ресурсів pods.

Якщо вебхук надається стороннім вендором:

  • Переконайтеся, що ви використовуєте останню версію.
  • Вимкніть вебхук для операцій UPDATE.
  • Повідомте про проблему відповідного постачальника.

Якщо ви є автором вебхуку:

  • Для мутуючого вебхуку переконайтеся, що він ніколи не змінює незмінні поля під час операцій UPDATE. Наприклад, зміни контейнерів зазвичай не дозволяються.
  • Для перевірки вебхуку переконайтеся, що ваші політики валідації застосовуються лише до нових змін. Іншими словами, ви повинні дозволяти Podʼам з наявними порушеннями пройти валідацію. Це дозволяє Podʼам, які були створені до встановлення вебхуку валідації, продовжувати працювати.

Мій Pod виходить з ладу або несправний іншим чином

Як тільки ваш Pod був запланований, методи, описані в Налагодження Запущених Подів, доступні для налагодження.

Мій Pod працює, але не робить те, що я йому сказав робити

Якщо ваш Pod не поводиться так, як ви очікували, це може бути повʼязано з помилкою у вашому описі Podʼа (наприклад, файл mypod.yaml на вашому локальному компʼютері), і цю помилку було мовчки проігноровано під час створення Podʼа. Часто розділ опису Podʼа вкладено неправильно, або імʼя ключа набрано неправильно, і тому ключ ігнорується. Наприклад, якщо ви помилилися в написанні command як commnd, тоді Pod буде створено, але не використовуватиме командний рядок, який ви планували.

Перша річ, яку варто зробити, — видалити свій Pod і спробувати створити його знову з опцією --validate. Наприклад, виконайте kubectl apply --validate -f mypod.yaml. Якщо ви помилилися в написанні command як commnd, то отримаєте помилку на кшталт цієї:

I0805 10:43:25.129850   46757 schema.go:126] unknown field: commnd
I0805 10:43:25.129973   46757 schema.go:129] this may be a false alarm, see https://github.com/kubernetes/kubernetes/issues/6842
pods/mypod

Наступне, що варто перевірити, — чи відповідає Pod на апісервері Podʼу, який ви збиралися створити (наприклад, у файлі YAML на вашому локальному компʼютері). Наприклад, виконайте kubectl get pods/mypod -o yaml > mypod-on-apiserver.yaml а потім вручну порівняйте оригінальний опис Podʼа, mypod.yaml, з тим, який ви отримали з апісервера, mypod-on-apiserver.yaml. Зазвичай деякі рядки в версії "апісервера" відсутні в оригінальній версії. Це очікувано. Однак, якщо є рядки в оригінальному описі, яких немає в версії апісервера, то це може свідчити про проблему з вашими специфікаціями Podʼа.

Налагодження Контролерів Реплікацій

Контролери реплікацій досить прості. Вони можуть або створювати Podʼи, або не можуть. Якщо вони не можуть створювати Podʼи, будь ласка, зверніться до інструкцій вище, щоб налагодити ваші Podʼи.

Ви також можете використовувати kubectl describe rc ${CONTROLLER_NAME} для вивчення подій, що стосуються контролера реплікацій.

Налагодження Serviceʼів

Serviceʼи забезпечують балансування навантаження між набором Podʼів. Є кілька загальних проблем, які можуть призвести до неправильної роботи Serviceʼів. Наступні інструкції допоможуть дослідити проблеми з Serviceʼами.

Спочатку перевірте, що для Service є точки доступу. Для кожного обʼєкта Service апісервер робить ресурс endpoints доступним.

Ви можете переглянути цей ресурс за допомогою:

kubectl get endpoints ${SERVICE_NAME}

Переконайтеся, що точки доступу відповідають кількості Podʼів, які ви очікуєте бачити в складі вашого Service. Наприклад, якщо ваш Service для контейнера nginx має 3 репліки, ви очікуєте побачити три різних IP-адреси у точках доступу Serviceʼу.

Мій Service відсутні точки доступу

Якщо вам не вистачає точок доступу, спробуйте передивитись перелік Podʼів за допомогою міток, які використовує Service. Уявіть, що у вас є Service, де є такі мітки:

...
spec:
  - selector:
     name: nginx
     type: frontend

Ви можете використовувати:

kubectl get pods --selector=name=nginx,type=frontend

щоб отримати перелік Podʼів, які відповідають цьому селектору. Перевірте, чи список відповідає Podʼам, які ви очікуєте бачити у вашому Service. Перевірте, чи containerPort Podʼа відповідає targetPort Serviceʼа.

Мережевий трафік не переспрямовується

Будь ласка, див. налагодження Service для отримання додаткової інформації.

Що далі

Якщо жоден із вищезазначених методів не розвʼязує вашу проблему, слідкуйте інструкціям у документі про налагодження Serviceʼу щоб переконатися, що ваш Service працює, має Endpoints, і ваші Podʼи фактично обслуговуються; DNS працює, встановлені правила iptables, і kube-proxy поводиться відповідно.

Ви також можете відвідати документ про налагодження для отримання додаткової інформації.

4.1.2 - Налагодження Service

Досить часто виникає проблема в нових інсталяціях Kubernetes, коли Service не працює належним чином. Ви запустили свої Podʼи за допомогою Deployment (або іншого контролера робочих навантажень) і створили Service, але не отримуєте відповіді при спробі отримати до нього доступ. Цей документ, сподіваємось, допоможе вам зрозуміти, що йде не так.

Виконання команд у Podʼі

Для багатьох кроків ви захочете побачити, що бачить Pod, який працює в кластері. Найпростіший спосіб зробити це — запустити інтерактивний Pod busybox:

kubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox sh

Якщо у вас вже є Pod, який ви хочете використовувати для цього, ви можете виконати команду у ньому за допомогою:

kubectl exec <ІМ'Я-ПОДА> -c <ІМ'Я-КОНТЕЙНЕРА> -- <КОМАНДА>

Підготовка

Для цілей цього огляду запустімо кілька Podʼів. Оскільки ви, швидше за все, налагоджуєте власний Service, ви можете використати свої власні дані, або ви можете слідувати разом із нами та отримати інші дані.

kubectl create deployment hostnames --image=registry.k8s.io/serve_hostname
deployment.apps/hostnames created

Команди kubectl будуть виводити тип та імʼя створеного або зміненого ресурсу, які потім можна використовувати в наступних командах.

Масштабуймо Deployment до 3 реплік.

kubectl scale deployment hostnames --replicas=3
deployment.apps/hostnames scaled

Зверніть увагу, що це так само, якби ви запустили Deployment за допомогою наступного YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: hostnames
  name: hostnames
spec:
  selector:
    matchLabels:
      app: hostnames
  replicas: 3
  template:
    metadata:
      labels:
        app: hostnames
    spec:
      containers:
      - name: hostnames
        image: registry.k8s.io/serve_hostname

За допомогою kubectl create deployment в значення мітки "app" автоматично встановлюється імʼя Deployment.

Ви можете підтвердити, що ваші Podʼи працюють:

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          2m
hostnames-632524106-ly40y   1/1       Running   0          2m
hostnames-632524106-tlaok   1/1       Running   0          2m

Ви також можете підтвердити, що ваші Podʼи обслуговуються. Ви можете отримати список IP-адрес Podʼів та протестувати їх безпосередньо.

kubectl get pods -l app=hostnames \
    -o go-template='{{range .items}}{{.status.podIP}}{{"\n"}}{{end}}'
10.244.0.5
10.244.0.6
10.244.0.7

Приклад контейнера, використаного для цього огляду, обслуговує своє власне імʼя хосту через HTTP на порті 9376, але якщо ви налагоджуєте свій власний застосунок, вам потрібно буде використовувати номер порту, на якому слухають ваші Podʼи.

З середини Podʼа:

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

Ви маєте отримати щось на зразок:

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

Якщо ви не отримуєте очікувані відповіді на цьому етапі, ваші Pod\и можуть бути несправними або можуть не прослуховувати порт, який ви вважаєте. Можливо, kubectl logs буде корисним для перегляду того, що відбувається, або, можливо, вам потрібно буде виконати kubectl exec безпосередньо у вашому Podʼі та робити налагодження звідти.

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

Чи Service існує?

Уважний читач може зауважити, що ви фактично ще не створили Service — це навмисно. Це крок, який іноді забувають, і це перше, що треба перевірити.

Що станеться, якщо ви спробуєте отримати доступ до Service, що не існує? Якщо у вас є інший Pod, який використовує цей Service за іменем, ви отримаєте щось на зразок:

wget -O- hostnames
Resolving hostnames (hostnames)... failed: Name or service not known.
wget: unable to resolve host address 'hostnames'

Перше, що треба перевірити — це чи насправді існує цей Service:

kubectl get svc hostnames
No resources found.
Error from server (NotFound): services "hostnames" not found

Створімо Service. Як і раніше, це для цього огляду — ви можете використовувати свої власні дані Service тут.

kubectl expose deployment hostnames --port=80 --target-port=9376
service/hostnames exposed

І перевіримо:

kubectl get svc hostnames
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
hostnames   ClusterIP   10.0.1.175   <none>        80/TCP    5s

Тепер ви знаєте, що Service існує.

Як і раніше, це те саме, якби ви запустили Service за допомогою YAML:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: hostnames
  name: hostnames
spec:
  selector:
    app: hostnames
  ports:
  - name: default
    protocol: TCP
    port: 80
    targetPort: 9376

Щоб підкреслити повний спектр налаштувань, Service, який ви створили тут, використовує інший номер порту, ніж Podʼи. Для багатьох реальних Serviceʼів ці значення можуть бути однаковими.

Чи впливають будь-які правила Network Policy Ingress на цільові Podʼи?

Якщо ви розгорнули будь-які правила Network Policy Ingress, які можуть вплинути на вхідний трафік до hostnames-* Pod, їх потрібно переглянути.

Будь ласка, зверніться до Мережевих політик для отримання додаткових відомостей.

Чи працює Service за DNS-іменем?

Один із найпоширеніших способів, яким клієнти використовують Service, — це через DNS-імʼя.

З Podʼа в тому ж Namespace:

nslookup hostnames
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

Якщо це не вдається, можливо, ваш Pod і Service знаходяться в різних Namespace, спробуйте використати імʼя з Namespace (знову ж таки, зсередини Podʼа):

nslookup hostnames.default
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

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

nslookup hostnames.default.svc.cluster.local
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      hostnames.default.svc.cluster.local
Address 1: 10.0.1.175 hostnames.default.svc.cluster.local

Зверніть увагу на суфікс тут: "default.svc.cluster.local". "default" — це Namespace, в якому ви працюєте. "svc" позначає, що це Service. "cluster.local" — це ваш домен кластера, який МОЖЕ відрізнятися у вашому власному кластері.

Ви також можете спробувати це з вузла в кластері:

nslookup hostnames.default.svc.cluster.local 10.0.0.10
Server:         10.0.0.10
Address:        10.0.0.10#53

Name:   hostnames.default.svc.cluster.local
Address: 10.0.1.175

Якщо ви можете зробити пошук повного імені, але не відноснлшл, вам потрібно перевірити, чи правильний ваш файл /etc/resolv.conf в Podʼі. Зсередини Podʼа:

cat /etc/resolv.conf

Ви повинні побачити щось на зразок:

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local example.com
options ndots:5

Рядок nameserver повинен вказувати на Service DNS вашого кластера. Це передається в kubelet з прапорцем --cluster-dns.

Рядок search повинен включати відповідний суфікс, щоб ви змогли знайти імʼя Serviceʼу. У цьому випадку він шукає Serviceʼи в локальному Namespace ("default.svc.cluster.local"), Serviceʼи у всіх Namespace ("svc.cluster.local"), і наостанок для імен в кластері ("cluster.local"). Залежно від вашого власного налаштування, у вас можуть бути додаткові записи після цього (до 6 загалом). Суфікс кластера передається в kubelet з прапорцем --cluster-domain. У цьому документі передбачається, що суфікс кластера — "cluster.local". У ваших власних кластерах це може бути налаштовано по-іншому, у такому випадку вам слід змінити це в усіх попередніх командах.

Чи працює будь-який Service за допомогою DNS-імені?

Якщо вищезазначене все ще не працює, DNS-запити не працюють для вашого Service. Ви можете зробити крок назад і побачити, що ще не працює. Service майстра Kubernetes повинен завжди працювати. Зсередини Podʼа:

nslookup kubernetes.default
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes.default
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local

Якщо це не вдається, будь ласка, перегляньте розділ kube-proxy цього документа, або навіть поверніться до початку цього документа і почніть знову, але замість налагодження вашого власного Service, спробуйте Service DNS.

Чи працює Service за IP?

Припускаючи, що ви підтвердили, що DNS працює, наступне, що варто перевірити, — це чи ваш Service працює за його IP-адресою. З Podʼа в вашому кластері, зверніться до IP-адреси Service (з вищезазначеного виводу kubectl get).

for i in $(seq 1 3); do 
    wget -qO- 10.0.1.175:80
done

Це має видати щось подібне:

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

Якщо ваш Service працює, ви повинні отримати правильні відповіді. Якщо ні, існує кілька можливих причин, через які це може не працювати. Дивіться далі.

Чи правильно визначений Service?

Це може звучати дивно, але ви справді повинні подеколи перевірити, що ваш Service вірний і відповідає порту вашого Podʼа. Перегляньте свій Service і перевірте його:

kubectl get service hostnames -o json
{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "hostnames",
        "namespace": "default",
        "uid": "428c8b6c-24bc-11e5-936d-42010af0a9bc",
        "resourceVersion": "347189",
        "creationTimestamp": "2015-07-07T15:24:29Z",
        "labels": {
            "app": "hostnames"
        }
    },
    "spec": {
        "ports": [
            {
                "name": "default",
                "protocol": "TCP",
                "port": 80,
                "targetPort": 9376,
                "nodePort": 0
            }
        ],
        "selector": {
            "app": "hostnames"
        },
        "clusterIP": "10.0.1.175",
        "type": "ClusterIP",
        "sessionAffinity": "None"
    },
    "status": {
        "loadBalancer": {}
    }
}
  • Чи вказано порт Serviceʼу, який ви намагаєтеся отримати доступ в spec.ports[]?
  • Чи правильний targetPort для ваших Podʼів (деякі Podʼи використовують інший порт, ніж Service)?
  • Якщо ви мали на увазі використання числового порту, це число (9376) чи рядок "9376"?
  • Якщо ви мали на увазі використання порту за іменем, чи ваші Podʼи використовують порт з тим самим імʼям?
  • Чи правильний протокол порту для ваших Podʼів?

Чи має Service будь-які Endpoints?

Якщо ви дійшли до цього пункту, ви підтвердили, що ваш Service правильно визначений і його можна знайти через DNS. Тепер перевірмо, що Podʼи, які ви запустили, фактично вибираються Serviceʼом.

Раніше ви бачили, що Podʼи працюють. Ви можете перевірити це ще раз:

kubectl get pods -l app=hostnames
NAME                        READY     STATUS    RESTARTS   AGE
hostnames-632524106-bbpiw   1/1       Running   0          1h
hostnames-632524106-ly40y   1/1       Running   0          1h
hostnames-632524106-tlaok   1/1       Running   0          1h

Аргумент -l app=hostnames — це селектор міток, налаштований у Service.

Стовпець "AGE" вказує, що ці Podʼи працюють добре і не мали збоїв.

Стовпець "RESTARTS" вказує, що ці Podʼи не часто мають збої або не перезавантажуються. Часті перезапуски можуть призвести до періодичних проблем зі зʼєднанням. Якщо кількість перезапусків висока, прочитайте більше про те, як налагоджувати Podʼи.

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

kubectl get endpoints hostnames

NAME        ENDPOINTS
hostnames   10.244.0.5:9376,10.244.0.6:9376,10.244.0.7:9376

Це підтверджує, що контролер Endpoints знайшов правильні Podʼи для вашого Service. Якщо стовпець ENDPOINTS має значення <none>, вам слід перевірити, що поле spec.selector вашого Service дійсно вибирає значення metadata.labels у ваших Podʼах. Частою помилкою є наявність хибодруку або іншої помилки, наприклад, Service вибирає app=hostnames, але Deployment вказує run=hostnames, як у версіях до 1.18, де команда kubectl run також могла бути використана для створення Deployment.

Чи працюють Podʼи?

На цьому етапі ви знаєте, що ваш Service існує і вибрав ваші Podʼи. На початку цього кроку ви перевірили самі Podʼи. Давайте ще раз перевіримо, що Podʼи дійсно працюють — ви можете оминути механізм Service і звернутися безпосередньо до Podʼів, які вказані в Endpoints вище.

З середини Podʼа:

for ep in 10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376; do
    wget -qO- $ep
done

Це повинно показати щось на зразок:

hostnames-632524106-bbpiw
hostnames-632524106-ly40y
hostnames-632524106-tlaok

Ви очікуєте, що кожний Pod в списку Endpoints поверне свій власний hostname. Якщо це не те, що відбувається (або будь-яка інша правильна поведінка для ваших власних Podʼів), вам слід дослідити, що відбувається там.

Чи працює kube-proxy?

Якщо ви дісталися до цього моменту, ваш Service працює, має Endpoints, і ваші Podʼи фактично обслуговують запити. На цьому етапі увесь механізм проксі Service під підозрою. Підтвердьмо це крок за кроком.

Стандартна реалізація Service, і та, яка використовується в більшості кластерів, — це kube-proxy. Це програма, яка працює на кожному вузлі і конфігурує один з невеликого набору механізмів для надання абстракції Service. Якщо ваш кластер не використовує kube-proxy, наступні розділи не застосовуються, і вам доведеться дослідити будь-яку реалізацію Service, яку ви використовуєте.

Чи запущено kube-proxy?

Підтвердіть, що kube-proxy працює на ваших Вузлах. Запустіть команду безпосередньо на Вузлі, і ви повинні побачити щось на зразок такого:

ps auxw | grep kube-proxy
root  4194  0.4  0.1 101864 17696 ?    Sl Jul04  25:43 /usr/local/bin/kube-proxy --master=https://kubernetes-master --kubeconfig=/var/lib/kube-proxy/kubeconfig --v=2

Далі переконайтесь, що він не має явних проблем, таких як неможливість звʼязатися з майстром. Для цього вам доведеться переглянути логи. Доступ до логів залежить від вашої ОС Вузла. На деяких ОС це файл, наприклад /var/log/kube-proxy.log, тоді як на інших ОС використовується journalctl для доступу до логів. Ви повинні побачити щось на зразок:

I1027 22:14:53.995134    5063 server.go:200] Running in resource-only container "/kube-proxy"
I1027 22:14:53.998163    5063 server.go:247] Using iptables Proxier.
I1027 22:14:54.038140    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns-tcp" to [10.244.1.3:53]
I1027 22:14:54.038164    5063 proxier.go:352] Setting endpoints for "kube-system/kube-dns:dns" to [10.244.1.3:53]
I1027 22:14:54.038209    5063 proxier.go:352] Setting endpoints for "default/kubernetes:https" to [10.240.0.2:443]
I1027 22:14:54.038238    5063 proxier.go:429] Not syncing iptables until Services and Endpoints have been received from master
I1027 22:14:54.040048    5063 proxier.go:294] Adding new service "default/kubernetes:https" at 10.0.0.1:443/TCP
I1027 22:14:54.040154    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns" at 10.0.0.10:53/UDP
I1027 22:14:54.040223    5063 proxier.go:294] Adding new service "kube-system/kube-dns:dns-tcp" at 10.0.0.10:53/TCP

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

Одна з можливих причин невдачі коректної роботи kube-proxy — неможливість знайти необхідний бінарний файл conntrack. Це може статися на деяких Linux-системах, залежно від того, як ви встановлюєте кластер, наприклад, якщо ви встановлюєте Kubernetes з нуля. У цьому випадку вам потрібно вручну встановити пакунок conntrack (наприклад, sudo apt install conntrack на Ubuntu), а потім спробувати ще раз.

Kube-proxy може працювати в одному з декількох режимів. У вищенаведеному журналі рядок Using iptables Proxier вказує на те, що kube-proxy працює в режимі "iptables". Ще один поширений режим — "ipvs".

Режим iptables

У режимі "iptables" ви повинні побачити щось на зразок наступного на Вузлі:

iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR

Для кожного порту кожного Service повинно бути 1 правило у KUBE-SERVICES і один ланцюжок KUBE-SVC-<хеш>. Для кожного endpoint Podʼа повинна бути невелика кількість правил у цьому KUBE-SVC-<хеш> і один ланцюжок KUBE-SEP-<хеш> з невеликою кількістю правил. Точні правила будуть варіюватися залежно від вашої конфігурації (включаючи порти вузлів та балансувальники навантаження).

Режим IPVS

У режимі "ipvs" ви повинні побачити щось на зразок наступного на Вузлі:

ipvsadm -ln
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
...
TCP  10.0.1.175:80 rr
  -> 10.244.0.5:9376               Masq    1      0          0
  -> 10.244.0.6:9376               Masq    1      0          0
  -> 10.244.0.7:9376               Masq    1      0          0
...

Для кожного порту кожного Service, плюс будь-які NodePorts, зовнішні IP-адреси та IP-адреси балансувальника навантаження, kube-proxy створить віртуальний сервер. Для кожного endpoint Podʼа він створить відповідні реальні сервери. У цьому прикладі служба hostnames(10.0.1.175:80) має 3 endpoint (10.244.0.5:9376, 10.244.0.6:9376, 10.244.0.7:9376).

Чи kube-proxy керує трафіком?

Якщо ви бачите один із вищезазначених випадків, спробуйте ще раз отримати доступ до вашого Service за допомогою IP з одного з ваших Вузлів:

curl 10.0.1.175:80
hostnames-632524106-bbpiw

Якщо це все ще не вдається, перегляньте логи kube-proxy на наявність конкретних рядків, подібних до:

Setting endpoints for default/hostnames:default to [10.244.0.5:9376 10.244.0.6:9376 10.244.0.7:9376]

Якщо ви не бачите цього, спробуйте перезапустити kube-proxy з прапорцем -v, встановленим на 4, і потім знову перегляньте логи.

Крайній випадок: Pod не може звернутися до себе за допомогою IP-адреси Service

Це може здатися малоймовірним, але це може трапитися і має працювати.

Це може статися, коли мережа не належним чином налаштована для "зачісування" трафіку, зазвичай коли kube-proxy працює в режимі iptables, а Podʼи підключені за допомогою мережі bridge. Kubelet надає прапорець hairpin-mode, який дозволяє точкам доступу Service балансувати навантаження поверненням до себе, якщо вони намагаються отримати доступ до своєї власної VIP-адреси Service. Прапорець hairpin-mode має бути встановлено на значення hairpin-veth або promiscuous-bridge.

Загальні кроки для розвʼязання цього випадку мають бути такими:

  • Підтвердіть, що hairpin-mode встановлено у значення hairpin-veth або promiscuous-bridge. Можливий вигляд нижченаведеного. У наступному прикладі hairpin-mode встановлено на promiscuous-bridge.
ps auxw | grep kubelet
root      3392  1.1  0.8 186804 65208 ?        Sl   00:51  11:11 /usr/local/bin/kubelet --enable-debugging-handlers=true --config=/etc/kubernetes/manifests --allow-privileged=True --v=4 --cluster-dns=10.0.0.10 --cluster-domain=cluster.local --configure-cbr0=true --cgroup-root=/ --system-cgroups=/system --hairpin-mode=promiscuous-bridge --runtime-cgroups=/docker-daemon --kubelet-cgroups=/kubelet --babysit-daemons=true --max-pods=110 --serialize-image-pulls=false --outofdisk-transition-frequency=0
  • Підтвердіть поточний hairpin-mode. Для цього вам потрібно переглянути логи kubelet. Доступ до логів залежить від вашої операційної системи. На деяких ОС це файл, наприклад /var/log/kubelet.log, тоді як на інших ОС використовується journalctl для доступу до логів. Зверніть увагу, що поточний режим hairpin може не відповідати прапорцю --hairpin-mode через сумісність. Перевірте, чи є будь-які рядки в лозі з ключовим словом hairpin в kubelet.log. Повинні бути рядки в лозі, які показують поточний режим hairpin, схожі на наступне:
I0629 00:51:43.648698    3252 kubelet.go:380] Hairpin mode set to "promiscuous-bridge"
  • Якщо поточний режим hairpin — hairpin-veth, переконайтеся, що Kubelet має дозвіл на роботу в /sys на вузлі. Якщо все працює належним чином, ви побачите щось на зразок:
for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1
  • Якщо поточний режим hairpin — promiscuous-bridge, переконайтеся, що Kubelet має дозвіл на маніпулювання bridge в Linux на вузлі. Якщо bridge cbr0 використовується і налаштований належним чином, ви побачите:
ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1460  Metric:1
  • Зверніться за допомогою, якщо жоден з вищезазначених методів не працює.

Пошук допомоги

Якщо ви дійшли до цього моменту, відбувається щось дуже дивне. Ваш Service працює, має точки доступу, і ваші Podʼи насправді обслуговують запити. DNS працює, і kube-proxy схоже не діє неправильно. Проте ваш Service не працює. Будь ласка, дайте нам знати, що відбувається, щоб ми могли допомогти вам розслідувати цю проблему!

Звертайтеся до нас у Slack або на Форум чи у GitHub.

Що далі

Відвідайте документ про загальні відомості про усунення несправностей для отримання додаткової інформації.

4.1.3 - Налагодження StatefulSet

Ця задача показує, як усувати несправності у StatefulSet.

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

  • Вам потрібен кластер Kubernetes, та інструмент командного рядка kubectl повинен бути налаштований на звʼязок з вашим кластером.
  • Ви повинні мати запущений StatefulSet, який ви хочете дослідити.

Усунення несправностей у StatefulSet

Для того, щоб переглянути всі Podʼи, які належать до StatefulSet і мають мітку app.kubernetes.io/name=MyApp на них, ви можете використовувати наступне:

kubectl get pods -l app.kubernetes.io/name=MyApp

Якщо ви помітили, що будь-які Podʼи вказані у стані Unknown або Terminating протягом тривалого періоду часу, зверніться до завдання Видалення Podʼів StatefulSet за інструкціями щодо дії з ними. Ви можете усувати несправності окремих Podʼів у StatefulSet, використовуючи Посібник з усунення несправностей Podʼів.

Що далі

Дізнайтеся більше про усунення несправностей контейнера ініціалізації.

4.1.4 - Визначення причини збою Podʼа

Ця сторінка показує, як записувати та читати повідомлення про припинення роботи контейнера.

Повідомлення про припинення роботи надають можливість контейнерам записувати інформацію про фатальні події у місце, звідки її можна легко витягти та показувати за допомогою інструментів, таких як інформаційні панелі та програмне забезпечення моніторингу. У більшості випадків інформацію, яку ви вводите у повідомлення про припинення роботи, також слід записати в логи Kubernetes.

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

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

Запис та читання повідомлення про припинення роботи

У цьому завданні ви створюєте Pod, який запускає один контейнер. У маніфесті для цього Podʼа вказано команду, яка виконується при запуску контейнера:

apiVersion: v1
kind: Pod
metadata:
  name: termination-demo
spec:
  containers:
  - name: termination-demo-container
    image: debian
    command: ["/bin/sh"]
    args: ["-c", "sleep 10 && echo Sleep expired > /dev/termination-log"]
  1. Створіть Pod на основі конфігураційного файлу YAML:

    kubectl apply -f https://k8s.io/examples/debug/termination.yaml
    

    У файлі YAML у полях command та args ви можете побачити, що контейнер перебуває в стані очікування (спить) протягом 10 секунд, а потім записує "Sleep expired" у файл /dev/termination-log. Після того, як контейнер записує повідомлення "Sleep expired", він завершує роботу.

  2. Покажіть інформацію про Pod:

    kubectl get pod termination-demo
    

    Повторіть попередню команду, доки Pod більше не буде запущений.

  3. Покажіть детальну інформацію про Pod:

    kubectl get pod termination-demo --output=yaml
    

    Вивід містить повідомлення "Sleep expired":

    apiVersion: v1
    kind: Pod
    ...
        lastState:
          terminated:
            containerID: ...
            exitCode: 0
            finishedAt: ...
            message: |
              Sleep expired          
            ...
    
  4. Використовуйте шаблон Go для фільтрування виводу так, щоб він містив лише повідомлення про припинення роботи контейнера:

    kubectl get pod termination-demo -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
    

Якщо у вас працює багатоконтейнерний Pod, ви можете використовувати шаблон Go, щоб включити імʼя контейнера. Таким чином, ви можете визначити, який з контейнерів несправний:

kubectl get pod multi-container-pod -o go-template='{{range .status.containerStatuses}}{{printf "%s:\n%s\n\n" .name .lastState.terminated.message}}{{end}}'

Налаштування повідомлення про припинення роботи

Kubernetes отримує повідомлення про припинення роботи з файлу повідомлення, вказаного в полі terminationMessagePath контейнера, яке має стандартне значення /dev/termination-log. Налаштувавши це поле, ви можете сказати Kubernetes використовувати інший файл. Kubernetes використовує вміст зазначеного файлу для заповнення повідомлення про стан контейнера як у випадку успіху, так і невдачі.

Повідомлення про припинення має бути коротким остаточним статусом, таким як повідомлення про помилку твердження. Kubelet обрізає повідомлення, які довше 4096 байтів.

Загальна довжина повідомлення по всіх контейнерах обмежена 12KiB і рівномірно розподілена між всіма контейнерами. Наприклад, якщо є 12 контейнерів (initContainers або containers), кожен має 1024 байти доступного простору для повідомлень про припинення роботи.

Стандартний шлях для повідомлення про припинення роботи — /dev/termination-log. Ви не можете встановити шлях повідомлення про припинення роботи після запуску Podʼа.

У наступному прикладі контейнер записує повідомлення про завершення в /tmp/my-log для отримання Kubernetes:

apiVersion: v1
kind: Pod
metadata:
  name: msg-path-demo
spec:
  containers:
  - name: msg-path-demo-container
    image: debian
    terminationMessagePath: "/tmp/my-log"

Крім того, користувачі можуть налаштувати поле terminationMessagePolicy контейнера для подальшої настройки. Типово це поле встановлене на "File", що означає, що повідомлення про припинення роботи отримуються лише з файлу повідомлення про припинення роботи. Встановивши terminationMessagePolicy на "FallbackToLogsOnError", ви можете вказати Kubernetes використовувати останній фрагмент виводу контейнера, якщо файл повідомлення про припинення роботи порожній, і контейнер завершився з помилкою. Вивід логу обмежується 2048 байтами або 80 рядками, якщо вибірка менша.

Що далі

4.1.5 - Налагодження контейнерів ініціалізації

Ця сторінка показує, як розвʼязувати проблеми, повʼязані з запуском контейнерів ініціалізації. Приклади команд нижче вказують на Pod як <pod-name> та на контейнери ініціалізації як <init-container-1> та <init-container-2>.

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

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

Для перевірки версії введіть kubectl version.

Перевірка стану контейнерів ініціалізації

Показ статусу вашого Podʼа:

kubectl get pod <pod-name>

Наприклад, статус Init:1/2 вказує на те, що один з двох контейнерів ініціалізації успішно завершено:

NAME         READY     STATUS     RESTARTS   AGE
<pod-name>   0/1       Init:1/2   0          7s

Дивіться Розуміння статусів Podʼа для отримання прикладів значень статусу та їх значень.

Отримання деталей про контейнери ініціалізації

Показ більш детальної інформації про виконання контейнерів ініціалізації:

kubectl describe pod <pod-name>

Наприклад, Pod з двома контейнерами ініціалізації може показати наступне:

Init Containers:
  <init-container-1>:
    Container ID:    ...
    ...
    State:           Terminated
      Reason:        Completed
      Exit Code:     0
      Started:       ...
      Finished:      ...
    Ready:           True
    Restart Count:   0
    ...
  <init-container-2>:
    Container ID:    ...
    ...
    State:           Waiting
      Reason:        CrashLoopBackOff
    Last State:      Terminated
      Reason:        Error
      Exit Code:     1
      Started:       ...
      Finished:      ...
    Ready:           False
    Restart Count:   3
    ...

Ви також можете отримувати доступ до статусів контейнерів ініціалізації програмно, читаючи поле status.initContainerStatuses у Pod Spec:

kubectl get pod nginx --template '{{.status.initContainerStatuses}}'

Ця команда поверне ту саму інформацію, що і вище у форматі JSON.

Отримання логів з контейнерів ініціалізації

Вкажіть імʼя контейнера ініціалізації разом з імʼям Podʼа, щоб отримати його логи.

kubectl logs <pod-name> -c <init-container-2>

Контейнери ініціалізації, що виконують скрипт оболонки, друкують команди в міру їх виконання. Наприклад, це можна зробити в Bash, запустивши set -x на початку скрипта.

Розуміння статусів Podʼа

Статус Podʼа, що починається з Init:, узагальнює стан виконання контейнерів ініціалізації. У таблиці нижче наведено деякі приклади значень статусу, які ви можете бачити під час налагодження контейнерів ініціалізації.

СтатусЗначення
Init:N/MPod має M контейнерів ініціалізації, і N вже завершено.
Init:ErrorКонтейнер ініціалізації не вдалося виконати.
Init:CrashLoopBackOffКонтейнер ініціалізації неперервно виходить з ладу.
PendingPod ще не розпочав виконувати контейнер ініціалізації.
PodInitializing або RunningPod вже завершив виконання контейнерів ініціалізації.

4.1.6 - Налагодження запущених Podʼів

Ця сторінка пояснює, як налагоджувати Podʼи, що запущені (або зазнають збою) на вузлі.

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

  • Ваш Pod вже повинен бути запланований та запущений. Якщо ваш Pod ще не запущений, почніть з Налагодження Podʼів.
  • Для деяких з розширених кроків налагодження вам потрібно знати, на якому вузлі запущений Pod, і мати доступ до оболонки для виконання команд на цьому вузлі. Вам не потрібен такий доступ для виконання стандартних кроків налагодження, що використовують kubectl.

Використання kubectl describe pod для отримання деталей про Podʼи

Для цього прикладу ми використовуватимемо Deployment для створення двох Podʼів, схожих на попередній приклад.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80

Створіть Deployment, запустивши наступну команду:

kubectl apply -f https://k8s.io/examples/application/nginx-with-request.yaml
deployment.apps/nginx-deployment created

Перевірте статус Podʼа за допомогою наступної команди:

kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-67d4bdd6f5-cx2nz   1/1     Running   0          13s
nginx-deployment-67d4bdd6f5-w6kd7   1/1     Running   0          13s

Ми можемо отримати більше інформації про кожен з цих Podʼів, використовуючи kubectl describe pod. Наприклад:

kubectl describe pod nginx-deployment-67d4bdd6f5-w6kd7
Name:         nginx-deployment-67d4bdd6f5-w6kd7
Namespace:    default
Priority:     0
Node:         kube-worker-1/192.168.0.113
Start Time:   Thu, 17 Feb 2022 16:51:01 -0500
Labels:       app=nginx
              pod-template-hash=67d4bdd6f5
Annotations:  <none>
Status:       Running
IP:           10.88.0.3
IPs:
  IP:           10.88.0.3
  IP:           2001:db8::1
Controlled By:  ReplicaSet/nginx-deployment-67d4bdd6f5
Containers:
  nginx:
    Container ID:   containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
    Image:          nginx
    Image ID:       docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Thu, 17 Feb 2022 16:51:05 -0500
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     500m
      memory:  128Mi
    Requests:
      cpu:        500m
      memory:     128Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-bgsgp (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-bgsgp:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Guaranteed
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  34s   default-scheduler  Successfully assigned default/nginx-deployment-67d4bdd6f5-w6kd7 to kube-worker-1
  Normal  Pulling    31s   kubelet            Pulling image "nginx"
  Normal  Pulled     30s   kubelet            Successfully pulled image "nginx" in 1.146417389s
  Normal  Created    30s   kubelet            Created container nginx
  Normal  Started    30s   kubelet            Started container nginx

Тут ви можете побачити інформацію про конфігурацію контейнерів та Podʼа (мітки, вимоги до ресурсів і т. д.), а також інформацію про статус контейнерів та Podʼа (стан, готовність, кількість перезапусків, події й т.д.).

Стан контейнера може бути Waiting (очікування), Running (виконання) або Terminated (завершено). Залежно від стану, буде надано додаткову інформацію — наприклад, ви можете побачити, коли контейнер був запущений, якщо він перебуває в стані Running.

Ready показує, чи пройшов контейнер останню пробу готовності. (У цьому випадку контейнер не має налаштованої проби готовності; вважається, що контейнер готовий, якщо проба готовності не налаштована.)

Restart Count показує, скільки разів контейнер був перезапущений; ця інформація може бути корисною для виявлення циклів аварійного перезапуску в контейнерах, які налаштовані на перезапуск завжди ('always').

Наразі єдина умова, повʼязана з Podʼом, — це бінарна умова Ready, яка вказує, що Pod може обслуговувати запити та повинен бути доданий до пулів балансування навантаження всіх відповідних Serviceʼів.

Нарешті, ви бачите логи недавніх подій, що стосуються вашого Podʼа. "From" вказує на компонент, який реєструє подію. "Reason" та "Message" розповідають вам, що сталося.

Приклад: налагодження Podʼів у стані Pending

Одна з поширених ситуацій, яку ви можете виявити за допомогою подій, — це коли ви створили Pod, який не може бути розміщений на жодному вузлі. Наприклад, Pod може вимагати більше ресурсів, ніж вільно на будь-якому вузлі, або він може вказати селектор міток, який не відповідає жодному вузлу. Скажімо, ми створили Deployment з 5 репліками (замість 2) і вимагаємо 600 міліядер замість 500, на чотирьох вузловому кластері, де кожна (віртуальна) машина має 1 ЦП. У цьому випадку один з Podʼів не зможе бути запланованим. (Зауважте, що через надбудови кластера, такі як fluentd, skydns тощо, які працюють на кожному вузлі, якби ми запросили 1000 міліядер, то жоден з Podʼів не міг би бути запланованим.)

kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1006230814-6winp   1/1       Running   0          7m
nginx-deployment-1006230814-fmgu3   1/1       Running   0          7m
nginx-deployment-1370807587-6ekbw   1/1       Running   0          1m
nginx-deployment-1370807587-fg172   0/1       Pending   0          1m
nginx-deployment-1370807587-fz9sd   0/1       Pending   0          1m

Щоб дізнатися, чому Pod nginx-deployment-1370807587-fz9sd не працює, ми можемо використовувати kubectl describe pod у Podʼі у стані Pending та переглянути його події:

kubectl describe pod nginx-deployment-1370807587-fz9sd
  Name:		nginx-deployment-1370807587-fz9sd
  Namespace:	default
  Node:		/
  Labels:		app=nginx,pod-template-hash=1370807587
  Status:		Pending
  IP:
  Controllers:	ReplicaSet/nginx-deployment-1370807587
  Containers:
    nginx:
      Image:	nginx
      Port:	80/TCP
      QoS Tier:
        memory:	Guaranteed
        cpu:	Guaranteed
      Limits:
        cpu:	1
        memory:	128Mi
      Requests:
        cpu:	1
        memory:	128Mi
      Environment Variables:
  Volumes:
    default-token-4bcbi:
      Type:	Secret (a volume populated by a Secret)
      SecretName:	default-token-4bcbi
  Events:
    FirstSeen	LastSeen	Count	From			        SubobjectPath	Type		Reason			    Message
    ---------	--------	-----	----			        -------------	--------	------			    -------
    1m		    48s		    7	    {default-scheduler }			        Warning		FailedScheduling	pod (nginx-deployment-1370807587-fz9sd) failed to fit in any node
  fit failure on node (kubernetes-node-6ta5): Node didn't have enough resource: CPU, requested: 1000, used: 1420, capacity: 2000
  fit failure on node (kubernetes-node-wul5): Node didn't have enough resource: CPU, requested: 1000, used: 1100, capacity: 2000

Тут ви можете побачити подію, створену планувальником, яка говорить, що Pod не вдалося запланувати з причиною FailedScheduling (і, можливо, інші). Повідомлення говорить нам, що на будь-якому з вузлів не було достатньо ресурсів для Podʼа.

Для виправлення цієї ситуації ви можете використовувати kubectl scale, щоб оновити ваш Deployment та вказати чотири або менше реплік. (Або ви можете залишити один Pod у стані Pending, що нешкідливо.)

Події, такі як ті, які ви бачили в кінці kubectl describe pod, зберігаються в etcd та надають високорівневу інформацію про те, що відбувається в кластері. Щоб переглянути всі події, ви можете використовувати

kubectl get events

але вам потрібно памʼятати, що події належать до простору імен. Це означає, що якщо вас цікавлять події для обʼєкта з простором імен (наприклад, що сталося з Podʼами в просторі імен my-namespace), вам потрібно явно вказати простір імен для команди:

kubectl get events --namespace=my-namespace

Щоб побачити події з усіх просторів імен, ви можете використовувати аргумент --all-namespaces.

Крім команди kubectl describe pod, інший спосіб отримати додаткову інформацію про Pod (поза тим, що надає kubectl get pod) — це передати прапорець формату виводу -o yaml команді kubectl get pod. Це надасть вам, у форматі YAML, ще більше інформації, ніж kubectl describe pod — фактично всю інформацію, яку система має про Pod. Тут ви побачите такі речі, як анотації (це метадані ключ-значення без обмежень міток, які використовуються внутрішньо компонентами системи Kubernetes), політику перезапуску, порти та томи.

kubectl get pod nginx-deployment-1006230814-6winp -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2022-02-17T21:51:01Z"
  generateName: nginx-deployment-67d4bdd6f5-
  labels:
    app: nginx
    pod-template-hash: 67d4bdd6f5
  name: nginx-deployment-67d4bdd6f5-w6kd7
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: nginx-deployment-67d4bdd6f5
    uid: 7d41dfd4-84c0-4be4-88ab-cedbe626ad82
  resourceVersion: "1364"
  uid: a6501da1-0447-4262-98eb-c03d4002222e
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: nginx
    ports:
    - containerPort: 80
      protocol: TCP
    resources:
      limits:
        cpu: 500m
        memory: 128Mi
      requests:
        cpu: 500m
        memory: 128Mi
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-bgsgp
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: kube-worker-1
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kube-api-access-bgsgp
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:01Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:06Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:06Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2022-02-17T21:51:01Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: containerd://5403af59a2b46ee5a23fb0ae4b1e077f7ca5c5fb7af16e1ab21c00e0e616462a
    image: docker.io/library/nginx:latest
    imageID: docker.io/library/nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
    lastState: {}
    name: nginx
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2022-02-17T21:51:05Z"
  hostIP: 192.168.0.113
  phase: Running
  podIP: 10.88.0.3
  podIPs:
  - ip: 10.88.0.3
  - ip: 2001:db8::1
  qosClass: Guaranteed
  startTime: "2022-02-17T21:51:01Z"

Перегляд логі Podʼа

Спочатку перегляньте журнали ураженого контейнера:

kubectl logs ${POD_NAME} ${CONTAINER_NAME}

Якщо ваш контейнер раніше впав, ви можете отримати доступ до попереднього логу аварії контейнера за допомогою:

kubectl logs --previous ${POD_NAME} ${CONTAINER_NAME}

Налагодження за допомогою виконання команд у контейнері

Якщо образ контейнера містить утиліти для налагодження, як це трапляється в образах, побудованих на основі базових образів операційних систем Linux і Windows, ви можете виконати команди всередині конкретного контейнера за допомогою kubectl exec:

kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}

Наприклад, щоб переглянути логи з робочого Podʼа Cassandra, ви можете виконати

kubectl exec cassandra -- cat /var/log/cassandra/system.log

Ви можете запустити оболонку, яка підключена до вашого термінала, використовуючи аргументи -i і -t для kubectl exec, наприклад:

kubectl exec -it cassandra -- sh

Для отримання додаткових відомостей дивіться Отримання оболонки до запущеного контейнера.

Налагодження за допомогою ефемерного контейнера

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.25 [stable]

Ефемерні контейнери корисні для інтерактивного усунення несправностей, коли kubectl exec недостатній через аварію контейнера або те, що образ контейнера не містить утиліт для налагодження, наприклад, в образах distroless.

Приклад налагодження за допомогою ефемерних контейнерів

Ви можете використовувати команду kubectl debug, щоб додати ефемерні контейнери до запущеного Podʼа. Спочатку створіть Pod для прикладу:

kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never

У цьому розділі приклади використовують образ контейнера pause, оскільки він не містить утиліт для налагодження, але цей метод працює з будь-якими образом контейнера.

Якщо ви спробуєте використати kubectl exec для створення оболонки, ви побачите помилку, оскільки в цьому образі контейнера немає оболонки.

kubectl exec -it ephemeral-demo -- sh
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown

Замість цього ви можете додати контейнер для налагодження за допомогою kubectl debug. Якщо ви вказуєте аргумент -i/--interactive, kubectl автоматично приєднується до консолі ефемерного контейнера.

kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo
Defaulting debug container name to debugger-8xzrl.
If you don't see a command prompt, try pressing enter.
/ #

Ця команда додає новий контейнер busybox і приєднується до нього. Параметр --target спрямовує простір імен процесу до іншого контейнера. Це необхідно тут, оскільки kubectl run не ввімкнув процес спільного використання простору імен у Pod, який він створює.

Ви можете переглянути стан нового ефемерного контейнера, використовуючи kubectl describe:

kubectl describe pod ephemeral-demo
...
Ephemeral Containers:
  debugger-8xzrl:
    Container ID:   docker://b888f9adfd15bd5739fefaa39e1df4dd3c617b9902082b1cfdc29c4028ffb2eb
    Image:          busybox
    Image ID:       docker-pullable://busybox@sha256:1828edd60c5efd34b2bf5dd3282ec0cc04d47b2ff9caa0b6d4f07a21d1c08084
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 12 Feb 2020 14:25:42 +0100
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

Використовуйте kubectl delete, щоб видалити Pod, коли ви закінчите:

kubectl delete pod ephemeral-demo

Налагодження за допомогою копії Podʼа

Іноді параметри конфігурації Podʼа ускладнюють усунення несправностей у певних ситуаціях. Наприклад, ви не можете виконувати kubectl exec, щоб усунути несправності у вашому контейнері, якщо ваш образ контейнера не містить оболонки або якщо ваш застосунок аварійно завершується при запуску. У цих ситуаціях ви можете використовувати kubectl debug, щоб створити копію Podʼа зі зміненими значеннями конфігурації для полегшення налагодження.

Копіювання Podʼа з додаванням нового контейнера

Додавання нового контейнера може бути корисним, коли ваш застосунок працює, але не так, як ви очікували, і ви хочете додати додаткові засоби усунення несправностей до Podʼа.

Наприклад, можливо, образ контейнера вашого застосунку збудований на основі busybox, але вам потрібні засоби для налагодження, які не включені в busybox. Ви можете симулювати цей сценарій за допомогою kubectl run:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

Виконайте цю команду, щоб створити копію myapp з назвою myapp-debug, яка додає новий контейнер Ubuntu для налагодження:

kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug
Defaulting debug container name to debugger-w7xmf.
If you don't see a command prompt, try pressing enter.
root@myapp-debug:/#

Не забудьте прибрати Pod для налагодження, коли ви закінчите:

kubectl delete pod myapp myapp-debug

Копіювання Podʼа зі зміною його команди

Іноді корисно змінити команду для контейнера, наприклад, щоб додати прапорець для налагодження або через те, що застосунок аварійно завершується.

Щоб симулювати аварійне завершення застосунку, використайте kubectl run, щоб створити контейнер, який негайно завершується:

kubectl run --image=busybox:1.28 myapp -- false

Ви можете побачити за допомогою kubectl describe pod myapp, що цей контейнер аварійно завершується:

Containers:
  myapp:
    Image:         busybox
    ...
    Args:
      false
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1

Ви можете використовувати kubectl debug, щоб створити копію цього Podʼа з командою зміненою на інтерактивну оболонку:

kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh
If you don't see a command prompt, try pressing enter.
/ #

Тепер у вас є інтерактивна оболонка, яку ви можете використовувати для виконання завдань, таких як перевірка шляхів файлової системи або виконання команди контейнера вручну.

Не забудьте прибрати Pod для налагодження, коли ви закінчите:

kubectl delete pod myapp myapp-debug

Копіювання Podʼа з заміною образів контейнерів

Іноді вам може знадобитися змінити образ Podʼа, який поводитися неналежним чином, зі звичайного образу, що використовується в операційній діяльності, на образ, що містить збірку для налагодження або додаткові утиліти.

Наприклад, створіть Pod за допомогою kubectl run:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

Тепер використовуйте kubectl debug, щоб створити копію та змінити його образ контейнера на ubuntu:

kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

Синтаксис --set-image використовує ту ж синтаксис container_name=image, що й kubectl set image. *=ubuntu означає зміну образу всіх контейнерів на ubuntu.

Не забудьте прибрати Pod для налагодження, коли ви закінчите:

kubectl delete pod myapp myapp-debug

Налагодження через оболонку на вузлі

Якщо жоден з цих підходів не працює, ви можете знайти вузол, на якому працює Pod, і створити Pod, який буде виконуватися на цьому вузлі. Щоб створити інтерактивну оболонку на вузлі за допомогою kubectl debug, виконайте:

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@ek8s:/#

При створенні сесії налагодження на вузлі майте на увазі, що:

  • kubectl debug автоматично генерує назву нового Podʼа на основі назви вузла.
  • Коренева файлова система вузла буде змонтована в /host.
  • Контейнер працює у просторах імен IPC, мережі та PID вузла, хоча Pod не є привілейованим, тому читання деякої інформації про процеси може не вдатися, і chroot /host може не спрацювати.
  • Якщо вам потрібен привілейований Pod, створіть його вручну або використовуйте прапорець --profile=sysadmin

Не забудьте прибрати Pod для налагодження, коли ви закінчите з ним:

kubectl delete pod node-debugger-mynode-pdx84

Профілі налагодження

Коли ви використовуєте kubectl debug для налагодження вузла за допомогою Podʼа налагодження, Pod за ефемерним контейнером або скопійованого Pod, ви можете застосувати до них профіль налагодження за допомогою прапорця --profile. Застосовуючи профіль, встановлюються конкретні властивості, такі як securityContext, що дозволяє адаптуватися до різних сценаріїв.

Доступні наступні профілі:

ПрофільОпис
legacyНабір властивостей для зворотної сумісності з поведінкою 1.22
generalРозумний набір загальних властивостей для кожного завдання налагодження
baselineНабір властивостей, сумісних з Політикою базової безпеки PodSecurityStandard
restrictedНабір властивостей, сумісних з Політикою обмеженої безпеки PodSecurityStandard
netadminНабір властивостей, включаючи привілеї адміністратора мережі
sysadminНабір властивостей, включаючи привілеї системного адміністратора (root)

Припустимо, що ви створюєте Pod і налагоджуєте його. Спочатку створіть Pod з назвою myapp, наприклад:

kubectl run myapp --image=busybox:1.28 --restart=Never -- sleep 1d

Потім, перевірте Pod за допомогою ефемерного контейнера. Якщо ефемерному контейнеру потрібно мати привілеї, ви можете використовувати профіль sysadmin:

kubectl debug -it myapp --image=busybox:1.28 --target=myapp --profile=sysadmin
Targeting container "myapp". If you don't see processes from this container it may be because the container runtime doesn't support this feature.
Defaulting debug container name to debugger-6kg4x.
If you don't see a command prompt, try pressing enter.
/ #

Перевірте можливості процесу ефемерного контейнера, виконавши наступну команду всередині контейнера:

/ # grep Cap /proc/$$/status
...
CapPrm:	000001ffffffffff
CapEff:	000001ffffffffff
...

Це означає, що процес контейнера наділений усіма можливостями як привілейований контейнер завдяки застосуванню профілю sysadmin. Дивіться більше деталей про можливості.

Ви також можете перевірити, що ефемерний контейнер був створений як привілейований контейнер:

kubectl get pod myapp -o jsonpath='{.spec.ephemeralContainers[0].securityContext}'
{"privileged":true}

Очистіть Pod, коли ви закінчите з ним:

kubectl delete pod myapp

4.1.7 - Отримання доступу до оболонки запущеного контейнера

Ця сторінка показує, як використовувати kubectl exec для отримання доступу до оболонки запущеного контейнера.

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

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

Отримання доступу до оболонки контейнера

У цьому завданні ви створите Pod, який має один контейнер. Контейнер виконує образ nginx. Ось файл конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  hostNetwork: true
  dnsPolicy: Default

Створіть Pod:

kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml

Перевірте, що контейнер працює:

kubectl get pod shell-demo

Отримайте доступ до оболонки запущеного контейнера:

kubectl exec --stdin --tty shell-demo -- /bin/bash

У своїй оболонці виведіть список кореневої теки:

# Виконайте це всередині контейнера
ls /

У своїй оболонці експериментуйте з іншими командами. Ось деякі приклади:

# Ви можете виконати ці приклади команд всередині контейнера
ls /
cat /proc/mounts
cat /proc/1/maps
apt-get update
apt-get install -y tcpdump
tcpdump
apt-get install -y lsof
lsof
apt-get install -y procps
ps aux
ps aux | grep nginx

Редагування головної сторінки nginx

Знову перегляньте файл конфігурації вашого Podʼа. Pod має том emptyDir, і контейнер монтує цей том в /usr/share/nginx/html.

У своїй оболонці створіть файл index.html у теці /usr/share/nginx/html:

# Виконайте це всередині контейнера
echo 'Hello shell demo' > /usr/share/nginx/html/index.html

У своїй оболонці надішліть GET-запит до сервера nginx:

# Виконайте це в оболонці всередині вашого контейнера
apt-get update
apt-get install curl
curl http://localhost/

Результат покаже текст, який ви написали в файл index.html:

Hello shell demo

Коли ви закінчите з вашою оболонкою, введіть exit.

exit # Щоб вийти з оболонки в контейнері

Виконання окремих команд в контейнері

У звичайному вікні команд виведіть змінні оточення в запущеному контейнері:

kubectl exec shell-demo -- env

Експериментуйте з виконанням інших команд. Ось деякі приклади:

kubectl exec shell-demo -- ps aux
kubectl exec shell-demo -- ls /
kubectl exec shell-demo -- cat /proc/1/mounts

Відкриття оболонки, коли в Podʼі є більше одного контейнера

Якщо в Podʼі є більше одного контейнера, використовуйте --container або -c для зазначення контейнера в команді kubectl exec. Наприклад, припустимо, у вас є Pod на ім’я my-pod, і в Podʼі є два контейнери з іменами main-app та helper-app. Наступна команда відкриє оболонку до контейнера main-app.

kubectl exec -i -t my-pod --container main-app -- /bin/bash

Що далі

4.2 - Налагодження кластера

Виправлення загальних проблем з кластером Kubernetes.

Цей документ присвячений усуненню несправностей в кластері; ми передбачаємо, що ви вже виключили свій застосунок з переліку причин проблеми, з якою ви стикаєтеся. Дивіться посібник Налагодження застосунку для порад з перевірки застосунків. Ви також можете звернутися до загального документа з усунення несправностей для отримання додаткової інформації.

Щодо усунення несправностей інструменту kubectl, звертайтеся до Посібника з усунення несправностей kubectl.

Виведення інформації про кластер

Перша річ, яку потрібно дослідити у кластері — це переконатися, що всі ваші вузли зареєстровані правильно.

Виконайте наступну команду:

kubectl get nodes

Перевірте, що всі вузли, які ви очікуєте бачити, присутні, і що всі вони перебувають у стані Ready.

Щоб отримати детальну інформацію про загальний стан вашого кластера, ви можете виконати:

kubectl cluster-info dump

Приклад: налагодження вимкненого/недоступного вузла

Іноді при налагодженні може бути корисно переглянути стан вузла, наприклад, через те, що ви помітили дивну поведінку Podʼа, який працює на вузлі, або щоб дізнатися, чому Pod не може розміститися на вузлі. Так само як і з Podʼами, ви можете використовувати kubectl describe node та kubectl get node -o yaml, щоб отримати детальну інформацію про вузли. Наприклад, ось що ви побачите, якщо вузол вимкнено (відключено від мережі, або kubelet припинив роботу і не може перезапуститися і т. д.). Зверніть увагу на події, які показують, що вузол не готовий, і також зверніть увагу, що Podʼи більше не працюють (їх буде виселено після пʼяти хвилин стану NotReady).

kubectl get nodes
NAME                     STATUS       ROLES     AGE     VERSION
kube-worker-1            NotReady     <none>    1h      v1.23.3
kubernetes-node-bols     Ready        <none>    1h      v1.23.3
kubernetes-node-st6x     Ready        <none>    1h      v1.23.3
kubernetes-node-unaj     Ready        <none>    1h      v1.23.3
kubectl describe node kube-worker-1
Name:               kube-worker-1
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=kube-worker-1
                    kubernetes.io/os=linux
Annotations:        kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
                    node.alpha.kubernetes.io/ttl: 0
                    volumes.kubernetes.io/controller-managed-attach-detach: true
CreationTimestamp:  Thu, 17 Feb 2022 16:46:30 -0500
Taints:             node.kubernetes.io/unreachable:NoExecute
                    node.kubernetes.io/unreachable:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  kube-worker-1
  AcquireTime:     <unset>
  RenewTime:       Thu, 17 Feb 2022 17:13:09 -0500
Conditions:
  Type                 Status    LastHeartbeatTime                 LastTransitionTime                Reason              Message
  ----                 ------    -----------------                 ------------------                ------              -------
  NetworkUnavailable   False     Thu, 17 Feb 2022 17:09:13 -0500   Thu, 17 Feb 2022 17:09:13 -0500   WeaveIsUp           Weave pod has set this
  MemoryPressure       Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  DiskPressure         Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  PIDPressure          Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
  Ready                Unknown   Thu, 17 Feb 2022 17:12:40 -0500   Thu, 17 Feb 2022 17:13:52 -0500   NodeStatusUnknown   Kubelet stopped posting node status.
Addresses:
  InternalIP:  192.168.0.113
  Hostname:    kube-worker-1
Capacity:
  cpu:                2
  ephemeral-storage:  15372232Ki
  hugepages-2Mi:      0
  memory:             2025188Ki
  pods:               110
Allocatable:
  cpu:                2
  ephemeral-storage:  14167048988
  hugepages-2Mi:      0
  memory:             1922788Ki
  pods:               110
System Info:
  Machine ID:                 9384e2927f544209b5d7b67474bbf92b
  System UUID:                aa829ca9-73d7-064d-9019-df07404ad448
  Boot ID:                    5a295a03-aaca-4340-af20-1327fa5dab5c
  Kernel Version:             5.13.0-28-generic
  OS Image:                   Ubuntu 21.10
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.5.9
  Kubelet Version:            v1.23.3
  Kube-Proxy Version:         v1.23.3
Non-terminated Pods:          (4 in total)
  Namespace                   Name                                 CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                                 ------------  ----------  ---------------  -------------  ---
  default                     nginx-deployment-67d4bdd6f5-cx2nz    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  default                     nginx-deployment-67d4bdd6f5-w6kd7    500m (25%)    500m (25%)  128Mi (6%)       128Mi (6%)     23m
  kube-system                 kube-proxy-dnxbz                     0 (0%)        0 (0%)      0 (0%)           0 (0%)         28m
  kube-system                 weave-net-gjxxp                      100m (5%)     0 (0%)      200Mi (10%)      0 (0%)         28m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                1100m (55%)  1 (50%)
  memory             456Mi (24%)  256Mi (13%)
  ephemeral-storage  0 (0%)       0 (0%)
  hugepages-2Mi      0 (0%)       0 (0%)
Events:
...
kubectl get node kube-worker-1 -o yaml
apiVersion: v1
kind: Node
metadata:
  annotations:
    kubeadm.alpha.kubernetes.io/cri-socket: /run/containerd/containerd.sock
    node.alpha.kubernetes.io/ttl: "0"
    volumes.kubernetes.io/controller-managed-attach-detach: "true"
  creationTimestamp: "2022-02-17T21:46:30Z"
  labels:
    beta.kubernetes.io/arch: amd64
    beta.kubernetes.io/os: linux
    kubernetes.io/arch: amd64
    kubernetes.io/hostname: kube-worker-1
    kubernetes.io/os: linux
  name: kube-worker-1
  resourceVersion: "4026"
  uid: 98efe7cb-2978-4a0b-842a-1a7bf12c05f8
spec: {}
status:
  addresses:
  - address: 192.168.0.113
    type: InternalIP
  - address: kube-worker-1
    type: Hostname
  allocatable:
    cpu: "2"
    ephemeral-storage: "14167048988"
    hugepages-2Mi: "0"
    memory: 1922788Ki
    pods: "110"
  capacity:
    cpu: "2"
    ephemeral-storage: 15372232Ki
    hugepages-2Mi: "0"
    memory: 2025188Ki
    pods: "110"
  conditions:
  - lastHeartbeatTime: "2022-02-17T22:20:32Z"
    lastTransitionTime: "2022-02-17T22:20:32Z"
    message: Weave pod has set this
    reason: WeaveIsUp
    status: "False"
    type: NetworkUnavailable
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient memory available
    reason: KubeletHasSufficientMemory
    status: "False"
    type: MemoryPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has no disk pressure
    reason: KubeletHasNoDiskPressure
    status: "False"
    type: DiskPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:13:25Z"
    message: kubelet has sufficient PID available
    reason: KubeletHasSufficientPID
    status: "False"
    type: PIDPressure
  - lastHeartbeatTime: "2022-02-17T22:20:15Z"
    lastTransitionTime: "2022-02-17T22:15:15Z"
    message: kubelet is posting ready status. AppArmor enabled
    reason: KubeletReady
    status: "True"
    type: Ready
  daemonEndpoints:
    kubeletEndpoint:
      Port: 10250
  nodeInfo:
    architecture: amd64
    bootID: 22333234-7a6b-44d4-9ce1-67e31dc7e369
    containerRuntimeVersion: containerd://1.5.9
    kernelVersion: 5.13.0-28-generic
    kubeProxyVersion: v1.23.3
    kubeletVersion: v1.23.3
    machineID: 9384e2927f544209b5d7b67474bbf92b
    operatingSystem: linux
    osImage: Ubuntu 21.10
    systemUUID: aa829ca9-73d7-064d-9019-df07404ad448

Аналіз логів

Тепер для докладнішого вивчення кластера потрібно увійти на відповідні машини. Ось розташування відповідних файлів журналу. На системах, що використовують systemd, може знадобитися використання journalctl замість перегляду файлів журналу.

Вузли панелі управління

  • /var/log/kube-apiserver.log — Сервер API, відповідальний за обслуговування API
  • /var/log/kube-scheduler.log — Планувальник, відповідальний за прийняття рішень щодо планування
  • /var/log/kube-controller-manager.log — Компонент, який виконує більшість вбудованих контролерів Kubernetes, за винятком планування (за це відповідає планувальник kube-scheduler).

Робочі вузли

  • /var/log/kubelet.log — логи kubelet, відповідального за запуск контейнерів на вузлі
  • /var/log/kube-proxy.log — логи kube-proxy, відповідального за направлення трафіку на Service endpoints.

Режими відмови кластера

Це неповний перелік того, що може піти не так, та як виправити вашу конфігурацію кластера для помʼякшення проблем.

Причини відмов

  • Вимкнення віртуальних машин(и)
  • Розділ мережі в межах кластера чи між кластером та користувачами
  • Крах програмного забезпечення Kubernetes
  • Втрата даних або недоступність постійного сховища (наприклад, GCE PD або томів AWS EBS)
  • Помилка оператора, наприклад, неправильно налаштоване програмне забезпечення Kubernetes або застосунку

Конкретні сценарії

  • Вимкнення віртуальної машини або аварійне вимикання apiserver
    • Результати
      • не можна зупинити, оновити чи запустити нові Podʼи, Services, контролер реплікацій
      • наявні Podʼи та Services мають продовжувати нормальну роботу, якщо вони не залежать від API Kubernetes
  • Втрата даних, на яких ґрунтується API сервер
    • Результати
      • компонент kube-apiserver не може успішно стартувати та стати спроможним обслуговувати запити
      • kubelet не зможе досягти його, але продовжить виконувати ті самі Podʼи та забезпечувати той самий сервіс проксі
      • необхідне ручне відновлення або відновлення стану apiserver перед його перезапуском
  • Припинення роботи служб підтримки (контролер вузлів, менеджер контролера реплікацій, планувальник і т. д.) або їх крах
    • наразі вони розміщені разом з apiserver, і їхня недоступність має схожі наслідки, що й в apiserver
    • у майбутньому ці служби також будуть репліковані та не можуть бути розміщені в одному місці
    • вони не мають власного постійного стану
  • Вимкнення окремого вузла (віртуальна машина або фізична машина)
    • Результати
      • Podʼи на цьому вузлі перестають працювати
  • Розрив мережі
    • Результати
      • розділ A вважає, що вузли в розділі B вимкнені; розділ B вважає, що apiserver вимкнений. (Якщо майстер-вузол опиниться в розділі A.)
  • Збій програмного забезпечення kubelet
    • Результати
      • аварійно вимкнений kubelet не може стартувати нові Podʼи на вузлі
      • kubelet може видаляти Podʼи або ні
      • вузол позначений як неспроможний
      • контролери реплікацій стартують нові Podʼи в іншому місці
  • Помилка оператора кластера
    • Результати
      • втрата Podʼів, Services і т. ін.
      • втрата сховища даних для apiserver
      • користувачі не можуть читати API
      • і т.д.

Помʼякшення

  • Дія: Використовуйте функцію автоматичного перезапуску віртуальних машин IaaS для віртуальних машин IaaS

    • Помʼякшує: Вимкнення віртуальної машини або аварійне вимикання apiserver
    • Помʼякшує: Вимкнення служб підтримки або їх краху
  • Дія: Використовуйте надійне сховище IaaS (наприклад, GCE PD або том AWS EBS) для віртуальних машин з apiserver+etcd

    • Помʼякшує: Втрата даних, на яких ґрунтується API сервер
  • Дія: Використовуйте конфігурацію високої доступності

    • Помʼякшує: Вимкнення вузла керування або аварійне завершення роботи компонентів управління керуванням (планувальник, API сервер, менеджер контролера)
      • Витримає одне або кілька одночасних відмов вузлів або компонентів
    • Помʼякшує: Втрата сховища даних для API сервера (тобто каталог даних etcd)
      • Передбачає конфігурацію HA (highly-available) etcd
  • Дія: Регулярно створюйте знімки віртуальних машин або томів PD/EBS, які використовуються apiserver

    • Помʼякшує: Втрата сховища даних для API сервера
    • Помʼякшує: Деякі випадки помилок оператора
    • Помʼякшує: Деякі випадки несправності програмного забезпечення Kubernetes
  • Дія: Використовуйте контролер реплікацій та служби перед Podʼами

    • Помʼякшує: Вимкнення вузла
    • Помʼякшує: Збій програмного забезпечення kubelet
  • Дія: Застосунки (контейнери), призначені для того, щоб витримувати неочікувані перезапуски

    • Помʼякшує: Вимкнення вузла
    • Помʼякшує: Збій програмного забезпечення kubelet

Що далі

4.2.1 - Усунення несправностей kubectl

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

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

Перевірка налаштувань kubectl

Переконайтеся, що ви правильно встановили та налаштували kubectl на вашому локальному компʼютері. Перевірте версію kubectl, щоб впевнитися, що вона актуальна та сумісна з вашим кластером.

Перевірка версії kubectl:

kubectl version

Ви побачите подібний вивід:

Client Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.4",GitCommit:"fa3d7990104d7c1f16943a67f11b154b71f6a132", GitTreeState:"clean",BuildDate:"2023-07-19T12:20:54Z", GoVersion:"go1.20.6", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v5.0.1
Server Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.3",GitCommit:"25b4e43193bcda6c7328a6d147b1fb73a33f1598", GitTreeState:"clean",BuildDate:"2023-06-14T09:47:40Z", GoVersion:"go1.20.5", Compiler:"gc", Platform:"linux/amd64"}

Якщо замість Server Version ви бачите Unable to connect to the server: dial tcp <server-ip>:8443: i/o timeout, вам потрібно дослідити проблеми зʼєднання kubectl з вашим кластером.

Переконайтеся, що ви встановили kubectl, слідуючи офіційній документації з встановлення kubectl, і правильно налаштували змінну середовища $PATH.

Перевірка kubeconfig

kubectl вимагає файл kubeconfig для зʼєднання з Kubernetes кластером. Файл kubeconfig зазвичай знаходиться в теці ~/.kube/config. Переконайтеся, що у вас є валідний файл kubeconfig. Якщо у вас немає файлу kubeconfig, ви можете отримати його у вашого адміністратора Kubernetes, або ви можете скопіювати його з теки /etc/kubernetes/admin.conf вашої панелі управління Kubernetes. Якщо ви розгортали ваш Kubernetes кластер на хмарній платформі та втратили ваш файл kubeconfig, ви можете згенерувати його знову за допомогою інструментів вашого хмарного провайдера. Дивіться документацію хмарного провайдера щодо генерації файлу kubeconfig.

Перевірте, чи правильно налаштовано змінну середовища $KUBECONFIG. Ви можете встановити змінну середовища $KUBECONFIG або використовувати параметр --kubeconfig з kubectl, щоб вказати теку файлу kubeconfig.

Перевірка VPN зʼєднання

Якщо ви використовуєте Віртуальну Приватну Мережу (VPN) для доступу до вашого Kubernetes кластеру, переконайтеся, що ваше VPN зʼєднання активне і стабільне. Іноді, перебої у зʼєднанні VPN можуть призвести до проблем зі зʼєднанням з кластером. Підʼєднайтеся до VPN знову і спробуйте отримати доступ до кластера знову.

Автентифікація та авторизація

Якщо ви використовуєте автентифікацію на базі токенів і kubectl повертає помилку щодо автентифікаційного токена або адреси сервера автентифікації, перевірте, що токен автентифікації Kubernetes та адреса сервера автентифікації налаштовані правильно.

Якщо kubectl повертає помилку щодо авторизації, переконайтеся, що ви використовуєте дійсні дані користувача. Та маєте дозвіл на доступ до ресурсу, який ви запросили.

Перевірка контекстів

Kubernetes підтримує роботу з кількома кластерами та контекстами. Переконайтеся, що ви використовуєте правильний контекст для взаємодії з вашим кластером.

Перелік доступних контекстів:

kubectl config get-contexts

Перемикання на відповідний контекст:

kubectl config use-context <context-name>

API сервер та балансувальник навантаження

kube-apiserver є центральним компонентом кластера Kubernetes. Якщо сервер API або балансувальник навантаження, який працює перед вашими серверами API, не доступний або не реагує, ви не зможете взаємодіяти з кластером.

Перевірте, чи доступний хост сервера API, використовуючи команду ping. Перевірте мережеве зʼєднання кластера та файервол. Якщо ви використовуєте хмарного провайдера для розгортання кластера, перевірте стан проб справності вашого хмарного провайдера для сервера API кластера.

Перевірте стан балансувальника навантаження (якщо використовується), щоб переконатися, що він справний і передає трафік на сервер API.

Проблеми з TLS

  • Потрібні додаткові інструменти — base64 та openssl версії 3.0 або вище.

Сервер API Kubernetes типово обслуговує лише HTTPS запити. У цьому випадку можуть виникнути проблеми з TLS з різних причин, таких як закінчення строку дії сертифіката або дійсність ланцюга довіри.

Ви можете знайти TLS сертифікат у файлі kubeconfig, який знаходиться у теці ~/.kube/config. Атрибут certificate-authority містить сертифікат ЦА, а атрибут client-certificate містить клієнтський сертифікат.

Перевірте строк дії цих сертифікатів:

kubectl config view --flatten --output 'jsonpath={.clusters[0].cluster.certificate-authority-data}' | base64 -d | openssl x509 -noout -dates

вивід:

notBefore=Feb 13 05:57:47 2024 GMT
notAfter=Feb 10 06:02:47 2034 GMT
kubectl config view --flatten --output 'jsonpath={.users[0].user.client-certificate-data}'| base64 -d | openssl x509 -noout -dates

вивід:

notBefore=Feb 13 05:57:47 2024 GMT
notAfter=Feb 12 06:02:50 2025 GMT

Перевірка допоміжних інструментів kubectl

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

Перевірте конфігурацію kubectl для отримання інформації про автентифікацію:

kubectl config view

Якщо раніше ви використовували допоміжний інструмент (наприклад, kubectl-oidc-login), переконайтеся, що він все ще встановлений і правильно налаштований.

4.2.2 - Конвеєер метрик ресурсів

Для Kubernetes Metrics API пропонує базовий набір метрик для підтримки автоматичного масштабування та подібних випадків використання. Це API робить доступною інформацію про використання ресурсів для вузла та Podʼа, включаючи метрики для CPU та памʼяті. Якщо ви розгортаєте Metrics API у своєму кластері, клієнти API Kubernetes можуть запитувати цю інформацію, і ви можете використовувати механізми контролю доступу Kubernetes для управління дозволами на це.

HorizontalPodAutoscaler (HPA) та VerticalPodAutoscaler (VPA) використовують дані з API метрик для налаштування реплік робочого навантаження та ресурсів для задоволення вимог користувачів.

Ви також можете переглядати метрики ресурсів, використовуючи команду kubectl top.

Схема 1 ілюструє архітектуру конвеєра метрик ресурсів.

flowchart RL subgraph cluster[Кластер] direction RL S[

] A[Сервер-
метрик] subgraph B[Вузли] direction TB D[cAdvisor] --> C[kubelet] E[Середовище
виконання
контейнерів] --> D E1[Середовище
виконання
контейнерів] --> D P[Дані Podʼа] -.- C end L[API-сервер] W[HPA] C ---->|метрики ресурсів
на рівні вузла| A -->|Metrics
API| L --> W end L ---> K[kubectl
top] classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000; class W,B,P,K,cluster,D,E,E1 box classDef spacewhite fill:#ffffff,stroke:#fff,stroke-width:0px,color:#000 class S spacewhite classDef k8s fill:#326ce5,stroke:#fff,stroke-width:1px,color:#fff; class A,L,C k8s

Схема 1. Конвеєр метрик ресурсів

Компоненти архітектури, справа наліво на схемі, включають наступне:

  • cAdvisor: Демон для збору, агрегування та викладання метрик контейнера, включених в Kubelet.

  • kubelet: Агент вузла для управління ресурсами контейнера. Метрики ресурсів доступні за допомогою точок доступу API kubelet /metrics/resource та /stats.

  • метрики ресурсів на рівні вузла: API, наданий kubelet для виявлення та отримання підсумкових статистичних даних на рівні вузла, доступних через точку доступу /metrics/resource.

  • сервер метрик: Компонент надбудови кластера, який збирає та агрегує метрики ресурсів, витягнуті з кожного kubelet. Сервер API надає API метрик для використання HPA, VPA та команди kubectl top. Сервер метрик є посиланням на реалізацію Metrics API.

  • Metrics API: API Kubernetes, що підтримує доступ до CPU та памʼяті, використаних для автоматичного масштабування робочого навантаження. Щоб це працювало у вашому кластері, вам потрібен сервер розширення API, який надає API метрик.

Metrics API

СТАН ФУНКЦІОНАЛУ: Kubernetes 1.8 [beta]

Metrics-server реалізує Metrics API. Це API дозволяє отримувати доступ до використання CPU та памʼяті для вузлів та Podʼів у вашому кластері. Його основна роль — надавати метрики використання ресурсів компонентам автомасштабування K8s.

Ось приклад запиту до Metrics API для вузла minikube, обробленого через jq для зручного перегляду:

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes/minikube" | jq '.'

Той же самий виклик API, використовуючи curl:

curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/nodes/minikube

Приклад відповіді:

{
  "kind": "NodeMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "minikube",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/nodes/minikube",
    "creationTimestamp": "2022-01-27T18:48:43Z"
  },
  "timestamp": "2022-01-27T18:48:33Z",
  "window": "30s",
  "usage": {
    "cpu": "487558164n",
    "memory": "732212Ki"
  }
}

Ось приклад запиту до Metrics API для Podʼа kube-scheduler-minikube, що міститься в просторі імен kube-system, оброблений через jq для зручного перегляду:

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube" | jq '.'

Той же самий виклик API, використовуючи curl:

curl http://localhost:8080/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube

Приклад відповіді:

{
  "kind": "PodMetrics",
  "apiVersion": "metrics.k8s.io/v1beta1",
  "metadata": {
    "name": "kube-scheduler-minikube",
    "namespace": "kube-system",
    "selfLink": "/apis/metrics.k8s.io/v1beta1/namespaces/kube-system/pods/kube-scheduler-minikube",
    "creationTimestamp": "2022-01-27T19:25:00Z"
  },
  "timestamp": "2022-01-27T19:24:31Z",
  "window": "30s",
  "containers": [
    {
      "name": "kube-scheduler",
      "usage": {
        "cpu": "9559630n",
        "memory": "22244Ki"
      }
    }
  ]
}

Metrics API визначено в репозиторії k8s.io/metrics. Вам потрібно увімкнути шар агрегації API та зареєструвати APIService для API metrics.k8s.io.

Щоб дізнатися більше про Metrics API, див. дизайн API метрик ресурсів, репозиторій metrics-server та API метрик ресурсів.

Вимірювання використання ресурсів

ЦП

Відомості про CPU показуються як середнє значення використання ядра, виміряне в одиницях процесорного часу. Один CPU, у Kubernetes, еквівалентний 1 віртуальному процесору/ядру для хмарних постачальників, і 1 гіперпотоку на процесорах Intel для bare-metal конфігурацій.

Це значення обчислюється шляхом взяття швидкості над кумулятивним лічильником CPU, який надається ядром (як для Linux, так і для Windows ядер). Вікно часу, яке використовується для обчислення CPU, показано у полі window в Metrics API.

Щоб дізнатися більше про те, як Kubernetes розподіляє та вимірює ресурси CPU, див. значення CPU.

Памʼять

Відомості про памʼять показуються як обсяг робочого набору, виміряний в байтах, в момент збору метрики.

У ідеальному світі "робочий набір" — це обсяг памʼяті, що використовується, який не може бути звільнений під час тиску на памʼять. Однак розрахунок робочого набору варіюється залежно від операційної системи хосту і, як правило, інтенсивно використовує евристики для оцінки.

Модель Kubernetes для робочого набору контейнера передбачає, що робочий набір контейнера, що розглядається, підраховується відносно анонімної памʼяті, повʼязаної з цим контейнером. Зазвичай метрика робочого набору також включає деяку кешовану (файлоподібну) памʼять, оскільки операційна система хосту не завжди може повторно використовувати сторінки.

Щоб дізнатися більше про те, як Kubernetes розподіляє та вимірює ресурси памʼяті, див. значення памʼяті.

Metrics Server

Metrics-server витягує метрики ресурсів з kubeletʼів і надає їх в API-серверу Kubernetes через Metrics API для використання HPA та VPA. Ви також можете переглядати ці метрики за допомогою команди kubectl top.

Metrics-server використовує API Kubernetes для відстеження вузлів та Podʼів у вашому кластері. Metrics-server запитує кожний вузол через HTTP, щоб отримати метрики. Metrics-server також створює внутрішнє представлення метаданих про Pod та зберігає кеш стану справності Podʼа. Ця кешована інформація про стан справності Podʼів доступна через розширення API, яке надає metrics-server.

Наприклад, при запиті HPA metrics-server повинен визначити, які Podʼи відповідають селекторам міток у Deployment.

Metrics-server викликає API kubelet для збору метрик з кожного вузла. Залежно від версії metrics-server використовується:

  • Точка доступу ресурсів метрик /metrics/resource у версії v0.6.0+ або
  • Точка доступу Summary API /stats/summary у старших версіях

Що далі

Щоб дізнатися більше про metrics-server, перегляньте репозиторій metrics-server.

Також ви можете перевірити наступне:

Щоб дізнатися про те, як kubelet надає метрики вузла, і як ви можете отримати до них доступ через API Kubernetes, прочитайте Дані метрик вузлів.

4.2.3 - Інструменти для моніторингу ресурсів

Щоб масштабувати застосунок і надавати надійні послуги, вам потрібно розуміти, як застосунок працює при його розгортанні. Ви можете аналізувати продуктивність застосунку в кластері Kubernetes, перевіряючи контейнери, Podʼи, Serviceʼи та загальні характеристики кластера. Kubernetes надає докладну інформацію про використання ресурсів застосункам на кожному з цих рівнів. Ця інформація дозволяє оцінити продуктивність вашого застосунку та визначити місця, де можна видалити перешкоди, щоб покращити загальну продуктивність.

У Kubernetes моніторинг застосунків не залежить від єдиного рішення для моніторингу. На нових кластерах ви можете використовувати конвеєри метрик ресурсів або повні метрики, щоб збирати статистику для моніторингу.

Конвеєр метрик ресурсів

Конвеєр метрик ресурсів надає обмежений набір метрик, повʼязаних з компонентами кластера, такими як контролер Горизонтального автомасштабування Podʼів та утилітою kubectl top. Ці метрики збираються легким, тимчасовим, розташованим в памʼяті metrics-server та експонується через API metrics.k8s.io.

Metrics-server виявляє всі вузли в кластері та запитує kubelet кожного вузла для визначення використання центрального процесора та памʼяті. Kubelet виступає як міст між майстром Kubernetes та вузлами, керуючи Podʼами та контейнерами, що працюють на машині. Kubelet перетворює кожний Pod у його складові контейнери та отримує статистику використання кожного контейнера через інтерфейс середовища виконання контейнерів. Якщо ви використовуєте середовище виконання контейнерів, яке використовує Linux cgroups та простори імен для роботи контейнерів, і середовище виконання контейнерів не публікує статистику використання, тоді kubelet може отримувати ці статистичні дані безпосередньо (використовуючи код з cAdvisor). Незалежно від того, як надходять ці статистичні дані, kubelet після цього використовує агреговану статистику використання ресурсів Podʼів через metrics-server Resource Metrics API. Цей API надається за адресою /metrics/resource/v1beta1 на автентифікованих та портах kublet, доступних тільки для читання.

Конвеєр повних метрик

Конвеєр повних метрик дає вам доступ до більш розширених метрик. Kubernetes може відповідати на ці метрики, автоматично масштабуючи або адаптуючи кластер на основі його поточного стану за допомогою механізмів, таких як Горизонтальне автомасштабування Podʼів. Конвеєр моніторингу витягує метрики з kubelet та експонує їх в Kubernetes через адаптер, реалізуючи API custom.metrics.k8s.io або external.metrics.k8s.io.

Kubernetes розроблено для роботи з OpenMetrics, який є одним із проєктів моніторингу CNCF, побудованим на основі формату експонування метрик Prometheus та розширюючи його майже у 100% сумісний спосіб.

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

При проєктуванні та реалізації конвеєра повних метрик ви можете зробити ці моніторингові дані доступні зворотньо у Kubernetes. Наприклад, HorizontalPodAutoscaler може використовувати оброблені метрики для розрахунку кількості Podʼів, які потрібно запустити як складову вашого навантаження.

Інтеграція конвеєра повних у вашу реалізацію Kubernetes знаходиться поза межами документації Kubernetes через дуже широкий спектр можливих рішень.

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

Що далі

Дізнайтеся про додаткові інструменти для налагодження, включаючи:

4.2.4 - Відстеження стану вузлів

Node Problem Detector — це служба для моніторингу та звітування про стан вузла. Ви можете запустити Node Problem Detector як DaemonSet або окремий демон. Node Problem Detector збирає інформацію про проблеми вузла з різних демонів і повідомляє їх на сервер API як стан вузла або як події.

Для отримання інформації щодо встановлення та використання Node Problem Detector, див. Документацію проєкту Node Problem Detector.

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

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

Обмеження

Увімкнення Node Problem Detector

Деякі хмарні постачальники увімкнуть Node Problem Detector як надбудову. Ви також можете увімкнути Node Problem Detector за допомогою kubectl або створити Addon DaemonSet.

Використання kubectl для увімкнення Node Problem Detector

kubectl надає найбільш гнучке керування Node Problem Detector. Ви можете перезаписати типову конфігурацію, щоб вона відповідала вашому середовищу або виявляла спеціалізовані проблеми вузла. Наприклад:

  1. Створіть конфігурацію Node Problem Detector, аналогічну node-problem-detector.yaml:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-problem-detector-v0.1
      namespace: kube-system
      labels:
        k8s-app: node-problem-detector
        version: v0.1
        kubernetes.io/cluster-service: "true"
    spec:
      selector:
        matchLabels:
          k8s-app: node-problem-detector  
          version: v0.1
          kubernetes.io/cluster-service: "true"
      template:
        metadata:
          labels:
            k8s-app: node-problem-detector
            version: v0.1
            kubernetes.io/cluster-service: "true"
        spec:
          hostNetwork: true
          containers:
          - name: node-problem-detector
            image: registry.k8s.io/node-problem-detector:v0.1
            securityContext:
              privileged: true
            resources:
              limits:
                cpu: "200m"
                memory: "100Mi"
              requests:
                cpu: "20m"
                memory: "20Mi"
            volumeMounts:
            - name: log
              mountPath: /log
              readOnly: true
          volumes:
          - name: log
            hostPath:
              path: /var/log/
  2. Запустіть Node Problem Detector за допомогою kubectl:

    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector.yaml
    

Використання Podʼа надбудови для увімкнення Node Problem Detector

Якщо ви використовуєте власне рішення для ініціалізації кластера та не потребуєте перезапису типової конфігурації, ви можете скористатися Podʼом надбудови, щоб автоматизувати розгортання.

Створіть node-problem-detector.yaml та збережіть конфігурацію в теці Podʼа надбудови /etc/kubernetes/addons/node-problem-detector на вузлі панелі управління.

Перезапис конфігурації

Типова конфігурація вбудована під час збирання Docker-образу Node Problem Detector.

Однак ви можете використовувати ConfigMap для перезапису конфігурації:

  1. Змініть файли конфігурації в config/.

  2. Створіть ConfigMap node-problem-detector-config:

    kubectl create configmap node-problem-detector-config --from-file=config/
    
  3. Змініть node-problem-detector.yaml, щоб використовувати ConfigMap:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: node-problem-detector-v0.1
      namespace: kube-system
      labels:
        k8s-app: node-problem-detector
        version: v0.1
        kubernetes.io/cluster-service: "true"
    spec:
      selector:
        matchLabels:
          k8s-app: node-problem-detector  
          version: v0.1
          kubernetes.io/cluster-service: "true"
      template:
        metadata:
          labels:
            k8s-app: node-problem-detector
            version: v0.1
            kubernetes.io/cluster-service: "true"
        spec:
          hostNetwork: true
          containers:
          - name: node-problem-detector
            image: registry.k8s.io/node-problem-detector:v0.1
            securityContext:
              privileged: true
            resources:
              limits:
                cpu: "200m"
                memory: "100Mi"
              requests:
                cpu: "20m"
                memory: "20Mi"
            volumeMounts:
            - name: log
              mountPath: /log
              readOnly: true
            - name: config # Overwrite the config/ directory with ConfigMap volume
              mountPath: /config
              readOnly: true
          volumes:
          - name: log
            hostPath:
              path: /var/log/
          - name: config # Define ConfigMap volume
            configMap:
              name: node-problem-detector-config
  4. Перестворіть Node Problem Detector з новим файлом конфігурації:

    # Якщо у вас вже працює Node Problem Detector, видаліть перед перстворенням
    kubectl delete -f https://k8s.io/examples/debug/node-problem-detector.yaml
    kubectl apply -f https://k8s.io/examples/debug/node-problem-detector-configmap.yaml
    

Перезапис конфігурації не підтримується, якщо Node Problem Detector працює як надбудова кластера. Менеджер надбудов не підтримує ConfigMap.

Демони проблем

Демон проблем — це піддемон Node Problem Detector. Він моніторить певні види проблем вузла та повідомляє про них Node Problem Detector. Існує кілька типів підтримуваних демонів проблем.

  • Тип демона SystemLogMonitor моніторить системні логи та повідомляє про проблеми та метрики згідно з попередньо визначеними правилами. Ви можете настроїти конфігурації для різних джерел логів таких як filelog, kmsg, kernel, abrt, та systemd.

  • Тип демона SystemStatsMonitor збирає різноманітні статистичні дані системи, повʼязані зі справністю, як метрики. Ви можете настроїти його поведінку, оновивши його файл конфігурації.

  • Тип демона CustomPluginMonitor викликає та перевіряє різні проблеми вузла, запускаючи сценарії, визначені користувачем. Ви можете використовувати різні власні втулки для моніторингу різних проблем і настроювати поведінку демона, оновивши файл конфігурації.

  • Тип демона HealthChecker перевіряє стан kubelet та контейнерного середовища на вузлі.

Додавання підтримки для іншого формату логів

Монітор системного логу наразі підтримує файлові логи, journald та kmsg. Додаткові джерела можна додати, реалізувавши новий спостерігач за логами.

Додавання власних втулків моніторингу

Ви можете розширити Node Problem Detector для виконання будь-яких сценаріїв моніторингу, написаних будь-якою мовою програмування, розробивши власний втулок. Сценарії моніторингу повинні відповідати протоколу втулка щодо коду виходу та стандартного виводу. Для отримання додаткової інформації див. пропозицію інтерфейсу втулка.

Експортер

Експортер повідомляє про проблеми та/або метрики вузлів до певних бекендів. Підтримуються наступні експортери:

  • Експортер Kubernetes: цей експортер повідомляє про проблеми вузлів на сервер API Kubernetes. Тимчасові проблеми повідомляються як Події, а постійні проблеми — як Стан вузла.

  • Експортер Prometheus: цей експортер локально повідомляє про проблеми вузлів та метрики у форматі Prometheus (або OpenMetrics). Ви можете вказати IP-адресу та порт для експортера, використовуючи аргументи командного рядка.

  • Експортер Stackdriver: цей експортер повідомляє про проблеми вузлів та метрики в службу моніторингу Stackdriver. Поведінку експорту можна налаштувати, використовуючи файл конфігурації.

Рекомендації та обмеження

Рекомендується запускати Node Problem Detector в вашому кластері для моніторингу стану вузлів. При запуску Node Problem Detector можна очікувати додаткове навантаження ресурсів на кожному вузлі. Зазвичай це прийнятно, оскільки:

  • Лог ядра росте відносно повільно.
  • Для Node Problem Detector встановлено обмеження ресурсів.
  • Навіть при великому навантаженні використання ресурсів прийнятне. Докладніше див. результати бенчмарків Node Problem Detector.

4.2.5 - Налагодження вузлів Kubernetes за допомогою crictl

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.11 [stable]

crictl — це інтерфейс командного рядка для сумісних з CRI контейнерних середовищ. Ви можете використовувати його для огляду та налагодження контейнерних середовищ та застосунків на вузлі Kubernetes. crictl та його вихідний код розміщені у репозиторії cri-tools.

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

Для роботи crictl потрібна операційна система Linux з CRI середовищем.

Встановлення crictl

Ви можете завантажити архів crictl зі сторінки релізів у репозиторії cri-tools release page, для різних архітектур. Завантажте версію, яка відповідає вашій версії Kubernetes. Розпакуйте її та перемістіть у розташування у вашому системному шляху, наприклад, /usr/local/bin/.

Використання

Команда crictl має кілька підкоманд та прапорців для використання. Використовуйте crictl help або crictl <підкоманда> help для отримання більш детальної інформації.

Ви можете встановити точку доступу для crictl, виконавши одну з наступних дій:

  • Встановіть прапорці --runtime-endpoint та --image-endpoint.
  • Встановіть змінні середовища CONTAINER_RUNTIME_ENDPOINT та IMAGE_SERVICE_ENDPOINT.
  • Встановіть точку доступу в файлі конфігурації /etc/crictl.yaml. Щоб вказати інший файл, використовуйте прапорець --config=ШЛЯХ_ДО_ФАЙЛУ під час запуску crictl.

Ви також можете вказати значення тайм-ауту при підключенні до сервера та увімкнути або вимкнути налагодження, вказавши значення timeout або debug в файлі конфігурації або використовуючи прапорці командного рядка --timeout та --debug.

Щоб переглянути або змінити поточну конфігурацію, перегляньте або відредагуйте вміст /etc/crictl.yaml. Наприклад, конфігурація при використанні виконавчого середовища containerd буде схожа на цю:

runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: true

Щоб дізнатися більше про crictl, зверніться до документації crictl.

Приклади команд crictl

Нижче наведено деякі приклади команд crictl та їх вивід.

Отримання переліку Podʼів

Вивести перелік усіх Podʼів:

crictl pods

Вихідний результат схожий на такий:

POD ID              CREATED              STATE               NAME                         NAMESPACE           ATTEMPT
926f1b5a1d33a       About a minute ago   Ready               sh-84d7dcf559-4r2gq          default             0
4dccb216c4adb       About a minute ago   Ready               nginx-65899c769f-wv2gp       default             0
a86316e96fa89       17 hours ago         Ready               kube-proxy-gblk4             kube-system         0
919630b8f81f1       17 hours ago         Ready               nvidia-device-plugin-zgbbv   kube-system         0

Список Podʼів за назвою:

crictl pods --name nginx-65899c769f-wv2gp

Вихідний результат схожий на такий:

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

Список Podʼів за мітками:

crictl pods --label run=nginx

Вихідний результат схожий на такий:

POD ID              CREATED             STATE               NAME                     NAMESPACE           ATTEMPT
4dccb216c4adb       2 minutes ago       Ready               nginx-65899c769f-wv2gp   default             0

Отримання переліку образів

Вивести всі образи:

crictl images

Вихідний результат схожий на такий:

IMAGE                                     TAG                 IMAGE ID            SIZE
busybox                                   latest              8c811b4aec35f       1.15MB
k8s.gcr.io/hyperkube-amd64                v1.10.3             e179bbfe5d238       665MB
k8s.gcr.io/pause                          3.1                 da86e6ba6ca19       742kB
nginx                                     latest              cd5239a0906a6       109MB

Список образів за репозиторієм:

crictl images nginx

Вихідний результат схожий на такий:

IMAGE               TAG                 IMAGE ID            SIZE
nginx               latest              cd5239a0906a6       109MB

Вивести лише ідентифікатори образів:

crictl images -q

Вихідний результат схожий на такий:

sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
sha256:e179bbfe5d238de6069f3b03fccbecc3fb4f2019af741bfff1233c4d7b2970c5
sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e
sha256:cd5239a0906a6ccf0562354852fae04bc5b52d72a2aff9a871ddb6bd57553569

Отримання переліку контейнерів

Вивести всі контейнери:

crictl ps -a

Вихідний результат схожий на такий:

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   7 хвилин тому       Running             sh                         1
9c5951df22c78       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   8 хвилин тому       Exited              sh                         0
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     8 хвилин тому       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   18 годин тому        Running             kube-proxy                 0

Вивести працюючі контейнери:

crictl ps

Вихідний результат схожий на такий:

CONTAINER ID        IMAGE                                                                                                             CREATED             STATE               NAME                       ATTEMPT
1f73f2d81bf98       busybox@sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47                                   6 хвилин тому       Running             sh                         1
87d3992f84f74       nginx@sha256:d0a8828cccb73397acb0073bf34f4d7d8aa315263f1e7806bf8c55d8ac139d5f                                     7 хвилин тому       Running             nginx                      0
1941fb4da154f       k8s-gcrio.azureedge.net/hyperkube-amd64@sha256:00d814b1f7763f4ab5be80c58e98140dfc69df107f253d7fdd714b30a714260a   17 годин тому        Running             kube-proxy                 0

Виконання команди у працюючому контейнері

crictl exec -i -t 1f73f2d81bf98 ls

Вихідний результат схожий на такий:

bin   dev   etc   home  proc  root  sys   tmp   usr   var

Отримання логів контейнерів

Отримати всі логи контейнера:

crictl logs 87d3992f84f74

Вихідний результат схожий на такий:

10.240.0.96 - - [06/Jun/2018:02:45:49 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:50 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"
10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

Отримати лише останні N рядків логів:

crictl logs --tail=1 87d3992f84f74

Вихідний результат схожий на такий:

10.240.0.96 - - [06/Jun/2018:02:45:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

Що далі

4.2.6 - Аудит

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

Аудит дозволяє адміністраторам кластера відповісти на такі питання:

  • що сталося?
  • коли це сталося?
  • хто це ініціював?
  • на чому це сталося?
  • де це було помічено?
  • звідки це було ініційовано?
  • куди це направлялося?

Записи аудиту починають свій життєвий цикл всередині компонента kube-apiserver. Кожен запит на кожному етапі його виконання генерує подію аудиту, яка потім передається до попередньої обробки відповідно до певної політики та записується в бекенд. Політика визначає, що буде записано, а бекенд зберігає записи. Поточні реалізації бекендів включають файли логів та вебхуки.

Кожний запит може бути записаний з асоційованим stage. Визначені етапи:

  • RequestReceived — Етап для подій, що генеруються, як тільки обробник аудиту отримує запит, і до того, як він передає його вниз по ланцюжку обробників.
  • ResponseStarted — Після надсилання заголовків відповіді, але перед відправленням тіла відповіді. Цей етап генерується лише для тривалих запитів (наприклад, watch).
  • ResponseComplete — Тіло відповіді завершено і більше байтів не буде відправлено.
  • Panic — Події, що генеруються при виникненні паніки.

Функція логування аудиту збільшує витрати памʼяті сервера API, оскільки для кожного запиту зберігається певний контекст, необхідний для аудитування. Витрати памʼяті залежать від конфігурації логування аудиту.

Політика аудиту

Політика аудиту визначає правила того, які події повинні бути записані та які дані вони повинні містити. Структура обʼєкта політики аудиту визначена в групі API audit.k8s.io. Коли подія обробляється, її порівнюють зі списком прав по черзі. Перший збіг прав встановлює рівень аудиту події. Визначені рівні аудиту:

  • None — не записувати події, які відповідають цьому правилу.
  • Metadata — записувати метадані запиту (користувач, часова відмітка, ресурс, дія тощо), але не тіло запиту чи відповіді.
  • Request — записувати метадані події та тіло запиту, але не тіло відповіді. Це не застосовується до запитів на ресурси.
  • RequestResponse — записувати метадані події, тіло запиту та відповіді. Це не застосовується до запитів на ресурси.

Ви можете передати файл з політикою до kube-apiserver, використовуючи прапорець --audit-policy-file. Якщо прапорець пропущено, жодні події не записуються. Зверніть увагу, що поле rules обовʼязково повинно бути вказано у файлі політики аудиту. Політика з нульовою кількістю (0) прав вважається неприпустимою.

Нижче наведено приклад файлу політики аудиту:

apiVersion: audit.k8s.io/v1 # Це обовʼязково.
kind: Policy
# Не генерувати події аудиту для всіх запитів на етапі RequestReceived.
omitStages:
  - "RequestReceived"
rules:
  # Логувати зміни у вузлах на рівні RequestResponse
  - level: RequestResponse
    resources:
    - group: ""
      # Ресурс "pods" не відповідає запитам на будь-який підресурс вузлів,
      # що відповідає політиці RBAC.
      resources: ["pods"]
  # Логувати "pods/log", "pods/status" на рівні Metadata
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

  # Не логувати запити на configmap під назвою "controller-leader"
  - level: None
    resources:
    - group: ""
      resources: ["configmaps"]
      resourceNames: ["controller-leader"]

  # Не логувати watch-запити "system:kube-proxy" на endpoints або services
  - level: None
    users: ["system:kube-proxy"]
    verbs: ["watch"]
    resources:
    - group: "" # Основна група API
      resources: ["endpoints", "services"]

  # Не логувати автентифіковані запити до певних URL-шляхів, що не є ресурсами.
  - level: None
    userGroups: ["system:authenticated"]
    nonResourceURLs:
    - "/api*" # Зіставлення з шаблоном.
    - "/version"

  # Логувати тіло запиту на зміни configmap у kube-system.
  - level: Request
    resources:
    - group: "" # Основна група API
      resources: ["configmaps"]
    # Це правило застосовується тільки до ресурсів в просторі імен "kube-system".
    # Порожній рядок "" можна використовувати для вибору ресурсів без простору імен.
    namespaces: ["kube-system"]

  # Логувати зміни configmap і secret у всіх інших просторах імен на рівні Metadata.
  - level: Metadata
    resources:
    - group: "" # Основна група API
      resources: ["secrets", "configmaps"]

  # Логувати всі інші ресурси в основній і розширюваній групах на рівні Request.
  - level: Request
    resources:
    - group: "" # Основна група API
    - group: "extensions" # Версія групи НЕ повинна включатися.

  # Загальне правило для логування всіх інших запитів на рівні Metadata.
  - level: Metadata
    # Довгострокові запити, такі як watches, які підпадають під це правило,
    # не генерують подію аудиту на етапі RequestReceived.
    omitStages:
      - "RequestReceived"

Ви можете використовувати мінімальну політику аудиту для логування всіх запитів на рівні Metadata:

# Записати всі запити на рівні Metadata.
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

Якщо ви створюєте власний профіль аудиту, ви можете скористатися профілем аудиту для Google Container-Optimized OS як вихідною точкою. Ви можете перевірити сценарій configure-helper.sh, який генерує файл політики аудиту. Більшість файлу політики аудиту можна побачити, дивлячись безпосередньо на цей сценарій.

Ви також можете звернутися до посилання на конфігурацію Policy для отримання деталей про визначені поля.

Бекенди аудиту

Події аудиту зберігаються в зовнішньому сховищі за допомогою бекендів аудиту. Стандартно kube-apiserver надає два бекенди:

  • Файловий бекенд, який записує події у файлову систему.
  • Бекенд Webhook, який відправляє події на зовнішній HTTP API.

У всіх випадках події аудиту слідують структурі, визначеній API Kubernetes в групі API audit.k8s.io.

Бекенд логів

Бекенд логів записує події аудиту у файл у форматі JSONlines. Ви можете налаштувати бекенд логів за допомогою наступних прапорців kube-apiserver:

  • --audit-log-path вказує шлях до файлу логу, який бекенд логів використовує для запису подій аудиту. Відсутність цього прапорця вимикає бекенд логів; - означає стандартний вивід
  • --audit-log-maxage визначає максимальну кількість днів для зберігання старих файлів логів аудиту
  • --audit-log-maxbackup визначає максимальну кількість файлів логів аудиту для зберігання
  • --audit-log-maxsize визначає максимальний розмір в мегабайтах файлу логів аудиту до його ротації

Якщо панель управління вашого кластера працює з kube-apiserver як з Pod, не забудьте змонтувати hostPath до місця розташування файлу політики та файлу логів, щоб записи аудиту були збережені. Наприклад:

  - --audit-policy-file=/etc/kubernetes/audit-policy.yaml
  - --audit-log-path=/var/log/kubernetes/audit/audit.log

потім змонтуйте томи:

...
volumeMounts:
  - mountPath: /etc/kubernetes/audit-policy.yaml
    name: audit
    readOnly: true
  - mountPath: /var/log/kubernetes/audit/
    name: audit-log
    readOnly: false

і нарешті налаштуйте hostPath:

...
volumes:
- name: audit
  hostPath:
    path: /etc/kubernetes/audit-policy.yaml
    type: File

- name: audit-log
  hostPath:
    path: /var/log/kubernetes/audit/
    type: DirectoryOrCreate

Бекенд Webhook

Бекенд аудиту webhook надсилає події аудиту до віддаленого веб-API, яке вважається формою Kubernetes API, включаючи засоби автентифікації. Ви можете налаштувати бекенд webhook за допомогою наступних прапорців kube-apiserver:

  • --audit-webhook-config-file вказує шлях до файлу з конфігурацією webhook. Конфігурація webhook фактично є спеціалізованим kubeconfig.
  • --audit-webhook-initial-backoff вказує час очікування після першого невдалого запиту перед повторною спробою. Наступні запити повторюються з експоненційною затримкою.

Файл конфігурації webhook використовує формат kubeconfig для вказування віддаленої адреси служби та облікових даних, які використовуються для підключення до неї.

Пакетна обробка подій

Обидва типи бекенд систем, як логів, так і webhook, підтримують пакетну обробку. Використаємо webhook як приклад, ось перелік доступних прапорців. Щоб отримати такий же прапорець для логів, замініть webhook на log у назві прапорця. Стандартно пакетна обробка увімкнена для webhook і вимкнена для log. Так само, типово, обмеження пропускної здатності увімкнено в webhook і вимкнене в log.

  • --audit-webhook-mode визначає стратегію буферизації. Одна з наступних:
    • batch — буферизувати події та асинхронно обробляти їх пакетами. Це стандартне значення.
    • blocking — блокувати відповіді сервера API на обробці кожної окремої події.
    • blocking-strict — те саме, що й blocking, але коли відбувається збій під час логування аудиту на етапі RequestReceived, весь запит до kube-apiserver зазнає збою.

Наступні прапорці використовуються тільки в режимі batch:

  • --audit-webhook-batch-buffer-size визначає кількість подій для буферизації перед пакетною обробкою. Якщо швидкість надходження подій переповнює буфер, події відкидаються.
  • --audit-webhook-batch-max-size визначає максимальну кількість подій в одному пакеті.
  • --audit-webhook-batch-max-wait визначає максимальний час очікування перед безумовною буферизацією подій у черзі.
  • --audit-webhook-batch-throttle-qps визначає максимальну середню кількість пакетів, що генеруються за секунду.
  • --audit-webhook-batch-throttle-burst визначає максимальну кількість пакетів, які генеруються в той же момент, якщо дозволений QPS раніше не використовувався повністю.

Налаштування параметрів

Параметри повинні бути встановлені з урахуванням навантаження на API-сервер.

Наприклад, якщо kube-apiserver отримує 100 запитів кожну секунду, і кожен запит проходить аудит лише на етапах ResponseStarted та ResponseComplete, вам слід розрахувати приблизно 200 подій аудиту, які генеруються кожну секунду. Припускаючи, що в пакеті може бути до 100 подій, вам слід встановити рівень обмеження принаймні у 2 запити на секунду. Припускаючи, що система може потребувати до 5 секунд для запису подій, вам слід встановити розмір буфера для зберігання подій протягом до 5 секунд; це означає: 10 пакетів або 1000 подій.

Проте в більшості випадків стандартні значення параметрів повинні бути достатніми, і вам не потрібно хвилюватися про їх ручне встановлення. Ви можете переглянути наступні метрики Prometheus, які експонує kube-apiserver, а також логи, щоб контролювати стан підсистеми аудиту.

  • Метрика apiserver_audit_event_total містить загальну кількість експортованих подій аудиту.
  • Метрика apiserver_audit_error_total містить загальну кількість подій, які були втрачені через помилку під час експортування.

Обмеження розміру запису в лозі

Обидва бекенди і логів, і webhook підтримують обмеження розміру подій, які записуються. Наприклад, ось список прапорців, доступних для бекенду логів:

  • audit-log-truncate-enabled визначає, чи ввімкнене обрізання подій та пакетів.
  • audit-log-truncate-max-batch-size максимальний розмір у байтах пакета, який надсилається до бекенду.
  • audit-log-truncate-max-event-size максимальний розмір у байтах аудитивної події, яка надсилається до бекенду.

Типово обрізання вимкнено як у webhook, так і у log. Адміністратор кластера повинен встановити audit-log-truncate-enabled або audit-webhook-truncate-enabled, щоб увімкнути цю функцію.

Що далі

4.2.7 - Налагодження вузлів Kubernetes за допомогою kubectl

Ця сторінка показує, як налагоджувати вузол на кластері Kubernetes за допомогою команди kubectl debug.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.2. Для перевірки версії введіть kubectl version.

Вам потрібно мати дозвіл на створення Podʼів та призначення їх новим Вузлам. Також вам потрібно мати дозвіл на створення Podʼів, які мають доступ до файлових систем з хосту.

Налагодження вузла за допомогою kubectl debug node

Використовуйте команду kubectl debug node, щоб розмістити Pod на Вузлі, який ви хочете налагодити. Ця команда корисна в сценаріях, коли ви не можете отримати доступ до свого Вузла за допомогою зʼєднання SSH. Після створення Podʼа, відкривається інтерактивний інтерфейс оболонки на Вузлі. Щоб створити інтерактивну оболонку на Вузлі з назвою “mynode”, виконайте:

kubectl debug node/mynode -it --image=ubuntu
Creating debugging pod node-debugger-mynode-pdx84 with container debugger on node mynode.
If you don't see a command prompt, try pressing enter.
root@mynode:/#

Команда налагоджування допомагає збирати інформацію та розвʼязувати проблеми. Команди, які ви можете використовувати, включають ip, ifconfig, nc, ping, ps тощо. Ви також можете встановити інші інструменти, такі як mtr, tcpdump та curl, з відповідного менеджера пакунків.

Pod налагодження може отримувати доступ до кореневої файлової системи Вузла, підключеної за адресою /host в Podʼі. Якщо ви використовуєте kubelet у просторі імен файлової системи, Pod налагодження бачить корінь для цього простору імен, а не всього Вузла. Для типового вузла Linux ви можете переглянути наступні шляхи для пошуку відповідних логів:

/host/var/log/kubelet.log
Логи kubelet, який відповідає за запуск контейнерів на вузлі.
/host/var/log/kube-proxy.log
Логи kube-proxy, який відповідає за направлення трафіку на точки доступу Service.
/host/var/log/containerd.log
Логи процесу containerd, який працює на вузлі.
/host/var/log/syslog
Показує загальні повідомлення та інформацію щодо системи.
/host/var/log/kern.log
Показує логи ядра.

При створенні сеансу налагодження на Вузлі майте на увазі, що:

  • kubectl debug автоматично генерує імʼя нового контейнера на основі імені вузла.
  • Коренева файлова система Вузла буде змонтована за адресою /host.
  • Хоча контейнер працює у просторі імен IPC, мережі та PID хосту, Pod не є привілейованим. Це означає, що читання деякої інформації про процес може бути неможливим, оскільки доступ до цієї інформації мають тільки суперкористувачі. Наприклад, chroot /host буде невдалим. Якщо вам потрібен привілейований контейнер, створіть його вручну або використовуйте прапорець --profile=sysadmin.
  • Застосовуючи профілі налагодження, ви можете встановити конкретні властивості, такі як securityContext до Podʼу налагодження.

Очищення

Коли ви закінчите використання Podʼа налагодження, видаліть його:

kubectl get pods
NAME                          READY   STATUS       RESTARTS   AGE
node-debugger-mynode-pdx84    0/1     Completed    0          8m1s
# Змініть імʼя контейнера відповідно
kubectl delete pod node-debugger-mynode-pdx84 --now
pod "node-debugger-mynode-pdx84" deleted

4.2.8 - Розробка та налагодження сервісів локально за допомогою telepresence

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

telepresence — це інструмент, який полегшує процес розробки та налагодження сервісів локально, прокидаючи проксі для сервісу на віддаленому кластері Kubernetes. Використання telepresence дозволяє використовувати власні інструменти, такі як налагоджувач та IDE, для локального сервісу та забезпечує повний доступ до ConfigMap, Secret та Service, що працюють на віддаленому кластері.

У цьому документі описано використання telepresence для розробки та налагодження сервісів локально, які працюють на віддаленому кластері.

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

  • Встановлений кластер Kubernetes
  • Налаштований kubectl для звʼязку з кластером
  • Telepresence вже встановлено

Підʼєднання вашого локального компʼютера до віддаленого кластера Kubernetes

Після встановлення telepresence запустіть telepresence connect, щоб запустити його Демона та підʼєднати ваш робочий компʼютер до кластера.

$ telepresence connect

Launching Telepresence Daemon
...
Connected to context default (https://<cluster public IP>)

Ви можете використовувати команду curl для отримання доступу до сервісів за синтаксисом Kubernetes, наприклад, curl -ik https://kubernetes.default.

Розробка або налагодження наявного сервісу

При розробці застосунку у Kubernetes ви зазвичай програмуєте або налагоджувати один сервіс. Цей сервіс може потребувати доступу до інших сервісів для тестування та налагодження. Один із варіантів — використання конвеєра постійного розгортання (continuous deployment pipeline), але навіть найшвидший конвеєр розгортання додає затримку в цикл програмування або налагодження.

Використовуйте команду telepresence intercept $SERVICE_NAME --port $LOCAL_PORT:$REMOTE_PORT для створення "перехоплення" для перенаправлення трафіку віддаленого сервісу.

Де:

  • $SERVICE_NAME — це назва вашого локального сервісу
  • $LOCAL_PORT — це порт, на якому працює ваш сервіс на вашому локальному робочому місці
  • $REMOTE_PORT — це порт, на який ваш сервіс слухає в кластері

Виконавши цю команду, Telepresence каже перенаправляти віддалений трафік на ваш локальний сервіс замість сервісу в віддаленому кластері Kubernetes. Вносьте зміни до вихідного коду вашого сервісу локально, зберігайте та переглядайте відповідні зміни, коли ви отримуєте доступ до вашого віддаленого застосунку, ефект буде відразу. Ви також можете запустити ваш локальний сервіс, використовуючи налагоджувач або будь-який інший локальний інструмент розробки.

Як працює Telepresence?

Telepresence встановлює агента перенаправлення трафіку поряд із контейнером вашого наявного застосунку, який працює в віддаленому кластері. Він перехоплює всі запити на трафік, що надходять до Podʼа, і замість того, щоб пересилати це до застосунку у віддаленому кластері, він маршрутизує весь трафік (коли ви створюєте глобальне перехоплення або підмножину трафіку (коли ви створюєте персональне перехоплення) до вашого локального середовища розробки.

Що далі

Якщо вас цікавить практичний посібник, перегляньте ось цей посібник, в якому покроково описано розробку програми Guestbook локально на Google Kubernetes Engine.

Також читання відвідайте вебсайт Telepresence.

4.2.9 - Поради щодо налагодження Windows

Розвʼязання проблем на рівні вузла

  1. Мої Podʼи застрягли на "Container Creating" або постійно перезавантажуються.

    Переконайтеся, що ваш образ pause відповідає версії вашої операційної системи Windows. Див. Контейнер pause для перегляду останнього/рекомендованого образу pause та/або отримання додаткової інформації.

  2. Мої Podʼи показують статус як ErrImgPull або ImagePullBackOff.

    Переконайтеся, що ваш Pod планується на сумісний вузол Windows.

    Додаткову інформацію про те, як вказати сумісний вузол для вашого Podʼа, можна знайти в цьому посібнику.

Розвʼязання проблем мережі

  1. Мої Podʼи Windows не мають підключення до мережі.

    Якщо ви використовуєте віртуальні машини, переконайтеся, що MAC spoofing увімкнено на всіх адаптерах мережі віртуальних машин.

  2. Мої Podʼи Windows не можуть пінгувати зовнішні ресурси.

    Podʼи Windows не мають правил для вихідного трафіку, програмованих для протоколу ICMP. Однак, підтримується TCP/UDP. При спробі продемонструвати підключення до ресурсів за межами кластера, замініть ping <IP> відповідними командами curl <IP>.

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

    Одним із вимог мережі Kubernetes (див. Модель Kubernetes) є внутрішнє звʼязування кластера без NAT всередині. Щоб відповідати цій вимозі, існує ExceptionList для всього трафіку, де ви не хочете, щоб відбувалось використання NAT назовні. Однак, це також означає, що вам потрібно виключити зовнішню IP-адресу, яку ви намагаєтесь запитати з ExceptionList. Тільки тоді трафік, який походить з вашого Podʼа Windows, буде коректно SNAT'ed для отримання відповіді зі світу. З цього погляду ваш ExceptionList у cni.conf повинен виглядати так:

    "ExceptionList": [
                    "10.244.0.0/16",  # Підмережа кластера
                    "10.96.0.0/12",   # Підмережа служби
                    "10.127.130.0/24" # Управління (хост) підмережа
                ]
    
  3. Мій вузол Windows не може отримати доступ до служб типу NodePort.

    Доступ до локального NodePort з самого вузла не вдається. Це відоме обмеження. Доступ до NodePort працює з інших вузлів або зовнішніх клієнтів.

  4. vNICs та HNS точки доступу контейнерів видаляються.

    Цю проблему може викликати відмова в передачі параметра hostname-override до kube-proxy. Щоб вирішити це, користувачі повинні передавати імʼя хосту kube-proxy наступним чином:

    C:\k\kube-proxy.exe --hostname-override=$(hostname)
    
  5. Мій вузол Windows не може отримати доступ до моїх Service за допомогою IP-адреси Service

    Це відоме обмеження стека мережі на Windows. Однак, Podʼи Windows можуть отримувати доступ до IP-адреси Service.

  6. Під час запуску kubelet не знайдено мережевого адаптера.

    Для правильної роботи мережі Windows потрібен віртуальний адаптер. Якщо наступні команди не повертають результатів (в оболонці адміністратора), створення віртуальної мережі, необхідна передумова для роботи kubelet, не вдалося:

    Get-HnsNetwork | ? Name -ieq "cbr0"
    Get-NetAdapter | ? Name -Like "vEthernet (Ethernet*"
    

    Часто варто змінити параметр InterfaceName у скрипті start.ps1, в разі, якщо мережевий адаптер хосту не є "Ethernet". В іншому випадку зверніться до виводу скрипту start-kubelet.ps1, щоб побачити, чи є помилки під час створення віртуальної мережі.

  7. DNS-перетворення не працює належним чином.

    Перевірте обмеження DNS для Windows у цьому розділі.

  8. kubectl port-forward видає помилку "unable to do port forwarding: wincat not found"

    Це було реалізовано в Kubernetes 1.15, включивши wincat.exe в інфраструктурний контейнер pause mcr.microsoft.com/oss/kubernetes/pause:3.6. Будьте впевнені, що використовуєте підтримувану версію Kubernetes. Якщо ви хочете побудувати власний контейнер інфраструктури pause, обовʼязково додайте wincat.

  9. Моє встановлення Kubernetes падає, тому що мій вузол сервера Windows знаходиться за проксі

    Якщо ви перебуваєте за проксі, наступні змінні середовища PowerShell повинні бути визначені:

    [Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://proxy.example.com:80/", [EnvironmentVariableTarget]::Machine)
    [Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://proxy.example.com:443/", [EnvironmentVariableTarget]::Machine)
    

Розвʼязання проблем Flannel

  1. З Flannel мої вузли мають проблеми після повторного приєднання до кластера.

    Кожного разу, коли раніше видалений вузол повторно приєднується до кластера, flannelD намагається призначити нову підмережу Podʼа вузлу. Користувачі повинні видалити старі файли конфігурації підмережі Podʼа за наступними шляхами:

    Remove-Item C:\k\SourceVip.json
    Remove-Item C:\k\SourceVipRequest.json
    
  2. Flanneld застрягає в "Waiting for the Network to be created"

    Є численні звіти про цю проблему; ймовірно, це проблема з часом встановлення управлінської IP-адреси мережі flannel. Обхідним рішенням є перезапуск start.ps1 або перезапуск його вручну так:

    [Environment]::SetEnvironmentVariable("NODE_NAME", "<Windows_Worker_Hostname>")
    C:\flannel\flanneld.exe --kubeconfig-file=c:\k\config --iface=<Windows_Worker_Node_IP> --ip-masq=1 --kube-subnet-mgr=1
    
  3. Мої Podʼи Windows не можуть запуститися через відсутність /run/flannel/subnet.env.

    Це вказує на те, що Flannel не запустився правильно. Ви можете спробувати перезапустити flanneld.exe або вручну скопіювати файли з /run/flannel/subnet.env на майстрі Kubernetes в C:\run\flannel\subnet.env на робочий вузол Windows та змінити рядок FLANNEL_SUBNET на інший номер. Наприклад, якщо підмережа вузла 10.244.4.1/24 бажана:

    FLANNEL_NETWORK=10.244.0.0/16
    FLANNEL_SUBNET=10.244.4.1/24
    FLANNEL_MTU=1500
    FLANNEL_IPMASQ=true
    

Подальші дослідження

Якщо ці кроки не розвʼязують вашої проблеми, ви можете отримати допомогу у запуску контейнерів Windows на вузлах Windows у Kubernetes у наступних ресурсах:

5 - Керування обʼєктами Kubernetes

Декларативні та імперативні парадигми взаємодії з API Kubernetes.

5.1 - Декларативне керування обʼєктами Kubernetes з використанням конфігураційних файлів

Обʼєкти Kubernetes можна створювати, оновлювати та видаляти, зберігаючи декілька файлів конфігурації обʼєктів у теці та використовувати kubectl apply для рекурсивного створення та оновлення цих обʼєктів за потреби. Цей метод зберігає записи, зроблені у поточних обʼєктах, без злиття змін до файлів конфігурації обʼєкта. За допомогою kubectl diff також можна переглянути зміни, які буде внесено командою apply.

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

Встановіть kubectl.

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

Для перевірки версії введіть kubectl version.

Компроміси

Інструмент kubectl підтримує три види управління обʼєктами:

  • Імперативні команди
  • Імперативне конфігурування обʼєктів
  • Декларативне конфігурування обʼєктів

Див. Управління обʼєктами Kubernetes для обговорення переваг та недоліків кожного виду управління обʼєктами.

Огляд

Декларативна конфігурація обʼєктів потребує чіткого розуміння визначень та конфігурації обʼєктів Kubernetes. Прочитайте наступні документи, якщо ви ще цього не зробили:

Нижче подано визначення термінів, використаних у цьому документі:

  • файл конфігурації обʼєкта / файл конфігурації: Файл, який визначає конфігурацію для обʼєкта Kubernetes. Ця тема показує, як передавати файли конфігурації до kubectl apply. Файли конфігурації зазвичай зберігаються у системі контролю версій, такі як Git.
  • поточна конфігурація обʼєкта / поточна конфігурація: Поточні значення конфігурації обʼєкта, які використовуються кластером Kubernetes. Їх зберігають у сховищі кластера Kubernetes, зазвичай etcd.
  • декларативний записувач конфігурації / декларативний письменник: Особа або компонент програмного забезпечення, який вносить оновлення до поточного обʼєкта. Поточні записувачі, на які посилається ця тема, вносять зміни до файлів конфігурації обʼєктів та запускають kubectl apply для запису змін.

Як створити обʼєкти

Використовуйте kubectl apply, щоб створити всі обʼєкти, за винятком тих, що вже існують, які визначені у конфігураційних файлах у вказаній теці:

kubectl apply -f <тека>

Це встановлює анотацію kubectl.kubernetes.io/last-applied-configuration: '{...}' для кожного обʼєкта. Анотація містить вміст файлу конфігурації обʼєкта, який був використаний для створення обʼєкта.

Ось приклад файлу конфігурації обʼєкта:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Запустіть kubectl diff, щоб показати обʼєкт, який буде створено:

kubectl diff -f https://k8s.io/examples/application/simple_deployment.yaml

Створіть обʼєкт за допомогою kubectl apply:

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

Виведіть поточну конфігурацію за допомогою kubectl get:

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

Вивід показує, що анотація kubectl.kubernetes.io/last-applied-configuration була записана до поточної конфігурації та відповідає конфігураційному файлу:

kind: Deployment
metadata:
  annotations:
    # ...
    # Це json-представлення simple_deployment.yaml
    # Воно було створено за допомогою kubectl apply під час створення обʼєкта
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

Як оновити обʼєкти

Ви також можете використовувати kubectl apply, щоб оновити всі обʼєкти, визначені у теці, навіть якщо ці обʼєкти вже існують. Цей підхід виконує наступне:

  1. Встановлює поля, що зʼявляються у файлі конфігурації, у поточній конфігурації.
  2. Очищає поля, які були видалені з файлу конфігурації, у поточній конфігурації.
kubectl diff -f <тека>
kubectl apply -f <тека>

Ось приклад конфігураційного файлу:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Створіть обʼєкт за допомогою kubectl apply:

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

Виведіть поточну конфігурацію за допомогою kubectl get:

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

Вивід показує, що анотація kubectl.kubernetes.io/last-applied-configuration була записана до поточної конфігурації та відповідає конфігураційному файлу:

kind: Deployment
metadata:
  annotations:
    # ...
    # Це json-представлення simple_deployment.yaml
    # Воно було створено за допомогою kubectl apply під час створення обʼєкта
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

Напряму оновіть поле replicas у поточній конфігурації за допомогою kubectl scale. Для цього не використовується kubectl apply:

kubectl scale deployment/nginx-deployment --replicas=2

Виведіть поточну конфігурацію за допомогою kubectl get:

kubectl get deployment nginx-deployment -o yaml

Вивід показує, що поле replicas встановлено на 2, і анотація last-applied-configuration не містить поле replicas:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # Зверніть увагу, що анотація не містить replicas
    # тому що воно не було оновлено через apply
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  replicas: 2 # Встановлено за допомогою `kubectl scale`. Ігнорується `kubectl apply`.
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2 # Встановлено за допомогою `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...
    # ...
  # ...

Оновіть конфігураційний файл simple_deployment.yaml, щоб змінити образ з nginx:1.14.2 на nginx:1.16.1 та видалити поле minReadySeconds:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # оновіть образ
        ports:
        - containerPort: 80

Застосуйте зміни, внесені до конфігураційного файлу:

kubectl diff -f https://k8s.io/examples/application/update_deployment.yaml
kubectl apply -f https://k8s.io/examples/application/update_deployment.yaml

Виведіть поточну конфігурацію за допомогою kubectl get:

kubectl get -f https://k8s.io/examples/application/update_deployment.yaml -o yaml

Вивід показує наступні зміни в поточній конфігурації:

  • Поле replicas зберігає значення 2, встановлене за допомогою kubectl scale. Це можливо через його відсутність у конфігураційному файлі.
  • Поле image було оновлено на nginx:1.16.1 з nginx:1.14.2.
  • Анотація last-applied-configuration була оновлена новим образом.
  • Поле minReadySeconds було очищено.
  • Анотація last-applied-configuration більше не містить поле minReadySeconds.
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # Анотація містить оновлений образ nginx 1.16.1,
    # але не містить оновлення копій на 2
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
    # ...
spec:
  replicas: 2 # Встановлено за допомогою `kubectl scale`. Ігнорується `kubectl apply`.
  # minReadySeconds очищено за допомогою `kubectl apply`
  # ...
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # Встановлено за допомогою `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...
    # ...
  # ...

Як видалити обʼєкти

Існують два підходи до видалення обʼєктів, керованих за допомогою kubectl apply.

Ручне видалення обʼєктів за допомогою імперативної команди є рекомендованим підходом, оскільки він більш явно вказує на те, що видаляється, і менш ймовірно призводить до випадкового видалення чогось:

kubectl delete -f <filename>

Альтернатива: kubectl apply -f <directory> --prune

Як альтернативу kubectl delete, ви можете використовувати kubectl apply для ідентифікації обʼєктів, які мають бути видалені після видалення їх маніфестів з теки у локальній файловій системі.

У Kubernetes 1.31, доступні два режими очищення в kubectl apply:

  • Очищення на основі allowlist: Цей режим існує з моменту kubectl v1.5, але все ще знаходиться на етапі альфа-тестування через проблеми з використанням, коректністю і продуктивністю його дизайну. Режим на основі ApplySet призначений для заміни його.
  • Очищення на основі ApplySet: apply set — це обʼєкт на стороні сервера (типово, Secret), який kubectl може використовувати для точного та ефективного відстеження членства в наборі під час операцій apply. Цей режим був введений у альфа-версії в kubectl v1.27 як заміна очищенню на основі allowlist.

<div class="feature-state-notice feature-alpha">
  <span class="feature-state-name">СТАН ФУНКЦІОНАЛУ:</span> 
  <code>Kubernetes v1.5 [alpha]</code>
</div>

Щоб використовувати очищення на основі allowlist, додайте наступні прапорці до свого виклику kubectl apply:

  • --prune: Видалити попередньо застосовані обʼєкти, які не є у наборі, що передані поточному виклику.
  • --prune-allowlist: Список груп-версій-типів (GVK, group-version-kind), які розглядаються для очищення. Цей прапорець є необовʼязковим, але настійно рекомендується, оскільки його стандартне значення є частковим списком обʼєктів з просторами імен та областями застосування, що може призвести до несподіваних результатів.
  • --selector/-l: Використовуйте селектор міток для обмеження набору обʼєктів, обраних для очищення. Цей прапорець є необовʼязковим, але настійно рекомендується.
  • --all: використовуйте замість --selector/-l, щоб явно вибрати всі попередньо застосовані обʼєкти відповідних типів, які знаходяться у списку дозволених.

Очищення на основі allowlist запитує API-сервер щодо всіх обʼєктів затверджених GVK, які відповідають заданим міткам (якщо є), і намагається зіставити конфігурації активних обʼєктів, отриманих в результаті, з файлами маніфестів обʼєктів. Якщо обʼєкт відповідає запиту і він не має маніфесту в теці, і має анотацію kubectl.kubernetes.io/last-applied-configuration, він видаляється.

kubectl apply -f <directory> --prune -l <labels> --prune-allowlist=<gvk-list>

<div class="feature-state-notice feature-alpha">
  <span class="feature-state-name">СТАН ФУНКЦІОНАЛУ:</span> 
  <code>Kubernetes v1.27 [alpha]</code>
</div>

Для використання очищення на основі ApplySet встановіть змінну середовища KUBECTL_APPLYSET=true, і додайте наступні прапорці до свого виклику kubectl apply:

  • --prune: Видалити попередньо застосовані обʼєкти, які не є у наборі, що передані поточному виклику.
  • --applyset: Назва обʼєкта, який kubectl може використовувати для точного та ефективного відстеження членства в наборі під час операцій apply.
KUBECTL_APPLYSET=true kubectl apply -f <directory> --prune --applyset=<name>

Типово тип батьківського обʼєкта ApplySet, що використовується, — Secret. Однак також можуть бути використані ConfigMaps у форматі: --applyset=configmaps/<name>. При використанні Secret або ConfigMap, kubectl створить обʼєкт, якщо він ще не існує.

Також можливе використання власних ресурсів як батьківських обʼєктів ApplySet. Для цього позначте міткою Custom Resource Definition (CRD), що визначає ресурс, який ви хочете використовувати з наступним: applyset.kubernetes.io/is-parent-type: true. Потім створіть обʼєкт, який ви хочете використовувати як батьківський обʼєкт ApplySet (kubectl цього не робить автоматично для Custom Resource). Нарешті, посилайтеся на цей обʼєкт у прапорці applyset таким чином: --applyset=<resource>.<group>/<name> (наприклад, widgets.custom.example.com/widget-name).

Під час очищення на основі ApplySet kubectl додає мітку applyset.kubernetes.io/part-of=<parentID> до кожного обʼєкта в наборі, перш ніж вони будуть надіслані на сервер. З метою продуктивності він також збирає список типів ресурсів і просторів імен, які включаються у набір, і додає ці дані в анотації поточного батьківського обʼєкта. В кінеці операції apply, він запитує API-сервер щодо обʼєктів цих типів в цих просторах імен (або в областях кластера, якщо це доречно), які належать до набору, визначеного міткою applyset.kubernetes.io/part-of=<parentID>.

Застереження та обмеження:

  • Кожен обʼєкт може бути членом не більше одного набору.
  • Прапорець --namespace є обовʼязковим при використанні будь-якого обʼєкта з простором імен, включаючи типово Secret. Це означає, що ApplySets, які охоплюють кілька просторів імен, повинні використовувати кластерний обʼєкт з кореневою текою.
  • Щоб безпечно використовувати очищення на основі ApplySet з декількома теками, використовуйте унікальне імʼя ApplySet для кожного.

Як переглянути обʼєкт

Ви можете використовувати kubectl get з -o yaml, щоб переглянути конфігурацію поточного обʼєкта:

kubectl get -f <filename|url> -o yaml

Як apply обчислює різницю та обʼєднує зміни

Коли kubectl apply оновлює поточну конфігурацію обʼєкта, він робить це, надсилаючи запит на патч до API-сервера. Патч визначає оновлення для конкретних полів конфігурації живого обʼєкта. Команда kubectl apply обчислює цей запит на патч за допомогою файлу конфігурації, поточної конфігурації та анотації last-applied-configuration, збереженої в поточній конфігурації.

Обчислення злиття патчів

Команда kubectl apply записує вміст файлу конфігурації до анотації kubectl.kubernetes.io/last-applied-configuration. Вона використовується для ідентифікації полів, які були видалені з файлу конфігурації та які потрібно видалити з поточної конфігурації. Ось кроки, які використовуються для обчислення того, які поля потрібно видалити або встановити:

  1. Обчислити поля для видалення. Це поля, які присутні в last-applied-configuration та відсутні в файлі конфігурації.
  2. Обчислити поля для додавання або встановлення. Це поля, які присутні в файлі конфігурації, значення яких не відповідають поточній конфігурації.

Ось приклад. Припустимо, що це файл конфігурації для обʼєкта типу Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # оновіть образ
        ports:
        - containerPort: 80

Також, припустимо, що це поточна конфігурація для того самого обʼєкта типу Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # Зауважте, що анотація не містить поля replicas,
    # оскільки воно не було оновлено через apply
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
  # ...
spec:
  replicas: 2 # вказано через scale
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

Ось обчислення злиття, які виконає kubectl apply:

  1. Обчислення полів для видалення, отримуючи значення з last-applied-configuration і порівнюючи їх зі значеннями у файлі конфігурації. Очищення полів, які явно встановлені ​​на null у локальному файлі конфігурації обʼєкта, незалежно від того, чи вони зʼявляються в анотації last-applied-configuration. У цьому прикладі minReadySeconds зʼявляється в анотації last-applied-configuration, але не зʼявляється у файлі конфігурації. Дія: Прибрати minReadySeconds з поточної конфігурації.
  2. Обчислення полів для встановлення, отримуючи значення з файлу конфігурації та порівнюючи їх зі значеннями у поточній конфігурації. У цьому прикладі значення image у файлі конфігурації не відповідає значенню у поточній конфігурації. Дія: Встановити значення image у поточній конфігурації.
  3. Встановити анотацію last-applied-configuration, щоб вона відповідала значенню файлу конфігурації.
  4. Обʼєднати результати з 1, 2, 3 у єдиний запит на патч до API-сервера.

Ось поточна конфігурація, яка є результатом злиття:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # Анотація містить оновлений образ nginx 1.16.1,
    # але не містить оновлення реплік до 2
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment",
      "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
      "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
      "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}      
    # ...
spec:
  selector:
    matchLabels:
      # ...
      app: nginx
  replicas: 2 # Встановлено за допомогою `kubectl scale`.  Ігнорується `kubectl apply`.
  # minReadySeconds очищено за допомогою `kubectl apply`
  # ...
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # Встановлено за допомогою `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

Як зливаються поля різних типів

Як певне поле в конфігураційному файлі зливається з поточною конфігурацією залежить від типу поля. Існують кілька типів полів:

  • primitive: Поле типу рядок, ціле число або логічне значення. Наприклад, image та replicas є полями примітивів. Дія: Замінити.

  • map, також відомий як object: Поле типу map або комплексний тип, який містить підполя. Наприклад, labels, annotations, spec та metadata — це всі map. Дія: Злити елементи або підполя.

  • list: Поле, яке містить список елементів, які можуть бути або типами primitive, або map. Наприклад, containers, ports та args є списками. Дія: Варіюється.

Коли kubectl apply оновлює map або list, він зазвичай не замінює все поле цілком, а замість цього оновлює окремі піделементи. Наприклад, при злитті spec в Deployment, весь spec не замінюється. Замість цього порівнюються та зливаються підполя spec, такі як replicas.

Злиття змін для полів типу primitive

Поля primitive замінюються або очищаються.

Поле в конфігураційному файлі обʼєктаПоле в поточній конфігурації обʼєктаПоле в останній застосованій конфігураціїДія
ТакТак-Встановити поточне значення з конфігураційного файлу.
ТакНі-Встановити поточне значення з локального конфігураційного файлу.
Ні-ТакОчистити з поточної конфігурації.
Ні-НіНічого не робити. Зберегти значення поточного обʼєкта.

Злиття змін у полях типу map

Поля, які є map, зливаються шляхом порівняння кожного з підполів або елементів map:

Ключ в конфігураційному файлі обʼєктаКлюч у поточній конфігурації обʼєктаПоле в останній застосованій конфігураціїДія
ТакТак-Порівняти значення підполів.
ТакНі-Встановити поточне значення з локального конфігураційного файлу.
Ні-ТакВидалити з поточної конфігурації.
Ні-НіНічого не робити. Зберігти значення поточного обʼєкта.

Злиття змін для полів типу list

Злиття змін у list використовує одну з трьох стратегій:

  • Заміна list, якщо всі його елементи є primitive.
  • Злиття окремих елементів у списку комплексних елементів.
  • Злиття list елементів primitive.

Вибір стратегії залежить від конкретного поля.

Заміна list, якщо всі його елементи є primitive

Такий список трактується так само як і поле primitive. Замініть або видаліть весь список. Це зберігає порядок.

Приклад: Використовуйте kubectl apply, щоб оновити поле args контейнера в Podʼі. Це встановлює значення args в поточній конфігурації на значення у файлі конфігурації. Будь-які елементи args, які раніше були додані до поточної конфігурації, втрачаються. Порядок елементів args, визначених у файлі конфігурації, зберігається у поточній конфігурації.

# Значення last-applied-configuration
    args: ["a", "b"]

# Значення файлу конфігурації
    args: ["a", "c"]

# Поточна конфігурація
    args: ["a", "b", "d"]

# Результат після злиття
    args: ["a", "c"]

Пояснення: Злиття використовує значення файлу конфігурації як нове значення списку.

Злиття окремих елементів списку комлексних елементів:

Трактуйте список як map, а конкретне поле кожного елемента як ключ. Додавайте, видаляйте або оновлюйте окремі елементи. Це не зберігає порядок.

Ця стратегія злиття використовує спеціальний теґ на кожному полі, який називається patchMergeKey. patchMergeKey визначено для кожного поля в коді Kubernetes: types.go При злитті списку map, поле, вказане як patchMergeKey для певного елемента, використовується як ключ map для цього елемента.

Приклад: Використайте kubectl apply, щоб оновити поле containers у PodSpec. Це злиття списку, ніби він був map, де кожен елемент має ключ name.

# Значення last-applied-configuration
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-a # ключ: nginx-helper-a; буде видалено у результаті
      image: helper:1.3
    - name: nginx-helper-b # ключ: nginx-helper-b; буде збережено
      image: helper:1.3

# Значення файлу конфігурації
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-b
      image: helper:1.3
    - name: nginx-helper-c # ключ: nginx-helper-c; буде додано у результаті
      image: helper:1.3

# Поточна конфігурація
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-a
      image: helper:1.3
    - name: nginx-helper-b
      image: helper:1.3
      args: ["run"] # Поле буде збережено
    - name: nginx-helper-d # ключ: nginx-helper-d; буде збережено
      image: helper:1.3

# Результат після злиття
    containers:
    - name: nginx
      image: nginx:1.16
      # Елемент nginx-helper-a був видалений
    - name: nginx-helper-b
      image: helper:1.3
      args: ["run"] # Поле було збережено
    - name: nginx-helper-c # Елемент був доданий
      image: helper:1.3
    - name: nginx-helper-d # Елемент був ігнорований
      image: helper:1.3

Пояснення:

  • Контейнер з імʼям "nginx-helper-a" був видалений, оскільки жодного контейнера з іменем "nginx-helper-a" не знайдено у файлі конфігурації.
  • Контейнер з імʼям "nginx-helper-b" зберіг зміни у args в поточній конфігурації. kubectl apply зміг ідентифікувати, що "nginx-helper-b" у поточній конфігурації був тим самим "nginx-helper-b", що й у файлі конфігурації, навіть якщо їхні поля мали різні значення (немає args у файлі конфігурації). Це тому, що значення поля patchMergeKey (name) було ідентичним у обох.
  • Контейнер з імʼям "nginx-helper-c" був доданий, оскільки жодного контейнера з таким імʼям не було у поточній конфігурації, але один з таким імʼям був у файлі конфігурації.
  • Контейнер з імʼям "nginx-helper-d" був збережений, оскільки жодного елемента з таким імʼям не було в last-applied-configuration.

Злиття списку елементів типу primitive

Зараз, починаючи з Kubernetes 1.5, злиття списків елементів типу primitive не підтримується.

Стандартні значення полів

Сервер API встановлює в певні поля станадартні значення у поточній конфігурації, якщо вони не вказані при створенні обʼєкта.

Ось файл конфігурації для обʼєкта Deployment. У файлі не вказано strategy:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Створіть обʼєкт, використовуючи kubectl apply:

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

Виведіть поточну конфігурацію, використовуючи kubectl get:

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

Вивід показує, що сервер API встановив в деякі поля стандартні значення у поточній конфігурації. Ці поля не були вказані в файлі конфігурації.

apiVersion: apps/v1
kind: Deployment
# ...
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  replicas: 1 # станадратне значення додане apiserver
  strategy:
    rollingUpdate: # станадратне значення додане apiserver - походить з strategy.type
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate # станадратне значення додане apiserver
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        imagePullPolicy: IfNotPresent # станадратне значення додане apiserver
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP # станадратне значення додане apiserver
        resources: {} # станадратне значення додане apiserver
        terminationMessagePath: /dev/termination-log # станадратне значення додане apiserver
      dnsPolicy: ClusterFirst # станадратне значення додане apiserver
      restartPolicy: Always # станадратне значення додане apiserver
      securityContext: {} # станадратне значення додане apiserver
      terminationGracePeriodSeconds: 30 # станадратне значення додане apiserver
# ...

У запиті на патч, поля, які мають станаддартні значення, не перезаписуються, якщо вони явно не очищені як частина запиту на патч. Це може призвести до неочікуваної поведінки для полів, які мають стнадартні значення на основі значень інших полів. Після зміни інших полів значення, які мають стандартні значення з них, не будуть оновлені, якщо їх не явно очищено.

З цієї причини рекомендується, щоб певні поля, стандартні значення яких встановлює сервер, були явно визначені в файлі конфігурації обʼєкта, навіть якщо бажані значення відповідають станадартним значенням сервера. Це полегшить розпізнавання суперечливих значень, які не будуть перезаписані сервером на станадартні значення.

Приклад:

# last-applied-configuration
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# конфігураційний файл
spec:
  strategy:
    type: Recreate # оновленне значення
  template:
    metadata:
      labels:
        app:

 nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# поточна конфігурація
spec:
  strategy:
    type: RollingUpdate # встановлене типове значення
    rollingUpdate: # встановлене типове значення отримане з type
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# результат після злиття - ПОМИЛКА!
spec:
  strategy:
    type: Recreate # оновленне значення: несумісне з rollingUpdate
    rollingUpdate: # встановлене типове значення: несумісне з "type: Recreate"
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Пояснення:

  1. Користувач створює Deployment без визначення strategy.type.
  2. Сервер встановлює станадартне значення для strategy.type на RollingUpdate тв стандартне значення для strategy.rollingUpdate.
  3. Користувач змінює strategy.type на Recreate. Значення strategy.rollingUpdate залишаються стандартними значеннями, хоча сервер очікує, що вони будуть очищені. Якщо значення strategy.rollingUpdate були визначені спочатку в файлі конфігурації, було б більш зрозуміло, що їх потрібно було б видалити.
  4. Оновлення не вдається через те, що strategy.rollingUpdate не очищено. Поле strategy.rollingupdate не може бути визначено з strategy.type Recreate.

Рекомендація: Ці поля слід явно визначити в файлі конфігурації обʼєкта:

  • Селектори та мітки PodTemplate для робочих навантажень, таких як Deployment, StatefulSet, Job, DaemonSet, ReplicaSet та ReplicationController.
  • Стратегія розгортання Deployment.

Як очистити стандартні поля встановлені сервером або поля, встановлені іншими записувачами

Поля, які не зʼявляються у файлі конфігурації, можна очистити, встановивши їх значення в null, а потім застосувати файл конфігурації. Для полів, стандартні значення яких встановлено сервером, це спричинить перезапис цих значень.

Як змінити власника поля між файлом конфігурації та прямими імперативними записувачами

Ці методи — єдиний вірний спосіб змінювати окреме поле обʼєкта:

  • Використовуйте kubectl apply.
  • Пишіть безпосередньо в поточну конфігурацію без змін файлу конфігурації: наприклад, використовуйте kubectl scale.

Зміна власника з прямого імперативного записувача на файл конфігурації

Додайте поле до файлу конфігурації. Для цього поля припиніть прямі оновлення поточної конфігурації, які не проходять через kubectl apply.

Зміна власника з файлу конфігурації на безпосереднього імперативного записувача

Починаючи з Kubernetes 1.5, зміна власника поля з файлу конфігурації на імперативного запусувача вимагає виконання наступних кроків:

  • Видаліть поле з файлу конфігурації.
  • Видаліть поле з анотації kubectl.kubernetes.io/last-applied-configuration на поточному обʼєкті.

Зміна методів управління

Обʼєктами Kubernetes слід керувати за допомогою лише одного методу одночасно. Перехід з одного методу на інший можливий, але це вимагає ручної обробки.

Міграція з управління імперативними командами до декларативної конфігурації обʼєктів

Міграція з управління імперативними командами до декларативної конфігурації обʼєктів включає кілька ручних кроків:

  1. Експортуйте поточний обʼєкт у локальний файл конфігурації:

    kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
    
  2. Видаліть вручну поле status з файлу конфігурації.

  3. Встановіть анотацію kubectl.kubernetes.io/last-applied-configuration на обʼєкті:

    kubectl replace --save-config -f <kind>_<name>.yaml
    
  4. Змініть процеси так, щоб вони використовували виключно kubectl apply для керування обʼєктом.

Міграція з імперативної конфігурації обʼєктів до декларативної конфігурації обʼєктів

  1. Встановіть анотацію kubectl.kubernetes.io/last-applied-configuration на обʼєкті:

    kubectl replace --save-config -f <kind>_<name>.yaml
    
  2. Змініть процеси так, щоб використовували kubectl apply виключно для керування обʼєктом.

Визначення селекторів контролера та міток PodTemplate

Рекомендований підхід — це визначення єдиного, незмінного підпису PodTemplate, який використовується тільки селектором контролера без іншого семантичного значення.

Приклад:

selector:
  matchLabels:
      controller-selector: "apps/v1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "apps/v1/deployment/nginx"

Що далі

5.2 - Декларативне керування обʼєктами Kubernetes за допомогою Kustomize

Kustomize — це окремий інструмент для налаштування обʼєктів Kubernetes за допомогою файлу кастомізації.

Починаючи з версії 1.14, Kubectl також підтримує керування обʼєктами Kubernetes за допомогою файлу кастомізації. Для перегляду ресурсів, знайдених у теці, що містить файл кастомізації, виконайте наступну команду:

kubectl kustomize <kustomization_directory>

Щоб застосувати ці ресурси, запустіть kubectl apply з прапорцем --kustomize або -k:

kubectl apply -k <kustomization_directory>

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

Встановіть kubectl.

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

Для перевірки версії введіть kubectl version.

Огляд Kustomize

Kustomize — це інструмент для налаштування конфігурацій Kubernetes. Він має наступні функції для керування файлами конфігурації застосунків:

  • генерація ресурсів з інших джерел
  • встановлення наскрізних полів для ресурсів
  • складання та налаштування колекцій ресурсів

Генерування ресурсів

ConfigMaps та Secrets зберігають конфігураційні або конфіденційні дані, які використовуються іншими обʼєктами Kubernetes, наприклад, Podʼами. Джерело істини для ConfigMaps або Secrets є зазвичай зовнішнім до кластера, наприклад як файл .properties або файл ключів SSH. Kustomize має secretGenerator та configMapGenerator, які створюють Secret та ConfigMap з файлів або літералів.

configMapGenerator

Щоб створити ConfigMap з файлу, додайте запис до списку files в configMapGenerator. Ось приклад створення ConfigMap з елементом даних з файлу .properties:

# Створіть файл application.properties
cat <<EOF >application.properties
FOO=Bar
EOF

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

Згенерований ConfigMap можна переглянути за допомогою наступної команди:

kubectl kustomize ./

Згенерований ConfigMap виглядає так:

apiVersion: v1
data:
  application.properties: |
    FOO=Bar    
kind: ConfigMap
metadata:
  name: example-configmap-1-8mbdf7882g

Щоб створити ConfigMap з файлу оточення (env), додайте запис до списку envs в configMapGenerator. Ось приклад створення ConfigMap з елементом даних з файлу .env:

# Створіть файл .env
cat <<EOF >.env
FOO=Bar
EOF

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
  envs:
  - .env
EOF

Згенерований ConfigMap можна переглянути за допомогою наступної команди:

kubectl kustomize ./

Згенерований ConfigMap виглядає так:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-1-42cfbf598f

ConfigMaps також можна створювати з літеральних пар ключ-значення. Щоб створити ConfigMap з літеральною парою ключ-значення, додайте запис до списку literals в configMapGenerator. Ось приклад створення ConfigMap з елементом даних з пари ключ-значення:

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-2
  literals:
  - FOO=Bar
EOF

Згенерований ConfigMap можна перевірити за допомогою наступної команди:

kubectl kustomize ./

Згенерований ConfigMap виглядає так:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  name: example-configmap-2-g2hdhfc6tk

Щоб використовувати згенерований ConfigMap у Deployment, посилайтеся на нього за іменем configMapGenerator. Kustomize автоматично замінить це імʼя згенерованим.

Ось приклад Deployment, що використовує згенерований ConfigMap:

# Створіть файл application.properties
cat <<EOF >application.properties
FOO=Bar
EOF

cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app
        volumeMounts:
        - name: config
          mountPath: /config
      volumes:
      - name: config
        configMap:
          name: example-configmap-1
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
  files:
  - application.properties
EOF

Згенеруйте ConfigMap та Deployment:

kubectl kustomize ./

Згенерований Deployment буде посилатися на згенерований ConfigMap за іменем:

apiVersion: v1
data:
  application.properties: |
    FOO=Bar    
kind: ConfigMap
metadata:
  name: example-configmap-1-g4hk9g2ff8
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: my-app
  name: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - image: my-app
        name: app
        volumeMounts:
        - mountPath: /config
          name: config
      volumes:
      - configMap:
          name: example-configmap-1-g4hk9g2ff8
        name: config

secretGenerator

Ви можете створювати Secrets з файлів або літеральних пар ключ-значення. Щоб створити Secret з файлу, додайте запис до списку files в secretGenerator. Ось приклад створення Secret з елементом даних з файлу:

# Створіть файл password.txt
cat <<EOF >./password.txt
username=admin
password=secret
EOF

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

Згенерований Secret має наступний вигляд:

apiVersion: v1
data:
  password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
  name: example-secret-1-t2kt65hgtb
type: Opaque

Щоб створити Secret з літеральною парою ключ-значення, додайте запис до списку literals в secretGenerator. Ось приклад створення Secret з елементом даних з пари ключ-значення:

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-2
  literals:
  - username=admin
  - password=secret
EOF

Згенерований Secret має наступний вигляд:

apiVersion: v1
data:
  password: c2VjcmV0
  username: YWRtaW4=
kind: Secret
metadata:
  name: example-secret-2-t52t6g96d8
type: Opaque

Podʼібно до ConfigMaps, створені Secrets можна використовувати у Deployment, посилаючись на імʼя secretGenerator:

# Створіть файл password.txt
cat <<EOF >./password.txt
username=admin
password=secret
EOF

cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app
        volumeMounts:
        - name: password
          mountPath: /secrets
      volumes:
      - name: password
        secret:
          secretName: example-secret-1
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
  files:
  - password.txt
EOF

generatorOptions

Згенеровані ConfigMaps та Secrets мають суфікс хешу вмісту, що додається. Це забезпечує створення нового ConfigMap або Secret при зміні вмісту. Щоб вимкнути поведінку додавання суфікса, можна використовувати generatorOptions. Крім того, також можливо вказати загальні опції для згенерованих ConfigMaps та Secrets.

cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-3
  literals:
  - FOO=Bar
generatorOptions:
  disableNameSuffixHash: true
  labels:
    type: generated
  annotations:
    note: generated
EOF

Виконайте kubectl kustomize ./, щоб переглянути згенерований ConfigMap:

apiVersion: v1
data:
  FOO: Bar
kind: ConfigMap
metadata:
  annotations:
    note: generated
  labels:
    type: generated
  name: example-configmap-3

Встановлення загальних наскрізних полів

Досить поширене явище — встановлення загальних наскрізних полів для всіх ресурсів Kubernetes у проєкті. Деякі випадки встановлення загальних полів:

  • встановлення одного й того ж простору імен для всіх ресурсів
  • додавання одного й того ж префікса чи суфікса до імені
  • додавання одного й того ж набору міток
  • додавання одного й того ж набору анотацій

Ось приклад:

# Створіть deployment.yaml
cat <<EOF >./deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
EOF

cat <<EOF >./kustomization.yaml
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
commonLabels:
  app: bingo
commonAnnotations:
  oncallPager: 800-555-1212
resources:
- deployment.yaml
EOF

Виконайте kubectl kustomize ./, щоб переглянути, як ці поля встановлені у ресурсі Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    oncallPager: 800-555-1212
  labels:
    app: bingo
  name: dev-nginx-deployment-001
  namespace: my-namespace
spec:
  selector:
    matchLabels:
      app: bingo
  template:
    metadata:
      annotations:
        oncallPager: 800-555-1212
      labels:
        app: bingo
    spec:
      containers:
      - image: nginx
        name: nginx

Компонування та кастомізація ресурсів

Часто в проєкті складають набір Ресурсів та керують ними всередині одного файлу чи теки. Kustomize пропонує компонування Ресурсів з різних файлів та застосування патчів чи інших налаштувань до них.

Компонування

Kustomize підтримує компонування різних ресурсів. Поле resources у файлі kustomization.yaml визначає список ресурсів, які слід включити в конфігурацію. Встановіть шлях до файлу конфігурації ресурсу у списку resources. Ось приклад додавання до конфігурації застосунку NGINX, що складається з Deployment та Service:

# Створіть файл deployment.yaml
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# Створіть файл service.yaml
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF

# Створіть файл kustomization.yaml та скомпонуйте їх
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

Ресурси з kubectl kustomize ./ містять обʼєкти як Deployment, так і Service.

Кастомізація

Патчі можуть бути використані для застосування різних кастомізацій до Ресурсів. Kustomize підтримує різні механізми патчінгу через patchesStrategicMerge та patchesJson6902. patchesStrategicMerge — це список шляхів до файлів. Кожен файл повинен бути розвʼязаний до стратегічного обʼєднання патчів. Імена всередині патчів повинні відповідати іменам Ресурсів, які вже завантажені. Рекомендується використовувати невеликі патчі, які виконують одну задачу. Наприклад, створіть один патч для збільшення кількості реплік у Deployment та інший патч для встановлення обмеження памʼяті.

# Створіть файл deployment.yaml
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# Створіть патч increase_replicas.yaml
cat <<EOF > increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
EOF

# Створіть інший патч set_memory.yaml
cat <<EOF > set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  template:
    spec:
      containers:
      - name: my-nginx
        resources:
          limits:
            memory: 512Mi
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesStrategicMerge:
- increase_replicas.yaml
- set_memory.yaml
EOF

Виконайте kubectl kustomize ./, щоб переглянути Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: nginx
        name: my-nginx
        ports:
        - containerPort: 80
        resources:
          limits:
            memory: 512Mi

Не всі Ресурси чи поля підтримують стратегічні обʼєднувальні патчі. Для підтримки зміни довільних полів у довільних Ресурсах, Kustomize пропонує застосування JSON патчів через patchesJson6902. Для знаходження правильного Ресурсу для Json патчу, потрібно вказати групу, версію, тип та імʼя цього Ресурсу у kustomization.yaml. Наприклад, збільшення кількості реплік у обʼєкті Deployment також можна зробити через patchesJson6902.

# Створіть файл deployment.yaml
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# Створіть json патч
cat <<EOF > patch.yaml
- op: replace
  path: /spec/replicas
  value: 3
EOF

# Створіть kustomization.yaml
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml

patchesJson6902:
- target:
    group: apps
    version: v1
    kind: Deployment
    name: my-nginx
  path: patch.yaml
EOF

Виконайте kubectl kustomize ./, щоб побачити, що поле replicas оновлене:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: nginx
        name: my-nginx
        ports:
        - containerPort: 80

Крім патчів, Kustomize також пропонує налаштування контейнерних образів або введення значень полів з інших обʼєктів в контейнери без створення патчів. Наприклад, ви можете змінити використаний образ усередині контейнерів, вказавши новий образ у полі images у kustomization.yaml.

cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
images:
- name: nginx
  newName: my.image.registry/nginx
  newTag: 1.4.0
EOF

Виконайте kubectl kustomize ./, щоб переглянути, що використовується оновлений образ:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - image: my.image.registry/nginx:1.4.0
        name: my-nginx
        ports:
        - containerPort: 80

Іноді застосунок, що працює у Podʼі, може потребувати використання значень конфігурації з інших обʼєктів. Наприклад, Pod з обʼєкту Deployment повинен читати відповідне імʼя Service з Env або як аргумент команди. Оскільки імʼя Service може змінюватися через namePrefix або nameSuffix, додавання імені Service у командний аргумент не рекомендується. Для цього використовується Kustomize, що вводить імʼя Service в контейнери через vars.

# Створіть файл deployment.yaml (взявши в лапки роздільник документа)
cat <<'EOF' > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        command: ["start", "--host", "$(MY_SERVICE_NAME)"]
EOF

# Створіть файл service.yaml
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF

cat <<EOF >./kustomization.yaml
namePrefix: dev-
nameSuffix: "-001"

resources:
- deployment.yaml
- service.yaml

vars:
- name: MY_SERVICE_NAME
  objref:
    kind: Service
    name: my-nginx
    apiVersion: v1
EOF

Виконайте kubectl kustomize ./, щоб побачити, що імʼя Service, введене у контейнери, — це dev-my-nginx-001:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dev-my-nginx-001
spec:
  replicas: 2
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - command:
        - start
        - --host
        - dev-my-nginx-001
        image: nginx
        name: my-nginx

Base та Overlay

У Kustomize існують концепції base та overlay. Base — це тека з файлом kustomization.yaml, яка містить набір ресурсів та повʼязані налаштування. Base може бути як локальною текою, так і текою з віддаленого репозиторію, якщо присутній файл kustomization.yaml. Overlay — це тека з файлом kustomization.yaml, яка посилається на інші теки з налаштуваннями як на свої base компоненти. Base не знає про overlay і може використовуватися в кількох overlay. Overlay може мати кілька base та компонувати всі ресурси з base, а також мати власні налаштування поверх.

Ось приклад base:

# Створіть теку для зберігання base
mkdir base
# Створіть файл base/deployment.yaml
cat <<EOF > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
EOF

# Створіть файл base/service.yaml
cat <<EOF > base/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
EOF
# Створіть файл base/kustomization.yaml
cat <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF

Цю базу можна використовувати в кількох overlay. Ви можете додавати різний namePrefix або інші загальні поля у різних overlay. Ось два overlay, які використовують один і той же base.

mkdir dev
cat <<EOF > dev/kustomization.yaml
resources:
- ../base
namePrefix: dev-
EOF

mkdir prod
cat <<EOF > prod/kustomization.yaml
resources:
- ../base
namePrefix: prod-
EOF

Як застосувати/переглядати/видаляти обʼєкти використовуючи Kustomize

Використовуйте --kustomize або -k у командах kubectl, щоб визначити Ресурси, які керуються kustomization.yaml. Зверніть увагу, що -k повинен посилатися на теку з налаштуваннями kustomization, наприклад,

kubectl apply -k <тека kustomization>/

Враховуючи наступний kustomization.yaml,

# Створіть файл deployment.yaml
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
EOF

# Створіть файл kustomization.yaml
cat <<EOF >./kustomization.yaml
namePrefix: dev-
commonLabels:
  app: my-nginx
resources:
- deployment.yaml
EOF

Виконайте наступну команду, щоб застосувати обʼєкт Deployment dev-my-nginx:

> kubectl apply -k ./
deployment.apps/dev-my-nginx створено

Виконайте одну з наступних команд, щоб переглянути обʼєкт Deployment dev-my-nginx:

kubectl get -k ./
kubectl describe -k ./

Виконайте наступну команду, щоб порівняти обʼєкт Deployment dev-my-nginx зі станом, в якому буде кластер, якщо маніфест буде застосований:

kubectl diff -k ./

Виконайте наступну команду, щоб видалити обʼєкт Deployment dev-my-nginx:

> kubectl delete -k ./
deployment.apps "dev-my-nginx" deleted

Перелік елементів Kustomize

ПолеТипПояснення
namespacestringдодає простір імен до всіх ресурсів
namePrefixstringце значення додається до імен всіх ресурсів
nameSuffixstringце значення додається до кінця імен всіх ресурсів
commonLabelsmap[string]stringмітки, що додаються до всіх ресурсів і селекторів
commonAnnotationsmap[string]stringанотації, що додаються до всіх ресурсів
resources[]stringкожний елемент цього списку повинен посилатися на наявний файл конфігурації ресурсів
configMapGenerator[]ConfigMapArgsКожний елемент цього списку генерує ConfigMap
secretGenerator[]SecretArgsКожний елемент цього списку генерує Secret
generatorOptionsGeneratorOptionsМодифікує поведінку всіх генераторів ConfigMap і Secret
bases[]stringКожний елемент цього списку повинен посилатися на теку, що містить файл kustomization.yaml
patchesStrategicMerge[]stringКожний елемент цього списку повинен посилатися на стратегічне патч злиття обʼєкта Kubernetes
patchesJson6902[]PatchКожний елемент цього списку повинен посилатися на обʼєкт Kubernetes та Json Patch
vars[]VarКожний елемент призначений для отримання тексту з поля одного ресурсу
images[]ImageКожний елемент призначений для зміни імені, тегів і/або дайджесту для одного образу без створення патчів
configurations[]stringКожний елемент цього списку повинен посилатися на файл, що містить конфігурації перетворювача Kustomize
crds[]stringКожний елемент цього списку повинен посилатися на файл визначення OpenAPI для типів Kubernetes

Що далі

5.3 - Керування обʼєктами Kubernetes за допомогою імперативних команд

Обʼєкти Kubernetes можна швидко створювати, оновлювати та видаляти безпосередньо за допомогою імперативних команд, вбудованих в інструмент командного рядка kubectl. Цей документ пояснює, як організовано ці команди та як їх використовувати для керування поточними обʼєктами.

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

Встановіть kubectl.

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

Для перевірки версії введіть kubectl version.

Компроміси

Інструмент kubectl підтримує три види управління обʼєктами:

  • Імперативні команди
  • Імперативне конфігурування обʼєктів
  • Декларативне конфігурування обʼєктів

Див. Управління обʼєктами Kubernetes для обговорення переваг та недоліків кожного виду управління обʼєктами.

Як створювати обʼєкти

Інструмент kubectl підтримує команди, що базуються на дії, для створення деяких найпоширеніших типів обʼєктів. Команди названі так, щоб їх можна було впізнати користувачам, що не знайомі з типами обʼєктів Kubernetes.

  • run: Створює новий Pod для запуску контейнера.
  • expose: Створює новий обʼєкт Service для балансування трафіку між Pod.
  • autoscale: Створює новий обʼєкт Autoscaler для автоматичного горизонтального масштабування контролера, наприклад, Deployment.

Інструмент kubectl також підтримує команди створення, що базуються на типі обʼєкта. Ці команди підтримують більше типів обʼєктів і більш явно вказують на їх наміри, але вимагають, щоб користувачі знали типи обʼєктів, які вони мають намір створити.

  • create <типобʼєкта> [<підтип>] <імʼяекземпляра>

Деякі типи обʼєктів мають підтипи, які можна вказати в команді create. Наприклад, обʼєкт Service має кілька підтипів, включаючи ClusterIP, LoadBalancer та NodePort. Ось приклад створення Service з підтипом NodePort:

kubectl create service nodeport <myservicename>

У попередньому прикладі команда create service nodeport викликається як підкоманда команди create service.

Ви можете використовувати прапорець -h, щоб знайти аргументи та прапорці, які підтримуються підкомандою:

kubectl create service nodeport -h

Як оновлювати обʼєкти

Команда kubectl підтримує команди, що базуються на дії, для деяких загальних операцій оновлення. Ці команди названі так, щоб користувачі, які не знайомі з обʼєктами Kubernetes, могли виконувати оновлення, не знаючи конкретних полів, які потрібно встановити:

  • scale: Горизонтально масштабувати контролер для додавання або видалення Podʼів шляхом оновлення кількості реплік контролера.
  • annotate: Додати або видалити анотацію з обʼєкта.
  • label: Додати або видалити мітку з обʼєкта.

Команда kubectl також підтримує команди оновлення, що базуються на аспекті обʼєкта. Встановлення цього аспекту може встановлювати різні поля для різних типів обʼєктів:

  • set <поле>: Встановити аспект обʼєкта.

Інструмент kubectl підтримує ці додаткові способи оновлення поточного обʼєкта безпосередньо, однак вони вимагають кращого розуміння схеми обʼєктів Kubernetes.

  • edit: Безпосередньо редагувати сирі конфігурації поточного обʼєкта, відкриваючи його конфігурацію в редакторі.
  • patch: Безпосередньо модифікувати конкретні поля поточного обʼєкта, використовуючи рядок патча. Докладніше про рядки патча див. розділ патча в Конвенціях API.

Як видаляти обʼєкти

Ви можете використовувати команду delete, щоб видалити обʼєкт з кластера:

  • delete <тип>/<імʼя>
kubectl delete deployment/nginx

Як переглянути обʼєкт

Існують кілька команд для виведення інформації про обʼєкт:

  • get: Виводить базову інформацію про відповідні обʼєкти. Використовуйте get -h, щоб побачити список опцій.
  • describe: Виводить агреговану детальну інформацію про відповідні обʼєкти.
  • logs: Виводить stdout та stderr для контейнера, що працює в Pod.

Використання команд set для модифікації обʼєктів перед створенням

Є деякі поля обʼєктів, для яких не існує прапорця, який можна використовувати в команді create. У деяких з цих випадків ви можете використовувати комбінацію set та create, щоб вказати значення для поля перед створенням обʼєкта. Це робиться за допомогою перенаправлення виводу команди create на команду set, а потім назад на команду create. Ось приклад:

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
  1. Команда kubectl create service -o yaml --dry-run=client створює конфігурацію для Service, але виводить її на stdout у форматі YAML замість надсилання до сервера API Kubernetes.
  2. Команда kubectl set selector --local -f - -o yaml читає конфігурацію з stdin і записує оновлену конфігурацію на stdout у форматі YAML.
  3. Команда kubectl create -f - створює обʼєкт, використовуючи надану конфігурацію через stdin.

Використання --edit для модифікації обʼєктів перед створенням

Можна використовувати kubectl create --edit, щоб зробити довільні зміни обʼєкта перед його створенням. Ось приклад:

kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
  1. Команда kubectl create service створює конфігурацію для Service та зберігає її у /tmp/srv.yaml.
  2. Команда kubectl create --edit відкриває файл конфігурації для редагування перед створенням обʼєкта.

Що далі

5.4 - Імперативне керування обʼєктами Kubernetes за допомогою файлів конфігурації

Обʼєкти Kubernetes можна створювати, оновлювати та видаляти за допомогою інструменту командного рядка kubectl разом із файлом конфігурації обʼєкта, написаним у форматі YAML або JSON. У цьому документі пояснюється, як визначати та керувати обʼєктами за допомогою файлів конфігурації.

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

Встановіть kubectl.

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

Для перевірки версії введіть kubectl version.

Компроміси

Інструмент kubectl підтримує три види управління обʼєктами:

  • Імперативні команди
  • Імперативне конфігурування обʼєктів
  • Декларативне конфігурування обʼєктів

Див. Управління обʼєктами Kubernetes для обговорення переваг та недоліків кожного виду управління обʼєктами.

Як створювати обʼєкти

Ви можете використовувати kubectl create -f для створення обʼєкта з файлу конфігурації. Дивіться Довідник API Kubernetes для отримання деталей.

  • kubectl create -f <filename|url>

Як оновити обʼєкти

Ви можете використовувати kubectl replace -f для оновлення поточного обʼєкта згідно з файлом конфігурації.

  • kubectl replace -f <filename|url>

Як видалити обʼєкти

Ви можете використовувати kubectl delete -f для видалення обʼєкта, який описаний у файлі конфігурації.

  • kubectl delete -f <filename|url>

Як переглянути обʼєкт

Ви можете використовувати kubectl get -f, щоб переглянути інформацію про обʼєкт, що описаний у файлі конфігурації.

  • kubectl get -f <filename|url> -o yaml

Прапорець -o yaml вказує, що повна конфігурація обʼєкта виводиться. Використовуйте kubectl get -h, щоб побачити список опцій.

Обмеження

Команди create, replace та delete працюють добре, коли кожна конфігурація обʼєкта повністю визначена і записана у своєму файлі конфігурації. Однак, коли поточний обʼєкт оновлюється, і оновлення не обʼєднуються в його файл конфігурації, оновлення будуть втрачені після наступного виконання replace. Це може статися, якщо контролер, такий як HorizontalPodAutoscaler, вносить оновлення безпосередньо до поточного обʼєкта. Ось приклад:

  1. Ви створюєте обʼєкт з файлу конфігурації.
  2. Інше джерело оновлює обʼєкт, змінюючи якесь поле.
  3. Ви замінюєте обʼєкт з файлу конфігурації. Зміни, внесені іншим джерелом на кроці 2, втрачені.

Якщо вам потрібна підтримка кількох записувачів для одного обʼєкта, ви можете використовувати kubectl apply для керування обʼєктом.

Створення та редагування обʼєкта за URL без зберігання конфігурації

Припустимо, що у вас є URL файлу конфігурації обʼєкта. Ви можете використовувати kubectl create --edit для внесення змін до конфігурації перед створенням обʼєкта. Це особливо корисно для підручників та завдань, які вказують на файл конфігурації, який може бути змінений читачем.

kubectl create -f <url> --edit

Міграція від імперативних команд до імперативного конфігурування обʼєктів

Міграція від імперативних команд до імперативної конфігурації обʼєктів включає декілька кроків, які потрібно виконати вручну.

  1. Експортуйте поточний обʼєкт у локальний файл конфігурації обʼєкта:

    kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
    
  2. Ручне видалення поля стану з файлу конфігурації обʼєкта.

  3. Для подальшого керування обʼєктом виключно використовуйте replace.

    kubectl replace -f <kind>_<name>.yaml
    

Визначення селекторів контролерів та міток PodTemplate

Рекомендований підхід — визначити одну незмінну мітку PodTemplate, яка використовується тільки селектором контролера без іншого семантичного значення.

Приклад мітки:

selector:
  matchLabels:
      controller-selector: "apps/v1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "apps/v1/deployment/nginx"

Що далі

5.5 - Оновлення обʼєктів API на місці за допомогою kubectl patch

Використовуйте kubectl patch для оновлення обʼєктів Kubernetes API на місці. Виконайте стратегічний патч злиття або патч злиття JSON.

Це завдання показує, як використовувати kubectl patch для оновлення обʼєкта API на місці. Вправи в цьому завданні демонструють стратегічний патч злиття та патч злиття JSON.

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

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

Для перевірки версії введіть kubectl version.

Використання стратегічного патчу злиття для оновлення Deployment

Ось файл конфігурації для Deployment, що має дві репліки. Кожна репліка є Podʼом, який має один контейнер:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: patch-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: patch-demo-ctr
        image: nginx
      tolerations:
      - effect: NoSchedule
        key: dedicated
        value: test-team

Створіть Deployment:

kubectl apply -f https://k8s.io/examples/application/deployment-patch.yaml

Перегляньте Podʼи, повʼязані з вашим Deployment:

kubectl get pods

Вивід показує, що Deployment має два Podʼи. 1/1 вказує на те, що кожен Pod має один контейнер:

NAME                        READY     STATUS    RESTARTS   AGE
patch-demo-28633765-670qr   1/1       Running   0          23s
patch-demo-28633765-j5qs3   1/1       Running   0          23s

Зробіть позначку про імена працюючих Podʼів. Пізніше ви побачите, що ці Podʼи будуть завершені та замінені новими.

На цей момент кожен Pod має один контейнер, який запускає образ nginx. Тепер, здавалося б, вам потрібно, щоб кожен Pod мав два контейнери: один, який запускає nginx, і один, який запускає redis.

Створіть файл з іменем patch-file.yaml, який має такий вміст:

spec:
  template:
    spec:
      containers:
      - name: patch-demo-ctr-2
        image: redis

Застосуйте патч до вашого Deployment:

kubectl patch deployment patch-demo --patch-file patch-file.yaml

Перегляньте Deployment після накладання патчу:

kubectl get deployment patch-demo --output yaml

Вивід показує, що PodSpec у Deployment має ддва контейнери

containers:
- image: redis
  imagePullPolicy: Always
  name: patch-demo-ctr-2
  ...
- image: nginx
  imagePullPolicy: Always
  name: patch-demo-ctr
  ...

Перегляньте Podʼи, повʼязані з вашим Deployment після накладання патчу:

kubectl get pods

Вивід показує, що працюючі Podʼи мають різні імена Podʼів, у порівнняні з тими що працювали раніше. Deployment припинив роботу старих Podʼів та створив два нові Podʼи, які відповідають оновленій специфікації Deployment. 2/2 вказує на те, що кожен Pod має два контейнера:

NAME                          READY     STATUS    RESTARTS   AGE
patch-demo-1081991389-2wrn5   2/2       Running   0          1m
patch-demo-1081991389-jmg7b   2/2       Running   0          1m

Придивіться уважніше до одного з Podʼів patch-demo:

kubectl get pod <your-pod-name> --output yaml

Вивід показує, що Pod має два контейнери: один, який запускає nginx, і один, який запускає redis:

containers:
- image: redis
  ...
- image: nginx
  ...

Примітки щодо стратегічного патчу злиття

Патч, який ви виконали у попередній вправі, називається стратегічним патчем злиття. Зверніть увагу, що патч не замінив список containers. Замість цього він додав новий контейнер до списку. Іншими словами, список у патчі був обʼєднаний з поточним списком. Це не завжди трапляється, коли ви використовуєте стратегічний патч злиття для списку. У деяких випадках список замінюється, а не обʼєднується.

За допомогою стратегічних патчів злиття список або замінюється, або обʼєднується залежно від його стратегії патча. Стратегія патча вказується значенням ключа patchStrategy у мітці поля в вихідному коді Kubernetes. Наприклад, поле Containers структури PodSpec має patchStrategy рівне merge:

type PodSpec struct {
  ...
  Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" ...`
  ...
}

Ви також можете побачити стратегію патча в специфікації OpenApi:

"io.k8s.api.core.v1.PodSpec": {
    ...,
    "containers": {
        "description": "List of containers belonging to the pod.  ...."
    },
    "x-kubernetes-patch-merge-key": "name",
    "x-kubernetes-patch-strategy": "merge"
}

І ви можете побачити стратегію патча в документації Kubernetes API.

Створіть файл з іменем patch-file-tolerations.yaml із таким вмістом:

spec:
  template:
    spec:
      tolerations:
      - effect: NoSchedule
        key: disktype
        value: ssd

Застосуйте патч до вашого Deployment:

kubectl patch deployment patch-demo --patch-file patch-file-tolerations.yaml

Перегляньте Deployment після накладання патчу:

kubectl get deployment patch-demo --output yaml

Відвід показує, що у PodSpec у Deployment є лише один Toleration:

tolerations:
- effect: NoSchedule
  key: disktype
  value: ssd

Зверніть увагу, що список tolerations в PodSpec був замінений, а не обʼєднаний. Це тому, що поле Tolerations структури PodSpec не має ключа patchStrategy у своїй мітці поля. Тому стратегія патча, що використовується стандартно, дорівнює replace.

type PodSpec struct {
  ...
  Tolerations []Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"`
  ...
}

Використання патчу злиття JSON для оновлення Deployment

Стратегічне патч злиття відрізняється від JSON merge patch. З JSON merge patch, якщо вам потрібно оновити список, вам потрібно вказати цілий новий список. І новий список повністю замінює поточний список.

Команда kubectl patch має параметр type, який ви можете встановити на одне з цих значень:

Значення параметраТип злиття
jsonJSON Patch, RFC 6902
mergeJSON Merge Patch, RFC 7386
strategicСтратегічне патч злиття

Для порівняння JSON patch та JSON merge patch, див. JSON Patch та JSON Merge Patch.

Стандартне значення для параметра type є strategic. Таким чином, у попередній вправі ви виконали стратегічний патч злиття.

Далі виконайте JSON merge patch на вашому Deployment. Створіть файл з іменем patch-file-2.yaml із таким вмістом:

spec:
  template:
    spec:
      containers:
      - name: patch-demo-ctr-3
        image: gcr.io/google-samples/hello-app:2.0

У вашій команді патча встановіть type на merge:

kubectl patch deployment patch-demo --type merge --patch-file patch-file-2.yaml

Перегляньте Deployment після накладання патчу:

kubectl get deployment patch-demo --output yaml

Список containers, який ви вказали у патчі, має лише один контейнер. Вивід показує, що ваш список з одним контейнером замінив наявний список containers.

spec:
  containers:
  - image: gcr.io/google-samples/hello-app:2.0
    ...
    name: patch-demo-ctr-3

Перегляньте Podʼи:

kubectl get pods

У виводі ви можете побачити, що наявні Podʼи були завершені, а нові Podʼи — створені. 1/1 вказує на те, що кожен новий Pod працює лише з одним контейнером.

NAME                          READY     STATUS    RESTARTS   AGE
patch-demo-1307768864-69308   1/1       Running   0          1m
patch-demo-1307768864-c86dc   1/1       Running   0          1m

Використання стратегічного патча злиття для оновлення Deployment з використанням стратегії retainKeys

Ось файл конфігурації для Deployment, що використовує стратегію RollingUpdate:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: retainkeys-demo
spec:
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 30%
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: retainkeys-demo-ctr
        image: nginx

Створіть Deployment:

kubectl apply -f https://k8s.io/examples/application/deployment-retainkeys.yaml

На цей момент Deployment створено і використовує стратегію RollingUpdate.

Створіть файл з іменем patch-file-no-retainkeys.yaml з таким вмістом:

spec:
  strategy:
    type: Recreate

Застосуйте патч до вашого Deployment:

kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-no-retainkeys.yaml

У виводі ви побачите, що неможливо встановити type як Recreate, коли значення визначено для spec.strategy.rollingUpdate:

The Deployment "retainkeys-demo" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate'

Способом видалити значення для spec.strategy.rollingUpdate під час оновлення значення для type є використання стратегії retainKeys для стратегічного злиття.

Створіть ще один файл з іменем patch-file-retainkeys.yaml з таким вмістом:

spec:
  strategy:
    $retainKeys:
    - type
    type: Recreate

З цим патчем ми вказуємо, що ми хочемо зберегти лише ключ type обʼєкта strategy. Таким чином, під час операції патча значення rollingUpdate буде видалено.

Знову застосуйте патч до вашого Deployment з цим новим патчем:

kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-retainkeys.yaml

Дослідіть вміст Deployment:

kubectl get deployment retainkeys-demo --output yaml

У виводі показано, що обʼєкт стратегії в Deployment більше не містить ключа rollingUpdate:

spec:
  strategy:
    type: Recreate
  template:

Примітки щодо стратегічного патчу злиття з використанням стратегії retainKeys

Патч, який ви виконали в попередній вправі, називається стратегічним патчем злиття з використанням стратегії retainKeys. Цей метод вводить нову директиву $retainKeys, яка має наступні стратегії:

  • Вона містить список рядків.
  • Усі поля, які потрібно зберегти, повинні бути присутні в списку $retainKeys.
  • Поля, які присутні, будуть обʼєднані з поточним обʼєктом.
  • Всі відсутні поля будуть очищені під час застосування патча.
  • Усі поля у списку $retainKeys повинні бути надмножиною або такими ж, як і поля, що присутні в патчі.

Стратегія retainKeys не працює для всіх обʼєктів. Вона працює лише тоді, коли значення ключа patchStrategy у мітці поля в вихідному коді Kubernetes містить retainKeys. Наприклад, поле Strategy структури DeploymentSpec має patchStrategy рівне retainKeys:

type DeploymentSpec struct {
  ...
  // +patchStrategy=retainKeys
  Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" ...`
  ...
}

Ви також можете побачити стратегію retainKeys в специфікації OpenApi:

"io.k8s.api.apps.v1.DeploymentSpec": {
    ...,
    "strategy": {
        "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy",
        "description": "The deployment strategy to use to replace existing pods with new ones.",
        "x-kubernetes-patch-strategy": "retainKeys"
    },
    ....
}

І ви можете дізнатись більше про стратегію retainKeys в документації Kubernetes API.

Альтернативні форми команди kubectl patch

Команда kubectl patch приймає YAML або JSON. Вона може приймати патч як файл або безпосередньо в командному рядку.

Створіть файл з іменем patch-file.json з таким вмістом:

{
   "spec": {
      "template": {
         "spec": {
            "containers": [
               {
                  "name": "patch-demo-ctr-2",
                  "image": "redis"
               }
            ]
         }
      }
   }
}

Наступні команди є еквівалентними:

kubectl patch deployment patch-demo --patch-file patch-file.yaml
kubectl patch deployment patch-demo --patch 'spec:\n template:\n  spec:\n   containers:\n   - name: patch-demo-ctr-2\n     image: redis'

kubectl patch deployment patch-demo --patch-file patch-file.json
kubectl patch deployment patch-demo --patch '{"spec": {"template": {"spec": {"containers": [{"name": "patch-demo-ctr-2","image": "redis"}]}}}}'

Оновлення кількості реплік обʼєкта за допомогою kubectl patch з --subresource

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.24 [alpha]

Прапорець --subresource=[імʼя-субресурсу] використовується з командами kubectl, такими як get, patch, edit і replace, для отримання та оновлення субресурсів status та scale ресурсів (застосовується для версії kubectl v1.24 або новішої). Цей прапорець використовується з усіма ресурсами API (вбудованими та CR), які мають субресурси status або scale. Deployment — один з прикладів, які підтримують ці субресурси.

Ось маніфест для Deployment, що має дві репліки:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # вказує Deployment запустити 2 Podʼи, що відповідають шаблону
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Створіть Deployment:

kubectl apply -f https://k8s.io/examples/application/deployment.yaml

Перегляньте Podʼи, повʼязані з вашим Deployment:

kubectl get pods -l app=nginx

У виводі ви можете побачити, що у Deployment є дві Podʼи. Наприклад:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-22567   1/1     Running   0          47s
nginx-deployment-7fb96c846b-mlgns   1/1     Running   0          47s

Тепер застосуйте патч до Deployment з прапорцем --subresource=[імʼя-субресурсу]:

kubectl patch deployment nginx-deployment --subresource='scale' --type='merge' -p '{"spec":{"replicas":3}}'

Вивід:

scale.autoscaling/nginx-deployment patched

Перегляньте Podʼи, повʼязані з вашим Deployment після патчу:

kubectl get pods -l app=nginx

У виводі ви можете побачити, що було створено один новий Pod, тепер у вас є 3 запущені Podʼи.

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-22567   1/1     Running   0          107s
nginx-deployment-7fb96c846b-lxfr2   1/1     Running   0          14s
nginx-deployment-7fb96c846b-mlgns   1/1     Running   0          107s

Перегляньте Deployment після патчу:

kubectl get deployment nginx-deployment -o yaml
...
spec:
  replicas: 3
  ...
status:
  ...
  availableReplicas: 3
  readyReplicas: 3
  replicas: 3

Підсумки

У цій вправі ви використали kubectl patch, щоб змінити поточну конфігурацію обʼєкта Deployment. Ви не змінювали файл конфігурації, який ви спочатку використовували для створення обʼєкта Deployment. Інші команди для оновлення обʼєктів API включають kubectl annotate, kubectl edit, kubectl replace, kubectl scale, та kubectl apply.

Що далі

5.6 - Міграція обʼєктів Kubernetes за допомогою міграції версій сховища

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.30 [alpha]

Kubernetes покладається на активне переписування даних API, щоб підтримувати деякі дії обслуговування, повʼязані зі збереженням у спокої. Два видатні приклади цього — це версійна схема збережених ресурсів (тобто зміна перевіреної схеми збереження від v1 до v2 для певного ресурсу) та шифрування у спокої (тобто перезапис старих даних на основі зміни у способі шифрування даних).

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

Встановіть kubectl.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.30. Для перевірки версії введіть kubectl version.

Переконайтеся, що у вашому кластері увімкнено функціональні можливості StorageVersionMigrator та InformerResourceVersion. Для внесення цих змін вам знадобиться доступ адміністратора панелі управління.

Увімкніть REST API для міграції версій сховища, встановивши параметр конфігурації storagemigration.k8s.io/v1alpha1 на true для API-сервера. Для отримання додаткової інформації про те, як це зробити, прочитайте увімкнення або вимкнення API Kubernetes.

Перешифрування Secret Kubernetes за допомогою міграції версій сховища

  • Для початку, налаштуйте постачальника KMS для шифрування даних у спокої в etcd, використовуючи наступну конфігурацію шифрування.

    kind: EncryptionConfiguration
    apiVersion: apiserver.config.k8s.io/v1
    resources:
    - resources:
      - secrets
      providers:
      - aescbc:
        keys:
        - name: key1
          secret: c2VjcmV0IGlzIHNlY3VyZQ==
    

    Переконайтеся, що автоматичне перезавантаження конфігурації шифрування встановлено на значення true, встановивши --encryption-provider-config-automatic-reload.

  • Створіть Secret за допомогою kubectl.

    kubectl create secret generic my-secret --from-literal=key1=supersecret
    
  • Перевірте серіалізовані дані для цього обʼєкта Secret, що починаються з k8s:enc:aescbc:v1:key1.

  • Оновіть файл конфігурації шифрування наступним чином, щоб виконати ротацію ключа шифрування.

    kind: EncryptionConfiguration
    apiVersion: apiserver.config.k8s.io/v1
    resources:
    - resources:
      - secrets
      providers:
      - aescbc:
        keys:
        - name: key2
          secret: c2VjcmV0IGlzIHNlY3VyZSwgaXMgaXQ/
      - aescbc:
        keys:
        - name: key1
          secret: c2VjcmV0IGlzIHNlY3VyZQ==
    
  • Щоб переконатися, що раніше створений секрет my-secret зашифровано новим ключем key2, ви будете використовувати Storage Version Migration.

  • Створіть маніфест StorageVersionMigration з назвою migrate-secret.yaml, наступного змісту:

    kind: StorageVersionMigration
    apiVersion: storagemigration.k8s.io/v1alpha1
    metadata:
      name: secrets-migration
    spec:
      resource:
        group: ""
        version: v1
        resource: secrets
    

    Створіть обʼєкт за допомогою kubectl наступним чином:

    kubectl apply -f migrate-secret.yaml
    
  • Спостерігайте за міграцією Secret, перевіряючи .status обʼєкта StorageVersionMigration. Успішна міграція повинна мати умову Succeeded встановлену на true. Отримайте обʼєкт StorageVersionMigration наступним чином:

    kubectl get storageversionmigration.storagemigration.k8s.io/secrets-migration -o yaml
    

    Вивід буде подібним до:

    kind: StorageVersionMigration
    apiVersion: storagemigration.k8s.io/v1alpha1
    metadata:
      name: secrets-migration
      uid: 628f6922-a9cb-4514-b076-12d3c178967c
      resourceVersion: "90"
      creationTimestamp: "2024-03-12T20:29:45Z"
    spec:
      resource:
        group: ""
        version: v1
        resource: secrets
    status:
      conditions:
      - type: Running
        status: "False"
        lastUpdateTime: "2024-03-12T20:29:46Z"
        reason: StorageVersionMigrationInProgress
      - type: Succeeded
        status: "True"
        lastUpdateTime: "2024-03-12T20:29:46Z"
        reason: StorageVersionMigrationSucceeded
      resourceVersion: "84"
    
  • Перевірте збережений Secret тепер починається з k8s:enc:aescbc:v1:key2.

Оновлення бажаної схеми зберігання CRD

Розгляньте сценарій, де створено CustomResourceDefinition (CRD), щоб обслуговувати власні ресурси (CR), і встановлено як бажана схема сховища. Коли настане час ввести v2 CRD, його можна додати для обслуговування лише з вебхуком. Це дозволяє плавний перехід, де користувачі можуть створювати CR за допомогою схеми v1 або v2, з вкбхуком, що виконує необхідну конвертацію схеми між ними. Перш ніж встановити v2 як бажану версію схеми сховища, важливо переконатися, що всі наявні CR, збережені як v1, мігрували на v2. Ця міграція може бути досягнута через Storage Version Migration для міграції всіх CR від v1 до v2.

  • Створіть маніфест для CRD з назвою test-crd.yaml, наступного змісту:

    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
      name: selfierequests.stable.example.com
    spec:
      group: stable.example.com
      names:
        plural: SelfieRequests
        singular: SelfieRequest
        kind: SelfieRequest
        listKind: SelfieRequestList
      scope: Namespaced
      versions:
      - name: v1
        served: true
        storage: true
        schema:
          openAPIV3Schema:
            type: object
            properties:
              hostPort:
                type: string
      conversion:
        strategy: Webhook
        webhook:
          clientConfig:
            url: "https://127.0.0.1:9443/crdconvert"
            caBundle: <Інформація про CA Bundle>
        conversionReviewVersions:
        - v1
        - v2
    

    Створіть CRD за допомогою kubectl:

    kubectl apply -f test-crd.yaml
    
  • Створіть маніфест для прикладу testcrd. Назва маніфесту — cr1.yaml, з наступним змістом:

    apiVersion: stable.example.com/v1
    kind: SelfieRequest
    metadata:
      name: cr1
      namespace: default
    

    Створіть CR за допомогою kubectl:

    kubectl apply -f cr1.yaml
    
  • Перевірте, що CR записано і збережено як v1, отримавши обʼєкт з etcd.

    ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr1 [...] | hexdump -C
    

    де [...] містить додаткові аргументи для підключення до сервера etcd.

  • Оновіть CRD test-crd.yaml, щоб додати версію v2 для обслуговування і сховища, а також v1 як обслуговування тільки, наступним чином:

    apiVersion: apiextensions.k8s.io/v1
    kind: CustomResourceDefinition
    metadata:
    name: selfierequests.stable.example.com
    spec:
      group: stable.example.com
      names:
        plural: SelfieRequests
        singular: SelfieRequest
        kind: SelfieRequest
        listKind: SelfieRequestList
      scope: Namespaced
      versions:
        - name: v2
          served: true
          storage: true
          schema:
            openAPIV3Schema:
              type: object
              properties:
                host:
                  type: string
                port:
                  type: string
        - name: v1
          served: true
          storage: false
          schema:
            openAPIV3Schema:
              type: object
              properties:
                hostPort:
                  type: string
      conversion:
        strategy: Webhook
        webhook:
          clientConfig:
            url: "https://127.0.0.1:9443/crdconvert"
            caBundle: <Інформація про CA Bundle>
          conversionReviewVersions:
            - v1
            - v2
    

    Оновіть CRD за допомогою kubectl:

    kubectl apply -f test-crd.yaml
    
  • Створіть файл ресурсу CR з назвою cr2.yaml наступного змісту:

    apiVersion: stable.example.com/v2
    kind: SelfieRequest
    metadata:
      name: cr2
      namespace: default
    
  • Створіть CR за допомогою kubectl:

    kubectl apply -f cr2.yaml
    
  • Перевірте, що CR записано і збережено як v2, отримавши обʼєкт з etcd.

    ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr2 [...] | hexdump -C
    

    де [...] містить додаткові аргументи для підключення до сервера etcd.

  • Створіть маніфест міграції версії сховища з назвою migrate-crd.yaml, з наступним

змістом:

kind: StorageVersionMigration
apiVersion: storagemigration.k8s.io/v1alpha1
metadata:
  name: crdsvm
spec:
  resource:
    group: stable.example.com
    version: v1
    resource: SelfieRequest

Створіть обʼєкт за допомогою kubectl наступним чином:

kubectl apply -f migrate-crd.yaml
  • Спостерігайте за міграцією Secretʼів, використовуючи статус. Успішна міграція повинна мати умову Succeeded, встановлену на "True" у полі статусу. Отримайте ресурс міграції наступним чином:

    kubectl get storageversionmigration.storagemigration.k8s.io/crdsvm -o yaml
    

    Виведення буде подібним до:

    kind: StorageVersionMigration
    apiVersion: storagemigration.k8s.io/v1alpha1
    metadata:
      name: crdsvm
      uid: 13062fe4-32d7-47cc-9528-5067fa0c6ac8
      resourceVersion: "111"
      creationTimestamp: "2024-03-12T22:40:01Z"
    spec:
      resource:
        group: stable.example.com
        version: v1
        resource: testcrds
    status:
      conditions:
        - type: Running
          status: "False"
          lastUpdateTime: "2024-03-12T22:40:03Z"
          reason: StorageVersionMigrationInProgress
        - type: Succeeded
          status: "True"
          lastUpdateTime: "2024-03-12T22:40:03Z"
          reason: StorageVersionMigrationSucceeded
      resourceVersion: "106"
    
  • Перевірте, що раніше створений cr1 тепер записано і збережено як v2, отримавши обʼєкт з etcd.

    ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr1 [...] | hexdump -C
    

    де [...] містить додаткові аргументи для підключення до сервера etcd.

6 - Керування Secret

Керування конфіденційними налаштуваннями за допомогою Secret.

6.1 - Керування Secret за допомогою kubectl

Створення обʼєктів Secret за допомогою інструменту командного рядка kubectl.

На цій сторінці ви дізнаєтесь, як створювати, редагувати, керувати та видаляти Secret Kubernetes за допомогою інструменту командного рядка kubectl.

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

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

Створення Secret

Обʼєкт Secret зберігає конфіденційні дані, такі як облікові дані, які використовуються потоками для доступу до служб. Наприклад, вам може знадобитися Secret для зберігання імені користувача та пароля, необхідних для доступу до бази даних.

Ви можете створити Secret, передаючи необроблену інформацію у команді, або зберігаючи облікові дані у файлах, які ви передаєте в команді. Наступні команди створюють Secret, який зберігає імʼя користувача admin та пароль S!B\*d$zDsb=.

Використання необробленої інформації

Виконайте наступну команду:

kubectl create secret generic db-user-pass \
    --from-literal=username=admin \
    --from-literal=password='S!B\*d$zDsb='

Вам потрібно використовувати одинарні лапки '', щоб екранувати спеціальні символи, такі як $, \, *, =, і ! у вашому рядку. Якщо ви цього не зробите, ваша оболонка буде інтерпретувати ці символи відповідним чином.

Використання сирцевих файлів

  1. Збережіть облікові дані у файлах:

    echo -n 'admin' > ./username.txt
    echo -n 'S!B\*d$zDsb=' > ./password.txt
    

    Прапорець -n гарантує, що згенеровані файли не матимуть додаткового символу нового рядка в кінці тексту. Це важливо, оскільки коли kubectl зчитує файл і кодує вміст у рядок base64, додатковий символ нового рядка також буде закодований. Вам не потрібно екранувати спеціальні символи у рядках, які ви включаєте в файл.

  2. Передайте шляхи до файлів у команду kubectl:

    kubectl create secret generic db-user-pass \
        --from-file=./username.txt \
        --from-file=./password.txt
    

    Стандартне імʼя ключа — це назва файлу. За потреби ви можете встановити імʼя ключа за допомогою --from-file=[key=]source. Наприклад:

    kubectl create secret generic db-user-pass \
        --from-file=username=./username.txt \
        --from-file=password=./password.txt
    

Не важливо який метод буде використаний вивід буде подібний до:

secret/db-user-pass created

Перевірка Secret

Перевірте, що Secret був створений:

kubectl get secrets

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

NAME              TYPE       DATA      AGE
db-user-pass      Opaque     2         51s

Перегляньте деталі Secret:

kubectl describe secret db-user-pass

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

Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password:    12 bytes
username:    5 bytes

Команди kubectl get та kubectl describe стандартно уникають показу вмісту Secret. Це зроблено для захисту Secret від випадкового розкриття або збереження в журналі термінала.

Розкодування Secret

  1. Перегляньте вміст створеного вами Secret:

    kubectl get secret db-user-pass -o jsonpath='{.data}'
    

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

    { "password": "UyFCXCpkJHpEc2I9", "username": "YWRtaW4=" }
    
  2. Розкодуйте дані password:

    echo 'UyFCXCpkJHpEc2I9' | base64 --decode
    

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

    S!B\*d$zDsb=
    
    kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode
    

Редагування Secret

Ви можете редагувати наявний обʼєкт Secret, якщо він не є незмінним. Щоб редагувати Secret, виконайте наступну команду:

kubectl edit secrets <secret-name>

Це відкриває ваш стандартний редактор і дозволяє оновити значення Secret, закодовані в base64, у полі data, як у наступному прикладі:

# Будь ласка, відредагуйте обʼєкт нижче. Рядки, що починаються з '#', будуть ігноруватися,
# і порожній файл припинить редагування. Якщо виникне помилка під час збереження цього файлу, він буде
# знову відкритий з відповідними збоями.
#
apiVersion: v1
data:
  password: UyFCXCpkJHpEc2I9
  username: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: "2022-06-28T17:44:13Z"
  name: db-user-pass
  namespace: default
  resourceVersion: "12708504"
  uid: 91becd59-78fa-4c85-823f-6d44436242ac
type: Opaque

Прибирання

Щоб видалити Secret, виконайте наступну команду:

kubectl delete secret db-user-pass

Що далі

6.2 - Керування Secret за допомогою конфігураційного файлу

Створення обʼєктів Secret за допомогою конфігураційного файлу ресурсів.

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

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

Створення Secret

Ви можете спочатку визначити обʼєкт Secret у форматі JSON або YAML у маніфесті, а потім створити цей обʼєкт. Ресурс Secret містить два словники: data та stringData. Поле data використовується для зберігання довільних даних, закодованих за допомогою base64. Поле stringData надається для зручності, і воно дозволяє вам надавати ті самі дані у вигляді незакодованих рядків. Ключі data та stringData повинні складатися з буквено-цифрових символів, -, _ або ..

Наведений нижче приклад зберігає два рядки у Secret, використовуючи поле data.

  1. Конвертуйте рядки в base64:

    echo -n 'admin' | base64
    echo -n '1f2d1e2e67df' | base64
    

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

    YWRtaW4=
    MWYyZDFlMmU2N2Rm
    
  2. Створіть маніфест:

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    type: Opaque
    data:
      username: YWRtaW4=
      password: MWYyZDFlMmU2N2Rm
    

    Зверніть увагу, що імʼя обʼєкта Secret повинно бути дійсним піддоменом DNS.

  3. Створіть Secret, використовуючи kubectl apply:

    kubectl apply -f ./secret.yaml
    

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

    secret/mysecret created
    

Щоб перевірити, що Secret був створений та щоб розкодувати дані Secret, див. Керування Secret за допомогою kubectl.

Вказання незакодованих даних під час створення Secret

Для певних сценаріїв можливо вам захочеться використовувати поле stringData. Це поле дозволяє вам розміщувати незакодований рядок безпосередньо у Secret, і цей рядок буде закодований за вас при створенні або оновленні Secret.

Практичний приклад цього може бути там, де ви розгортаєте застосунок, що використовує Secret для зберігання файлу конфігурації, і ви хочете заповнити частини цього файлу конфігурації під час процесу розгортання.

Наприклад, якщо ваш застосунок використовує такий файл конфігурації:

apiUrl: "https://my.api.com/api/v1"
username: "<user>"
password: "<password>"

Ви можете зберегти це в Secret, використовуючи таке визначення:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |
    apiUrl: "https://my.api.com/api/v1"
    username: <user>
    password: <password>    

При отриманні даних Secret, команда повертає закодовані значення, а не текстові значення, які ви вказали у stringData.

Наприклад, якщо ви виконаєте наступну команду:

kubectl get secret mysecret -o yaml

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

apiVersion: v1
data:
  config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:40:59Z
  name: mysecret
  namespace: default
  resourceVersion: "7225"
  uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque

Вказання як data, так і stringData

Якщо ви вказали поле як у data, так і у stringData, буде використано значення з stringData.

Наприклад, якщо ви визначите наступний секрет:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
stringData:
  username: administrator

Обʼєкт Secret буде створено так:

apiVersion: v1
data:
  username: YWRtaW5pc3RyYXRvcg==
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:46:46Z
  name: mysecret
  namespace: default
  resourceVersion: "7579"
  uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque

YWRtaW5pc3RyYXRvcg== декодується у administrator.

Редагування Secret

Щоб редагувати дані у Secret, створеному за допомогою маніфесту, змініть поле data або stringData у вашому маніфесті та застосуйте файл у вашому кластері. Ви можете редагувати наявний обʼєкт Secret, за винятком випадку, коли він є незмінним.

Наприклад, якщо ви хочете змінити пароль з попереднього прикладу на birdsarentreal, виконайте наступне:

  1. Закодуйте новий рядок пароля:

    echo -n 'birdsarentreal' | base64
    

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

    YmlyZHNhcmVudHJlYWw=
    
  2. Оновіть поле data із вашим новим рядком пароля:

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    type: Opaque
    data:
      username: YWRtaW4=
      password: YmlyZHNhcmVudHJlYWw=
    
  3. Застосуйте маніфест у вашому кластері:

    kubectl apply -f ./secret.yaml
    

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

    secret/mysecret configured
    

Kubernetes оновлює наявний обʼєкт Secret. Докладно, інструмент kubectl помічає, що є обʼєкт Secret з тим самим імʼям. kubectl отримує поточний обʼєкт, планує зміни в ньому і надсилає змінений обʼєкт Secret до панелі управління кластера.

Якщо ви вказали kubectl apply --server-side, kubectl використовує застосування на боці сервера замість цього.

Прибирання

Щоб видалити створений вами Secret:

kubectl delete secret mysecret

Що далі

6.3 - Керування Secret за допомогою Kustomize

Створення обʼєктів Secret за допомогою файлу kustomization.yaml.

kubectl підтримує використання інструменту керування обʼєктами Kustomize для керування Secret та ConfigMap. Ви можете створити генератор ресурсів за допомогою Kustomize, який створює Secret, який ви можете застосувати до сервера API за допомогою kubectl.

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

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

Створення Secret

Ви можете створити Secret, визначивши secretGenerator у файлі kustomization.yaml, який посилається на інші наявні файли, файли .env або літеральні значення. Наприклад, наведені нижче інструкції створюють файл конфігурації kustomization для імені користувача admin та пароля 1f2d1e2e67df.

Створення файлу kustomization


secretGenerator:
- name: database-creds
  literals:
  - username=admin
  - password=1f2d1e2e67df

  1. Збережіть дані доступу у файлах. Назви файлів є ключами секрету:

    echo -n 'admin' > ./username.txt
    echo -n '1f2d1e2e67df' > ./password.txt
    

    Прапорець -n забезпечує відсутність символу нового рядка в кінці ваших файлів.

  2. Створіть файл kustomization.yaml:

    secretGenerator:
    - name: database-creds
      files:
      - username.txt
      - password.txt
    

Ви також можете визначити secretGenerator у файлі kustomization.yaml, надаючи файли .env. Наприклад, наступний файл kustomization.yaml використовує дані з файлу .env.secret:

secretGenerator:
- name: db-user-pass
  envs:
  - .env.secret

У всіх випадках вам не потрібно кодувати значення base64. Імʼя YAML файлу має бути kustomization.yaml або kustomization.yml.

Застосування файлу kustomization

Для створення Secret застосуйте теку, який містить файл kustomization:

kubectl apply -k <directory-path>

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

secret/database-creds-5hdh7hhgfk created

Коли Secret генерується, імʼя Secret створюється шляхом хешування даних Secret і додавання до нього значення хешу. Це забезпечує, що при зміні даних генерується новий Secret.

Щоб перевірити, що Secret був створений та розкодувати дані Secret,

kubectl get -k <directory-path> -o jsonpath='{.data}' 

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

{ "password": "MWYyZDFlMmU2N2Rm", "username": "YWRtaW4=" }
echo 'MWYyZDFlMmU2N2Rm' | base64 --decode

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

1f2d1e2e67df

Для отримання додаткової інформації див. Керування Secret за допомогою kubectl та Декларативне керування обʼєктами Kubernetes за допомогою Kustomize.

Редагування Secret

  1. У вашому файлі kustomization.yaml змініть дані, наприклад, password.

  2. Застосуйте теку, який містить файл kustomization:

    kubectl apply -k <directory-path>
    

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

    secret/db-user-pass-6f24b56cc8 created
    

Змінений Secret створюється як новий обʼєкт Secret, а не оновлюється наявний обʼєкт Secret. Можливо, вам знадобиться оновити посилання на Secret у ваших контейнерах.

Прибирання

Для видалення Secret використовуйте kubectl:

kubectl delete secret db-user-pass

Що далі

7 - Введення даних у застосунки

Визначення конфігурації та інших даних для Podʼів, на яких працює ваше навантаження.

7.1 - Визначення команд та аргументів для контейнера

Ця сторінка показує, як визначати команди та аргументи при запуску контейнера в Pod.

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

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

Для перевірки версії введіть kubectl version.

Визначення команди та аргументів при створенні Podʼа

При створенні Podʼа ви можете визначити команду та аргументи для контейнерів, які працюють в Podʼі. Щоб визначити команду, включіть поле command у файл конфігурації. Щоб визначити аргументи для команди, включіть поле args у файл конфігурації. Команду та аргументи, які ви визначаєте, не можна змінити після створення Podʼа.

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

У цьому завданні ви створюєте Pod, який запускає один контейнер. Файл конфігурації для Podʼа визначає команду та два аргументи:

apiVersion: v1
kind: Pod
metadata:
  name: command-demo
  labels:
    purpose: demonstrate-command
spec:
  containers:
  - name: command-demo-container
    image: debian
    command: ["printenv"]
    args: ["HOSTNAME", "KUBERNETES_PORT"]
  restartPolicy: OnFailure
  1. Створіть Podʼ на основі файлу конфігурації YAML:

    kubectl apply -f https://k8s.io/examples/pods/commands.yaml
    
  2. Перегляньте список запущених Podʼів:

    kubectl get pods
    

    Вивід показує, що контейнер, який працював у Podʼі command-demo, завершився.

  3. Щоб побачити вивід команди, яка запустилася в контейнері, перегляньте логи з Podʼа:

    kubectl logs command-demo
    

    Вивід показує значення змінних середовища HOSTNAME та KUBERNETES_PORT:

    command-demo
    tcp://10.3.240.1:443
    

Використання змінних середовища для визначення аргументів

У попередньому прикладі ви визначили аргументи безпосередньо, надавши їх. Як альтернативу наданню рядків безпосередньо, ви можете визначати аргументи, використовуючи змінні середовища:

env:
- name: MESSAGE
  value: "hello world"
command: ["/bin/echo"]
args: ["$(MESSAGE)"]

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

Виконання команди в оболонці

У деяких випадках вам потрібно, щоб ваша команда запускалася в оболонці. Наприклад, ваша команда може складатися з кількох команд, обʼєднаних в конвеєр, або це може бути сценарій оболонки. Щоб запустити вашу команду в оболонці, оберніть її так:

command: ["/bin/sh"]
args: ["-c", "while true; do echo hello; sleep 10;done"]

Що далі

7.2 - Визначення залежних змінних середовища

Ця сторінка показує, як визначати залежні змінні середовища для контейнера у Podʼі Kubernetes.

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

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

Визначення змінної залежної від середовища для контейнера

При створенні Podʼа ви можете встановлювати залежні змінні середовища для контейнерів, які працюють в Podʼі. Для встановлення залежних змінних середовища ви можете використовувати $(VAR_NAME) у value env у файлі конфігурації.

У цьому завданні ви створюєте Pod, який запускає один контейнер. Файл конфігурації для Podʼа визначає залежну змінну середовища з визначеним загальним використанням. Ось маніфест конфігурації для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: dependent-envars-demo
spec:
  containers:
    - name: dependent-envars-demo
      args:
        - while true; do echo -en '\n'; printf UNCHANGED_REFERENCE=$UNCHANGED_REFERENCE'\n'; printf SERVICE_ADDRESS=$SERVICE_ADDRESS'\n';printf ESCAPED_REFERENCE=$ESCAPED_REFERENCE'\n'; sleep 30; done;
      command:
        - sh
        - -c
      image: busybox:1.28
      env:
        - name: SERVICE_PORT
          value: "80"
        - name: SERVICE_IP
          value: "172.17.0.1"
        - name: UNCHANGED_REFERENCE
          value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
        - name: PROTOCOL
          value: "https"
        - name: SERVICE_ADDRESS
          value: "$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
        - name: ESCAPED_REFERENCE
          value: "$$(PROTOCOL)://$(SERVICE_IP):$(SERVICE_PORT)"
  1. Створіть Podʼ на основі цього маніфесту:

    kubectl apply -f https://k8s.io/examples/pods/inject/dependent-envars.yaml
    
    pod/dependent-envars-demo created
    
  2. Перегляньте список запущених Podʼів:

    kubectl get pods dependent-envars-demo
    
    NAME                      READY     STATUS    RESTARTS   AGE
    dependent-envars-demo     1/1       Running   0          9s
    
  3. Перевірте лог контейнера, що працює у вашому Podʼі:

    kubectl logs pod/dependent-envars-demo
    
    UNCHANGED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
    SERVICE_ADDRESS=https://172.17.0.1:80
    ESCAPED_REFERENCE=$(PROTOCOL)://172.17.0.1:80
    

Як показано вище, ви визначили правильне посилання на залежність SERVICE_ADDRESS, неправильне посилання на залежність UNCHANGED_REFERENCE і пропустили залежні посилання на залежність ESCAPED_REFERENCE.

Коли змінна середовища вже визначена при посиланні, посилання може бути правильно розгорнуте, як у випадку з SERVICE_ADDRESS.

Зверніть увагу, що порядок має значення у списку env. Змінна середовища не вважається "визначеною", якщо вона вказана далі в списку. Тому UNCHANGED_REFERENCE не вдається розгорнути $(PROTOCOL) у прикладі вище.

Коли змінна середовища невизначена або містить лише деякі змінні, невизначена змінна середовища трактується як звичайний рядок, як у випадку з UNCHANGED_REFERENCE. Зверніть увагу, що неправильно опрацьовані змінні середовища, як правило, не блокують запуск контейнера.

Синтаксис $(VAR_NAME) може бути екранований подвійним $, тобто: $$(VAR_NAME). Екрановані посилання ніколи не розгортаються, незалежно від того, чи визначена посилана змінна, чи ні. Це можна побачити у випадку з ESCAPED_REFERENCE вище.

Що далі

7.3 - Визначення змінних середовища для контейнера

Ця сторінка показує, як визначити змінні середовища для контейнера у Kubernetes Pod.

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

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

Визначення змінної середовища для контейнера

При створенні Pod ви можете задати змінні середовища для контейнерів, які запускаються в Pod. Щоб задати змінні середовища, включіть поле env або envFrom у файлі конфігурації.

Поля env та envFrom мають різний ефект.

env
дозволяє вам задати змінні середовища для контейнера, вказуючи значення безпосередньо для кожної змінної, яку ви називаєте.
envFrom
дозволяє вам задати змінні середовища для контейнера, посилаючись на ConfigMap або Secret. Коли ви використовуєте envFrom, всі пари ключ-значення у зазначеному ConfigMap або Secret встановлюються як змінні середовища для контейнера. Ви також можете вказати спільний префіксовий рядок.

Докладніше про ConfigMap та Secret.

Ця сторінка пояснює, як використовувати env.

У цьому завданні ви створюєте Pod, який запускає один контейнер. Конфігураційний файл для Pod визначає змінну середовища з імʼям DEMO_GREETING та значенням "Привіт із середовища". Ось маніфест конфігурації для Pod:

apiVersion: v1
kind: Pod
metadata:
  name: envar-demo
  labels:
    purpose: demonstrate-envars
spec:
  containers:
  - name: envar-demo-container
    image: gcr.io/google-samples/hello-app:2.0
    env:
    - name: DEMO_GREETING
      value: "Hello from the environment"
    - name: DEMO_FAREWELL
      value: "Such a sweet sorrow"
  1. Створіть Pod на основі цього маніфесту:

    kubectl apply -f https://k8s.io/examples/pods/inject/envars.yaml
    
  2. Перегляньте перелік запущених Podʼів:

    kubectl get pods -l purpose=demonstrate-envars
    

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

    NAME            READY     STATUS    RESTARTS   AGE
    envar-demo      1/1       Running   0          9s
    
  3. Перегляньте змінні середовища контейнера Pod:

    kubectl exec envar-demo -- printenv
    

    Вивід буде подібний до такого:

    NODE_VERSION=4.4.2
    EXAMPLE_SERVICE_PORT_8080_TCP_ADDR=10.3.245.237
    HOSTNAME=envar-demo
    ...
    DEMO_GREETING=Привіт із середовища
    DEMO_FAREWELL=Такий солодкий прощальний слова
    

Використання змінних середовища у вашій конфігурації

Змінні середовища, які ви визначаєте у конфігурації Pod у .spec.containers[*].env[*], можна використовувати в інших частинах конфігурації, наприклад, у командах та аргументах, які ви задаєте для контейнерів Pod. У наступній конфігурації прикладу, змінні середовища GREETING, HONORIFIC та NAME встановлені на Warm greetings to, The Most Honorable та Kubernetes відповідно. Змінна середовища MESSAGE комбінує набір усіх цих змінних середовища, а потім використовує їх як аргумент командного рядка, переданий контейнеру env-print-demo.

Імена змінних середовища складаються з літер, цифр, підкреслення, крапок або дефісів, але перший символ не може бути цифрою. Якщо ввімкнуто функціональну можливість RelaxedEnvironmentVariableValidation, можна використовувати всі друковані символи ASCII, окрім "=" для імен змінних середовища.

apiVersion: v1
kind: Pod
metadata:
  name: print-greeting
spec:
  containers:
  - name: env-print-demo
    image: bash
    env:
    - name: GREETING
      value: "Warm greetings to"
    - name: HONORIFIC
      value: "The Most Honorable"
    - name: NAME
      value: "Kubernetes"
    - name: MESSAGE
      value: "$(GREETING) $(HONORIFIC) $(NAME)"
    command: ["echo"]
    args: ["$(MESSAGE)"]

Після створення команда echo Warm greetings to The Most Honorable Kubernetes виконується в контейнері.

Що далі

7.4 - Використання змінних середовища для передачі контейнерам інформації про Pod

Ця сторінка показує, як Pod може використовувати змінні середовища для передачі інформації про себе контейнерам, які працюють в Pod, використовуючи downward API. Ви можете використовувати змінні середовища для експозиції полів Pod, полів контейнера або обох.

У Kubernetes є два способи експозиції полів Pod та контейнера для запущеного контейнера:

  • Змінні середовища, як пояснено в цьому завданні
  • Файли томів

Разом ці два способи експозиції полів Pod та контейнера називаються downward API.

Оскільки Service є основним засобом взаємодії між контейнеризованими застосунками, якими керує Kubernetes, корисно мати можливість виявляти їх під час виконання.

Дізнайтеся більше про доступ до Сервісів тут.

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

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

Використання полів Pod як значень для змінних середовища

У цій частині завдання ви створюєте Pod з одним контейнером, і ви спроєцюєте поля рівня Pod у працюючий контейнер у вигляді змінних середовища.

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-fieldref
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_NODE_NAME MY_POD_NAME MY_POD_NAMESPACE;
          printenv MY_POD_IP MY_POD_SERVICE_ACCOUNT;
          sleep 10;
        done;
      env:
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: MY_POD_SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
  restartPolicy: Never

У цьому маніфесті ви бачите пʼять змінних середовища. Поле env є масивом визначень змінних середовища. Перший елемент у масиві вказує, що змінна середовища MY_NODE_NAME отримує своє значення з поля spec.nodeName Pod. Аналогічно, інші змінні середовища отримують свої назви з полів Pod.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-pod.yaml

Перевірте, що контейнер в Pod працює:

# Якщо новий Pod ще не став доступним, кілька разів перезапустіть цю команду.
kubectl get pods

Перегляньте лог контейнера:

kubectl logs dapi-envars-fieldref

Вивід показує значення вибраних змінних середовища:

minikube
dapi-envars-fieldref
default
172.17.0.4
default

Щоб побачити, чому ці значення є в лозі, подивіться на поля command та args у файлі конфігурації. При запуску контейнера він записує значення пʼяти змінних середовища у stdout. Він повторює це кожні десять секунд.

Далі, отримайте оболонку в контейнер, який працює в вашому Pod:

kubectl exec -it dapi-envars-fieldref -- sh

У вашій оболонці перегляньте змінні середовища:

# Виконайте це в оболонці всередині контейнера
printenv

Вивід показує, що деякі змінні середовища мають призначені значення полів Pod:

MY_POD_SERVICE_ACCOUNT=default
...
MY_POD_NAMESPACE=default
MY_POD_IP=172.17.0.4
...
MY_NODE_NAME=minikube
...
MY_POD_NAME=dapi-envars-fieldref

Використання полів контейнера як значень для змінних середовища

У попередньому завданні ви використовували інформацію з полів рівня Pod як значення для змінних середовища. У наступному завданні ви плануєте передати поля, які є частиною визначення Pod, але взяті з конкретного контейнера замість всього Pod загалом.

Ось маніфест для іншого Pod, який знову має лише один контейнер:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-envars-resourcefieldref
spec:
  containers:
    - name: test-container
      image: registry.k8s.io/busybox:1.24
      command: [ "sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          printenv MY_CPU_REQUEST MY_CPU_LIMIT;
          printenv MY_MEM_REQUEST MY_MEM_LIMIT;
          sleep 10;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: test-container
              resource: limits.memory
  restartPolicy: Never

У цьому маніфесті ви бачите чотири змінні середовища. Поле env є масивом визначень змінних середовища. Перший елемент у масиві вказує, що змінна середовища MY_CPU_REQUEST отримує своє значення з поля requests.cpu контейнера з іменем test-container. Аналогічно, інші змінні середовища отримують свої значення з полів, що є специфічними для цього контейнера.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-envars-container.yaml

Перевірте, що контейнер в Pod працює:

# Якщо новий Pod ще не став доступним, кілька разів перезапустіть цю команду.
kubectl get pods

Перегляньте лог контейнера:

kubectl logs dapi-envars-resourcefieldref

Вивід показує значення вибраних змінних середовища:

1
1
33554432
67108864

Що далі

Дізнайтеся про Pod, контейнери та змінні середовища в легасі довідці API:

7.5 - Передача інформації про Pod контейнерам через файли

Ця сторінка показує, як Pod може використовувати volumeDownwardAPI, щоб передати інформацію про себе контейнерам, які працюють в Pod. volumeDownwardAPI може викривати поля Pod та контейнера.

У Kubernetes є два способи експозиції полів Pod та контейнера для запущеного контейнера:

Разом ці два способи експозиції полів Pod та контейнера називаються downward API.

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

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

Зберігання полів Pod

У цій частині завдання ви створюєте Pod з одним контейнером, і ви проєцюєте поля рівня Pod у працюючий контейнер як файли. Ось маніфест для Pod:

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
  annotations:
    build: two
    builder: john-doe
spec:
  containers:
    - name: client-container
      image: registry.k8s.io/busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          if [[ -e /etc/podinfo/annotations ]]; then
            echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations

У маніфесті ви бачите, що у Pod є volumeDownwardAPI, і контейнер монтує том за шляхом /etc/podinfo.

Подивіться на масив items під downwardAPI. Кожен елемент масиву визначає volumeDownwardAPI. Перший елемент вказує, що значення поля metadata.labels Pod має бути збережене в файлі з назвою labels. Другий елемент вказує, що значення поля annotations Pod має бути збережене в файлі з назвою annotations.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume.yaml

Перевірте, що контейнер в Pod працює:

kubectl get pods

Перегляньте логи контейнера:

kubectl logs kubernetes-downwardapi-volume-example

Вивід показує вміст файлів labels та annotations:

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

build="two"
builder="john-doe"

Отримайте доступ до оболонки в контейнері, який працює в вашому Pod:

kubectl exec -it kubernetes-downwardapi-volume-example -- sh

У вашій оболонці перегляньте файл labels:

/# cat /etc/podinfo/labels

Вивід показує, що всі мітки Pod були записані у файл labels:

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

Аналогічно, перегляньте файл annotations:

/# cat /etc/podinfo/annotations

Перегляньте файли у теці /etc/podinfo:

/# ls -laR /etc/podinfo

У виводі ви побачите, що файли labels та annotations знаходяться в тимчасовій підтеці: у цьому прикладі, ..2982_06_02_21_47_53.299460680. У теці /etc/podinfo, ..data є символічним посиланням на тимчасову підтеку. Також у теці /etc/podinfo, labels та annotations є символічними посиланнями.

drwxr-xr-x  ... Feb 6 21:47 ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 ..data -> ..2982_06_02_21_47_53.299460680
lrwxrwxrwx  ... Feb 6 21:47 annotations -> ..data/annotations
lrwxrwxrwx  ... Feb 6 21:47 labels -> ..data/labels

/etc/..2982_06_02_21_47_53.299460680:
total 8
-rw-r--r--  ... Feb  6 21:47 annotations
-rw-r--r--  ... Feb  6 21:47 labels

Використання символічних посилань дозволяє динамічне атомарне оновлення метаданих; оновлення записуються у новий тимчасову теку, а символічне посилання ..data оновлюється атомарно за допомогою rename(2).

Вийдіть з оболонки:

/# exit

Зберігання полів контейнера

У попередньому завданні ви зробили поля Pod доступними за допомогою Downward API. У наступній вправі ви передаєте поля, які є частиною визначення Pod, але беруться з конкретного контейнера скоріше, ніж з Pod загалом. Ось маніфест для Pod, що має лише один контейнер:

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example-2
spec:
  containers:
    - name: client-container
      image: registry.k8s.io/busybox:1.24
      command: ["sh", "-c"]
      args:
      - while true; do
          echo -en '\n';
          if [[ -e /etc/podinfo/cpu_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_limit; fi;
          if [[ -e /etc/podinfo/cpu_request ]]; then
            echo -en '\n'; cat /etc/podinfo/cpu_request; fi;
          if [[ -e /etc/podinfo/mem_limit ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_limit; fi;
          if [[ -e /etc/podinfo/mem_request ]]; then
            echo -en '\n'; cat /etc/podinfo/mem_request; fi;
          sleep 5;
        done;
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
          - path: "cpu_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.cpu
              divisor: 1m
          - path: "cpu_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.cpu
              divisor: 1m
          - path: "mem_limit"
            resourceFieldRef:
              containerName: client-container
              resource: limits.memory
              divisor: 1Mi
          - path: "mem_request"
            resourceFieldRef:
              containerName: client-container
              resource: requests.memory
              divisor: 1Mi

У маніфесті ви бачите, що у Pod є volumeDownwardAPI, і що контейнер у цьому Pod монтує том за шляхом /etc/podinfo.

Подивіться на масив items під downwardAPI. Кожен елемент масиву визначає файл у томі downward API.

Перший елемент вказує, що в контейнері з назвою client-container, значення поля limits.cpu у форматі, вказаному як 1m, має бути опубліковане як файл з назвою cpu_limit. Поле divisor є необовʼязковим і має стандартне значення 1. Дільник 1 означає ядра для ресурсів cpu, або байти для ресурсів memory.

Створіть Pod:

kubectl apply -f https://k8s.io/examples/pods/inject/dapi-volume-resources.yaml

Отримайте доступ до оболонки в контейнері, який працює в вашому Pod:

kubectl exec -it kubernetes-downwardapi-volume-example-2 -- sh

У вашій оболонці перегляньте файл cpu_limit:

# Виконайте це в оболонці всередині контейнера
cat /etc/podinfo/cpu_limit

Ви можете використовувати подібні команди, щоб переглянути файли cpu_request, mem_limit та mem_request.

Проєцювання ключів на конкретні шляхи та дозволи на файли

Ви можете проєціювати ключі на конкретні шляхи та конкретні дозволи на файл на основі файлу. Для отримання додаткової інформації дивіться Secret.

Що далі

  • Прочитайте spec API-визначення для Pod. Специфікація включає визначення Контейнера (частина Pod).
  • Прочитайте список доступних полів, які ви можете викрити за допомогою downward API.

Дізнайтеся про томи в легасі довідці API:

  • Перегляньте Volume API-визначення, яке визначає загальний том у Pod для доступу контейнерів.
  • Перегляньте DownwardAPIVolumeSource API-визначення, яке визначає том, який містить інформацію Downward API.
  • Перегляньте DownwardAPIVolumeFile API-визначення, яке містить посилання на обʼєкт або поля ресурсу для заповнення файлу у томі Downward API.
  • Перегляньте ResourceFieldSelector API-визначення, яке вказує ресурси контейнера та їх формат виведення.

7.6 - Розповсюдження облікових даних з використанням Secret

Ця сторінка показує, як безпечно передати чутливі дані, такі як паролі та ключі шифрування, у Podʼи.

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

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

Перетворення ваших секретних даних у base64

Припустимо, ви хочете мати дві частини секретних даних: імʼя користувача my-app та пароль 39528$vdg7Jb. Спочатку використайте інструмент кодування base64, щоб перетворити ваше імʼя користувача та пароль на base64. Ось приклад використання загальнодоступної програми base64:

echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64

Вивід показує, що base64 варіант вашого імені користувача — bXktYXBw, а base64 вашого пароля — Mzk1MjgkdmRnN0pi.

Створіть Secret

Ось файл конфігурації, який ви можете використати для створення Secret, що містить ваше імʼя користувача та пароль:

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
data:
  username: bXktYXBw
  password: Mzk1MjgkdmRnN0pi
  1. Створіть Secret

    kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
    
  2. Перегляньте інформацію про Secret:

    kubectl get secret test-secret
    

    Вивід:

    NAME          TYPE      DATA      AGE
    test-secret   Opaque    2         1m
    
  3. Перегляньте більш детальну інформацію про Secret:

    kubectl describe secret test-secret
    

    Вивід:

    Name:       test-secret
    Namespace:  default
    Labels:     <none>
    Annotations:    <none>
    
    Type:   Opaque
    
    Data
    ====
    password:   13 bytes
    username:   7 bytes
    

Створіть Secret безпосередньо за допомогою kubectl

Якщо ви хочете пропустити крок кодування Base64, ви можете створити такий самий Secret, використовуючи команду kubectl create secret. Наприклад:

kubectl create secret generic test-secret --from-literal='username=my-app' --from-literal='password=39528$vdg7Jb'

Це зручніше. Детальний підхід, показаний раніше, розглядає кожен крок явно, щоб продемонструвати, що відбувається.

Створіть Pod, який має доступ до секретних даних через Том

Ось файл конфігурації, який ви можете використати для створення Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
        # ім'я повинно відповідати імені тома нижче
        - name: secret-volume
          mountPath: /etc/secret-volume
          readOnly: true
  # Дані секрету доступні контейнерам у Podʼі через том.
  volumes:
    - name: secret-volume
      secret:
        secretName: test-secret
  1. Створіть Pod:

    kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
    
  2. Перевірте, що ваш Pod працює:

    kubectl get pod secret-test-pod
    

    Вивід:

    NAME              READY     STATUS    RESTARTS   AGE
    secret-test-pod   1/1       Running   0          42m
    
  3. Отримайте доступ до оболонки в контейнері, що працює у вашому Podʼі:

    kubectl exec -i -t secret-test-pod -- /bin/bash
    
  4. Секретні дані доступні контейнеру через Том, змонтований у /etc/secret-volume.

    У вашій оболонці перегляньте файли у теці /etc/secret-volume:

    # Виконайте це в оболонці всередині контейнера
    ls /etc/secret-volume
    

    Вивід показує два файли, один для кожної частини секретних даних:

    password username
    
  5. У вашій оболонці gthtukzymnt вміст файлів username та password:

    # Виконайте це в оболонці всередині контейнера
    echo "$( cat /etc/secret-volume/username )"
    echo "$( cat /etc/secret-volume/password )"
    

    Вивід — ваше імʼя користувача та пароль:

    my-app
    39528$vdg7Jb
    

Змініть ваш образ або командний рядок так, щоб програма шукала файли у теці mountPath. Кожен ключ у масиві data Secretʼу стає імʼям файлу у цій теці.

Спроєцюйте ключі Secret на конкретні шляхи файлів

Ви також можете контролювати шляхи в томі, куди проєцюються ключі Secret. Використовуйте поле .spec.volumes[].secret.items, щоб змінити шлях для кожного ключа:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

При розгортанні цього Podʼа відбувається наступне:

  • Ключ username з mysecret доступний контейнеру за шляхом /etc/foo/my-group/my-username замість /etc/foo/username.
  • Ключ password з цього Secret не проєцюється.

Якщо ви отримуєте перелік ключів явно за допомогою .spec.volumes[].secret.items, розгляньте наступне:

  • Проєцюються тільки ключі, вказані у items.
  • Щоб використовувати всі ключі з Secret, всі вони повинні бути перераховані у полі items.
  • Усі перераховані ключі повинні існувати у відповідному Secret. Інакше Том не створюється.

Встановіть POSIX-права доступу до ключів Secret

Ви можете встановити POSIX права доступу до файлів для окремого ключа Secret. Якщо ви не вказали жодних дозволів, стандартно використовується 0644. Ви також можете встановити стандартно режим файлу POSIX для всього тому Secret, і ви можете перевизначити його за потреби для кожного ключа.

Наприклад, ви можете вказати режим стандартно так:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 0400

Секрет Secret у /etc/foo; всі файли, створені томом секрету, мають дозволи 0400.

Визначте змінні середовища контейнера, використовуючи дані з Secret

Ви можете використовувати дані у Secret як змінні середовища у ваших контейнерах.

Якщо контейнер вже використовує Secret у змінній середовища, оновлення Secret не буде видно контейнеру, якщо він не буде перезапущений. Існують сторонні рішення для виклику перезавантажень, коли змінюються Secret.

Визначення змінної середовища контейнера з даними з одного Secret

  • Визначте змінну середовища як пару ключ-значення в Secret:

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    
  • Призначте значення backend-username, визначене у Secret, змінній середовища SECRET_USERNAME у специфікації Podʼа.

    apiVersion: v1
    kind: Pod
    metadata:
      name: env-single-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
    
  • Створіть Pod:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
    
  • У вашій оболонці покажіть вміст змінної середовища контейнера SECRET_USERNAME.

    kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
    

    Вивід схожий на:

    backend-admin
    

Визначення змінних середовища контейнера з даними з декількох Secret

  • Як і у попередньому прикладі, спочатку створіть Secretʼи.

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    kubectl create secret generic db-user --from-literal=db-username='db-admin'
    
  • Визначте змінні середовища у специфікації Podʼа.

    apiVersion: v1
    kind: Pod
    metadata:
      name: envvars-multiple-secrets
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: BACKEND_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-user
              key: db-username
    
  • Створіть Pod:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-multiple-secret-env-variable.yaml
    
  • У вашій оболонці покажіть змінні середовища контейнера.

    kubectl exec -i -t envvars-multiple-secrets -- /bin/sh -c 'env | grep _USERNAME'
    

    Вивід схожий на:

    DB_USERNAME=db-admin
    BACKEND_USERNAME=backend-admin
    

налаштування всіх пар ключ-значення в Secret як змінних середовища контейнера

  • Створіть Secret, що містить декілька пар ключ-значення

    kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
    
  • Використовуйте envFrom, щоб визначити всі дані Secret як змінні середовища контейнера. Ключ з Secret стає імʼям змінної середовища у Pod.

    apiVersion: v1
    kind: Pod
    metadata:
      name: envfrom-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        envFrom:
        - secretRef:
            name: test-secret
    
  • Створіть Pod:

    kubectl create -f https://k8s.io/examples/pods/inject/pod-secret-envFrom.yaml
    
  • У вашій оболонці покажіть змінні середовища контейнера username та password.

    kubectl exec -i -t envfrom-secret -- /bin/sh -c 'echo "username: $username\npassword: $password\n"'
    

    Вивід схожий на:

    username: my-app
    password: 39528$vdg7Jb
    

Приклад: Надавання операційних/тестових облікових даних Podʼам за допомогою Secret

У цьому прикладі показано Pod, який використовує Secret, що містить облікові дані операційного середовища, і ще один Pod, який використовує Secret з обліковими даними тестового середовища.

  1. Створіть Secret для облікових даних операційного середовища:

    kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
    

    Вивід схожий на:

    secret "prod-db-secret" created
    
  2. Створіть Secret для облікових даних тестового середовища.

    kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
    

    Вивід схожий на:

    secret "test-db-secret" created
    
  3. Створіть маніфести Podʼів:

    cat <<EOF > pod.yaml
    apiVersion: v1
    kind: List
    items:
    - kind: Pod
      apiVersion: v1
      metadata:
        name: prod-db-pod
      spec:
        containers:
        - name: prod-db-container
          image: mysql
          env:
          - name: MYSQL_ROOT_PASSWORD
            valueFrom:
              secretKeyRef:
                name: prod-db-secret
                key: password
          - name: MYSQL_DATABASE
            value: mydatabase
          - name: MYSQL_USER
            valueFrom:
              secretKeyRef:
                name: prod-db-secret
                key: username
    - kind: Pod
      apiVersion: v1
      metadata:
        name: test-db-pod
      spec:
        containers:
        - name: test-db-container
          image: mysql
          env:
          - name: MYSQL_ROOT_PASSWORD
            valueFrom:
              secretKeyRef:
                name: test-db-secret
                key: password
          - name: MYSQL_DATABASE
            value: mydatabase
          - name: MYSQL_USER
            valueFrom:
              secretKeyRef:
                name: test-db-secret
                key: username
    EOF
    
  4. Застосуйте всі ці обʼєкти на сервер API, виконавши:

    kubectl create -f pod.yaml
    

Обидва контейнери матимуть у своїх файлових системах наступні файли зі значеннями для кожного середовища контейнера:

/etc/secret-volume/username
/etc/secret-volume/password

Ви також можете подальшим спрощенням базової специфікації Podʼа використовуючи два службові облікові записи:

  1. prod-user з prod-db-secret
  2. test-user з test-db-secret

Специфікація Podʼа скорочується до:

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

Довідка

Що далі

  • Дізнайтеся більше про Secret.
  • Дізнайтеся про Томи.

8 - Запуск застосунків

Запуск та керування застосунками зі збереженням стану так і без збереження стану"

8.1 - Запуск застосунку без збереження стану за допомогою Deployment

Ця сторінка показує, як запустити застосунок за допомогою обʼєкта Deployment в Kubernetes.

Цілі

  • Створити розгортання nginx.
  • Використання kubectl для виведення інформації про розгортання.
  • Оновити розгортання.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.9. Для перевірки версії введіть kubectl version.

Створення та дослідження розгортання nginx

Ви можете запустити застосунок, створивши обʼєкт Kubernetes Deployment, і ви можете описати Deployment у файлі YAML. Наприклад, цей файл YAML описує Deployment, який використовує образ Docker nginx:1.14.2:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # вказує Deployment запустити 2 Podʼи, що відповідають шаблону
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  1. Створіть Deployment на основі файлу YAML:

    kubectl apply -f https://k8s.io/examples/application/deployment.yaml
    
  2. Виведіть інформацію про Deployment:

    kubectl describe deployment nginx-deployment
    

    Вивід схожий на:

    Name:     nginx-deployment
    Namespace:    default
    CreationTimestamp:  Tue, 30 Aug 2016 18:11:37 -0700
    Labels:     app=nginx
    Annotations:    deployment.kubernetes.io/revision=1
    Selector:   app=nginx
    Replicas:   2 desired | 2 updated | 2 total | 2 available | 0 unavailable
    StrategyType:   RollingUpdate
    MinReadySeconds:  0
    RollingUpdateStrategy:  1 max unavailable, 1 max surge
    Pod Template:
      Labels:       app=nginx
      Containers:
        nginx:
        Image:              nginx:1.14.2
        Port:               80/TCP
        Environment:        <none>
        Mounts:             <none>
      Volumes:              <none>
    Conditions:
      Type          Status  Reason
      ----          ------  ------
      Available     True    MinimumReplicasAvailable
      Progressing   True    NewReplicaSetAvailable
    OldReplicaSets:   <none>
    NewReplicaSet:    nginx-deployment-1771418926 (2/2 replicas created)
    No events.
    
  3. Перегляньте Podʼи, створені розгортанням:

    kubectl get pods -l app=nginx
    

    Вивід схожий на:

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1771418926-7o5ns   1/1       Running   0          16h
    nginx-deployment-1771418926-r18az   1/1       Running   0          16h
    
  4. Виведіть інформацію про Pod:

    kubectl describe pod <pod-name>
    

    де <pod-name> — це імʼя одного з ваших Podʼів.

Оновлення розгортання

Ви можете оновити розгортання, застосувавши новий файл YAML. Цей файл YAML вказує, що розгортання повинне бути оновлене до nginx 1.16.1.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1 # Оновлення версії nginx з 1.14.2 до 1.16.1
        ports:
        - containerPort: 80
  1. Застосуйте новий файл YAML:

    kubectl apply -f https://k8s.io/examples/application/deployment-update.yaml
    
  2. Спостерігайте, як розгортання створює Podʼи з новими іменами і видаляє старі Podʼи:

    kubectl get pods -l app=nginx
    

Масштабування застосунку шляхом збільшення кількості реплік

Ви можете збільшити кількість Podʼів у вашому Deployment, застосувавши новий YAML файл. Цей файл YAML встановлює replicas на 4, що вказує, що Deployment повиннен мати чотири Podʼи:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 4 # Update the replicas from 2 to 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80
  1. Застосуйте новий файл YAML:

    kubectl apply -f https://k8s.io/examples/application/deployment-scale.yaml
    
  2. Перевірте, що Deployment має чотири Podʼи:

    kubectl get pods -l app=nginx
    

    Вивід схожий на:

    NAME                               READY     STATUS    RESTARTS   AGE
    nginx-deployment-148880595-4zdqq   1/1       Running   0          25s
    nginx-deployment-148880595-6zgi1   1/1       Running   0          25s
    nginx-deployment-148880595-fxcez   1/1       Running   0          2m
    nginx-deployment-148880595-rwovn   1/1       Running   0          2m
    

Видалення Deployment

Видаліть Deployment за іменем:

kubectl delete deployment nginx-deployment

ReplicationControllers — Старий спосіб

Перевагою створення реплікованих застосунків є використання Deployment, який своєю чергою використовує ReplicaSet. До того, як в Kubernetes були додані Deployment та ReplicaSet, репліковані застосунки конфігурувалися за допомогою ReplicationController.

Що далі

8.2 - Запуск одноекземплярного застосунку зі збереженням стану

На цій сторінці показано, як запустити одноекземплярний застосунок зі збереженням стану (Stateful) в Kubernetes, використовуючи PersistentVolume та Deployment. Застосунок — MySQL.

Цілі

  • Створити PersistentVolume, посилаючись на диск у вашому середовищі.
  • Створити Deployment MySQL.
  • Використання MySQL для інших Podʼів в кластері за відомим DNS-імʼям.

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

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

    Для перевірки версії введіть kubectl version.

  • Вам потрібно мати або динамічний провізор PersistentVolume з типовим StorageClass, або статично надавати PersistentVolume самостійно, щоб задовольнити PersistentVolumeClaims, що використовується тут.

Розгортання MySQL

Ви можете запустити stateful застосунок, створивши Deployment Kubernetes та підʼєднавши його до наявного PersistentVolume за допомогою PersistentVolumeClaim. Наприклад, цей файл YAML описує Deployment, що запускає MySQL та посилається на PersistentVolumeClaim. Файл визначає монтування тому для /var/lib/mysql, а потім створює PersistentVolumeClaim, який шукає том 20G. Ця заявка виконується будь-яким наявним томом, який відповідає вимогам, або за допомогою динамічного провізора.

Примітка: пароль визначений в файлі конфігурації yaml, і це не є безпечним. Див. Kubernetes Secrets для безпечнішого рішення.

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          # Використання secret у реальному використанні
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  1. Розгорніть PV та PVC з файлу YAML:

    kubectl apply -f https://k8s.io/examples/application/mysql/mysql-pv.yaml
    
  2. Розгорніть вміст файлу YAML:

    kubectl apply -f https://k8s.io/examples/application/mysql/mysql-deployment.yaml
    
  3. Показати інформацію про Deployment:

    kubectl describe deployment mysql
    

    Вивід схожий на:

    Name:                 mysql
    Namespace:            default
    CreationTimestamp:    Tue, 01 Nov 2016 11:18:45 -0700
    Labels:               app=mysql
    Annotations:          deployment.kubernetes.io/revision=1
    Selector:             app=mysql
    Replicas:             1 desired | 1 updated | 1 total | 0 available | 1 unavailable
    StrategyType:         Recreate
    MinReadySeconds:      0
    Pod Template:
      Labels:       app=mysql
      Containers:
        mysql:
        Image:      mysql:5.6
        Port:       3306/TCP
        Environment:
          MYSQL_ROOT_PASSWORD:      password
        Mounts:
          /var/lib/mysql from mysql-persistent-storage (rw)
      Volumes:
        mysql-persistent-storage:
        Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
        ClaimName:  mysql-pv-claim
        ReadOnly:   false
    Conditions:
      Type          Status  Reason
      ----          ------  ------
      Available     False   MinimumReplicasUnavailable
      Progressing   True    ReplicaSetUpdated
    OldReplicaSets:       <none>
    NewReplicaSet:        mysql-63082529 (1/1 replicas created)
    Events:
      FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
      ---------    --------    -----    ----                -------------    --------    ------            -------
      33s          33s         1        {deployment-controller }             Normal      ScalingReplicaSet Scaled up replica set mysql-63082529 to 1
    
  4. Перегляньте Podʼи, створені Deployment:

    kubectl get pods -l app=mysql
    

    Вивід схожий на:

    NAME                   READY     STATUS    RESTARTS   AGE
    mysql-63082529-2z3ki   1/1       Running   0          3m
    
  5. Перевірка PersistentVolumeClaim:

    kubectl describe pvc mysql-pv-claim
    

    Вивід схожий на:

    Name:         mysql-pv-claim
    Namespace:    default
    StorageClass:
    Status:       Bound
    Volume:       mysql-pv-volume
    Labels:       <none>
    Annotations:    pv.kubernetes.io/bind-completed=yes
                    pv.kubernetes.io/bound-by-controller=yes
    Capacity:     20Gi
    Access Modes: RWO
    Events:       <none>
    

Доступ до екземпляра MySQL

Попередній YAML-файл створює Service, який дозволяє іншим Podʼам в кластері отримувати доступ до бази даних. Опція Service clusterIP: None дозволяє імені DNS Serviceʼа безпосередньо посилатись на IP-адресу Podʼа. Це оптимально, коли у вас є лише один Pod за Service і ви не маєте наміру збільшувати кількість Podʼів.

Запустіть клієнта MySQL, щоб підʼєднатися до сервера:

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword

Ця команда створює новий Pod у кластері, що виконує клієнта MySQL та підʼєднується до сервера через Service. Якщо підʼєднання вдалося, ви знаєте, що ваша stateful база даних MySQL працює.

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

mysql>

Оновлення

Образ або будь-яку іншу частину Deployment можна оновити як зазвичай за допомогою команди kubectl apply. Ось деякі заходи обережності, які специфічні для застосунків зі збереженням стану:

  • Не масштабуйте застосунок. Цей варіант призначений лише для застосунків з одним екземпляром. Основний PersistentVolume можна примонтувати лише до одного Podʼа. Для кластеризованих застосунків зі збереженням стану див. документацію StatefulSet.
  • Використовуйте strategy: type: Recreate в файлі конфігурації YAML Deployment. Це вказує Kubernetes не використовувати постійні (rolling) оновлення. Такі оновлення не працюватимуть, оскільки ви не можете мати більше одного Podʼа, що працює одночасно. Стратегія Recreate зупинить перший Pod перед створенням нового з оновленою конфігурацією.

Видалення Deployment

Видаліть розгорнуті обʼєкти за іменем:

kubectl delete deployment,svc mysql
kubectl delete pvc mysql-pv-claim
kubectl delete pv mysql-pv-volume

Якщо ви вручну вказували PersistentVolume, вам також потрібно вручну видалити його, а також звільнити основний ресурс. Якщо ви використовували динамічного провізора, він автоматично видаляє PersistentVolume, коли бачить, що ви видалили PersistentVolumeClaim. Деякі динамічні провізори (наприклад, ті, що стосуються EBS та PD) також звільняють основний ресурс після видалення PersistentVolume.

Що далі

8.3 - Запуск реплікованого застосунку зі збереженням стану

Ця сторінка показує, як запустити реплікований застосунок зі збереженням стану (StatefulSet). Цей застосунок — реплікована база даних MySQL. У топології цього прикладу є один головний сервер і кілька реплік, що використовують асинхронну реплікацію на основі рядків.

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

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

  • Вам потрібно мати або динамічний провізор PersistentVolume з типовим StorageClass, або статично надавати PersistentVolume самостійно, щоб задовольнити PersistentVolumeClaims, що використовується тут.

  • Це завдання передбачає, що ви знаєте про Постійні томи та StatefulSets,а також інші основні поняття, такі як Pod, Service та ConfigMap.
  • Трохи знань MySQL корисні, але цей посібник має на меті представити загальні патерни, які можуть бути корисними для інших систем.
  • Ви використовуєте простір імен default або інший простір імен, в якому відсутні обʼєкти, що конфліктують.

Цілі

  • Розгорнути репліковану топологію MySQL за допомогою StatefulSet.
  • Направити трафік від клієнта MySQL.
  • Спостерігати стійкість до перерв у роботі.
  • Масштабувати StatefulSet вгору та вниз.

Розгортання MySQL

Приклад розгортання складається з ConfigMap, двох Service та StatefulSet.

Створення ConfigMap

Створіть ConfigMap із наступного файлу конфігурації YAML:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
data:
  primary.cnf: |
    # Застосовує цю конфігурацію до primary.
    [mysqld]
    log-bin    
  replica.cnf: |
    # Застосовує цю конфігурацію до реплік.
    [mysqld]
    super-read-only    

kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml

Цей ConfigMap надає перевизначення для my.cnf, що дозволяє вам незалежно керувати конфігурацією на головному сервері MySQL та його репліках. У цьому випадку ви хочете, щоб головний сервер міг обслуговувати логи реплікацій реплік, а репліки відхиляли будь-які записи, які надходять не через реплікацію.

Немає нічого особливого у самому ConfigMap, що зумовлює застосування різних частин до різних Podʼів. Кожен Pod вирішує, яку частину дивитися при ініціалізації, на основі інформації, що надає контролер StatefulSet.

Створення Service

Створіть Service із наступного файлу конфігурації YAML:

# Service headless для стабільних записів DNS членів StatefulSet.
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
spec:
  ports:
  - name: mysql
    port: 3306
  clusterIP: None
  selector:
    app: mysql
---
# Service клієнт для підʼєднання до екземпляру MySQL для читання.
# Для запису ви повинні підключитися до основного: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
  name: mysql-read
  labels:
    app: mysql
    app.kubernetes.io/name: mysql
    readonly: "true"
spec:
  ports:
  - name: mysql
    port: 3306
  selector:
    app: mysql
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml

Service headless надає місце для DNS-записів, які контролери StatefulSet створюють для кожного Podʼа, що є частиною набору. Оскільки headless Service називається mysql, Podʼи доступні за допомогою зіставлення <pod-name>.mysql з будь-якого іншого Podʼа в тому ж Kubernetes кластері та просторі імен.

Клієнтський Service, з назвою mysql-read, є звичайним Service з власним кластерним IP, який розподіляє підключення між всіма Podʼами MySQL, які повідомляють про готовність. Набір потенційних точок доступу включає головний сервер MySQL та всі репліки.

Зверніть увагу, що лише запити на читання можуть використовувати балансувальник Service. Оскільки існує лише один головний сервер MySQL, клієнти повинні підключатися безпосередньо до головного Podʼа MySQL (через його DNS-запис у головному Service) для виконання записів.

Створення StatefulSet

Наостанок, створіть StatefulSet із наступного файлу конфігурації YAML:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
      app.kubernetes.io/name: mysql
  serviceName: mysql
  replicas: 3
  template:
    metadata:
      labels:
        app: mysql
        app.kubernetes.io/name: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:5.7
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Generate mysql server-id from pod ordinal index.
          [[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          echo [mysqld] > /mnt/conf.d/server-id.cnf
          # Add an offset to avoid reserved server-id=0 value.
          echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
          # Copy appropriate conf.d files from config-map to emptyDir.
          if [[ $ordinal -eq 0 ]]; then
            cp /mnt/config-map/primary.cnf /mnt/conf.d/
          else
            cp /mnt/config-map/replica.cnf /mnt/conf.d/
          fi          
        volumeMounts:
        - name: conf
          mountPath: /mnt/conf.d
        - name: config-map
          mountPath: /mnt/config-map
      - name: clone-mysql
        image: gcr.io/google-samples/xtrabackup:1.0
        command:
        - bash
        - "-c"
        - |
          set -ex
          # Skip the clone if data already exists.
          [[ -d /var/lib/mysql/mysql ]] && exit 0
          # Skip the clone on primary (ordinal index 0).
          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
          ordinal=${BASH_REMATCH[1]}
          [[ $ordinal -eq 0 ]] && exit 0
          # Clone data from previous peer.
          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
          # Prepare the backup.
          xtrabackup --prepare --target-dir=/var/lib/mysql          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
      containers:
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ALLOW_EMPTY_PASSWORD
          value: "1"
        ports:
        - name: mysql
          containerPort: 3306
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 500m
            memory: 1Gi
        livenessProbe:
          exec:
            command: ["mysqladmin", "ping"]
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
        readinessProbe:
          exec:
            # Перевіряє, чи можемо ми виконувати запити через TCP (skip-networking вимкнено).
            command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 1
      - name: xtrabackup
        image: gcr.io/google-samples/xtrabackup:1.0
        ports:
        - name: xtrabackup
          containerPort: 3307
        command:
        - bash
        - "-c"
        - |
          set -ex
          cd /var/lib/mysql

          # Determine binlog position of cloned data, if any.
          if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
            # XtraBackup already generated a partial "CHANGE MASTER TO" query
            # because we're cloning from an existing replica. (Need to remove the tailing semicolon!)
            cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
            # Ignore xtrabackup_binlog_info in this case (it's useless).
            rm -f xtrabackup_slave_info xtrabackup_binlog_info
          elif [[ -f xtrabackup_binlog_info ]]; then
            # We're cloning directly from primary. Parse binlog position.
            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
            rm -f xtrabackup_binlog_info xtrabackup_slave_info
            echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
                  MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
          fi

          # Check if we need to complete a clone by starting replication.
          if [[ -f change_master_to.sql.in ]]; then
            echo "Waiting for mysqld to be ready (accepting connections)"
            until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done

            echo "Initializing replication from clone position"
            mysql -h 127.0.0.1 \
                  -e "$(<change_master_to.sql.in), \
                          MASTER_HOST='mysql-0.mysql', \
                          MASTER_USER='root', \
                          MASTER_PASSWORD='', \
                          MASTER_CONNECT_RETRY=10; \
                        START SLAVE;" || exit 1
            # In case of container restart, attempt this at-most-once.
            mv change_master_to.sql.in change_master_to.sql.orig
          fi

          # Start a server to send backups when requested by peers.
          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
            "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          subPath: mysql
        - name: conf
          mountPath: /etc/mysql/conf.d
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
      volumes:
      - name: conf
        emptyDir: {}
      - name: config-map
        configMap:
          name: mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-statefulset.yaml

Ви можете спостерігати за процесом запуску, виконавши:

kubectl get pods -l app=mysql --watch

Через деякий час ви повинні побачити, що всі 3 Podʼи стануть Running:

NAME      READY     STATUS    RESTARTS   AGE
mysql-0   2/2       Running   0          2m
mysql-1   2/2       Running   0          1m
mysql-2   2/2       Running   0          1m

Натисніть Ctrl+C, щоб скасувати перегляд.

Цей маніфест використовує різноманітні техніки для керування Podʼами як частиною StatefulSet. У наступному розділі підкреслено деякі з цих технік, щоб пояснити, що відбувається під час створення Podʼів StatefulSet.

Розуміння ініціалізації Podʼа зі збереженням стану

Контролер StatefulSet запускає Podʼи по одному, в порядку їх індексів. Він чекає, доки кожен Pod не повідомить про готовність, перш ніж запустити наступний.

Крім того, контролер призначає кожному Podʼу унікальне, стабільне імʼя у формі <statefulset-name>-<ordinal-index>, в результаті отримуємо Podʼи з іменами mysql-0, mysql-1 та mysql-2.

Шаблон Podʼа у вищенаведеному маніфесті StatefulSet використовує ці властивості, щоб здійснити впорядкований запуск реплікації MySQL.

Створення конфігурації

Перед запуском будь-яких контейнерів у специфікації Podʼа, спочатку Pod запускає будь-які контейнери ініціалізації у визначеному порядку.

Перший контейнер ініціалізації, init-mysql, генерує спеціальні конфігураційні файли MySQL на основі порядкового індексу.

Скрипт визначає свій власний порядковий індекс, витягуючи його з кінця імені Podʼа, яке повертається командою hostname. Потім він зберігає порядковий індекс (з числовим зміщенням для уникнення зарезервованих значень) у файлі з назвою server-id.cnf в теці conf.d MySQL. Це перетворює унікальний, стабільний ідентифікатор, наданий StatefulSet, у домен ідентифікаторів серверів MySQL, які вимагають таких же властивостей.

Скрипт у контейнері init-mysql також застосовує або primary.cnf, або replica.cnf з ConfigMap, копіюючи вміст у conf.d. Оскільки у топології цього прикладу є лише один головний сервер MySQL та будь-яка кількість реплік, скрипт призначає порядковий індекс 0 головному серверу, а всі інші — репліками. Разом з гарантією контролера StatefulSet щодо порядку розгортання, це забезпечує готовність головного сервера MySQL перед створенням реплік, щоб вони могли почати реплікацію.

Клонування наявних даних

Загалом, коли новий Pod приєднується до набору як репліка, він повинен припускати, що головний сервер MySQL може вже містити дані. Також він повинен припускати, що логи реплікації можуть не бути повністю отримані з самого початку. Ці консервативні припущення є ключем до можливості запуску робочого StatefulSet масштабуватися вгору та вниз з часом, а не залишатися фіксованим на своєму початковому розмірі.

Другий контейнер ініціалізації, з назвою clone-mysql, виконує операцію клонування на реплікаційному Podʼі першого разу, коли він запускається на порожньому PersistentVolume. Це означає, що він копіює всі наявні дані з іншого працюючого Podʼа, таким чином, його локальний стан є достатньо консистентним для початку реплікації з головного сервера.

MySQL сам по собі не надає механізму для цього, тому приклад використовує популярний відкритий інструмент — Percona XtraBackup. Під час клонування MySQL сервер може мати знижену продуктивність. Щоб мінімізувати вплив на головний сервер MySQL, скрипт вказує кожному Podʼу отримувати дані з Podʼа, чий порядковий індекс на одиницю менший. Це працює через те, що контролер StatefulSet завжди гарантує, що Pod N є готовим перед запуском Podʼа N+1.

Початок реплікації

Після успішного завершення контейнерів ініціалізації запускаються звичайні контейнери. Podʼи MySQL складаються з контейнера mysql, в якому працює фактичний сервер mysqld, та контейнера xtrabackup, який діє як sidecar.

Sidecar xtrabackup переглядає клоновані файли даних і визначає, чи необхідно ініціалізувати реплікацію MySQL на репліці. У цьому випадку він чекає, доки mysqld буде готовий, а потім виконує команди CHANGE MASTER TO та START SLAVE з параметрами реплікації, отриманими з клонованих файлів XtraBackup.

Після того як репліка починає реплікацію, вона запамʼятовує свій головний сервер MySQL і автоматично підʼєднується, якщо сервер перезавантажується або зʼєднання втрачається. Також, оскільки репліки шукають головний сервер за його стабільним DNS-імʼям (mysql-0.mysql), вони автоматично знаходять головний сервер навіть якщо він отримує новий IP Podʼа через перепланування.

Нарешті, після початку реплікації, контейнер xtrabackup слухає зʼєднання з інших Podʼів, які запитують клон даних. Цей сервер залишається активним нескінченно довго в разі, якщо StatefulSet масштабується вгору, або якщо наступний Pod втрачає свій PersistentVolumeClaim і потрібно повторно виконати клонування.

Надсилання клієнтського трафіку

Ви можете надіслати тестові запити до головного сервера MySQL (хост mysql-0.mysql) запустивши тимчасовий контейнер з образом mysql:5.7 і використовуючи бінарний файл клієнта mysql.

kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
  mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF

Використовуйте хост mysql-read, щоб надіслати тестові запити до будь-якого сервера, який повідомляє про готовність:

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-read -e "SELECT * FROM test.messages"

Ви повинні отримати вивід схожий на цей:

Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

Щоб продемонструвати, що Service mysql-read розподіляє підключення між серверами, ви можете запустити SELECT @@server_id у циклі:

kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
  bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"

Ви повинні бачити, що @@server_id змінюється випадковим чином, оскільки може бути вибрана інша точка доступу при кожній спробі підключення:

+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         100 | 2006-01-02 15:04:05 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         102 | 2006-01-02 15:04:06 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         101 | 2006-01-02 15:04:07 |
+-------------+---------------------+

Ви можете натиснути Ctrl+C, коли захочете зупинити цикл, але цікаво тримати його запущеним в іншому вікні, щоб бачити ефект від наступних кроків.

Симуляція відмови Podʼа та Вузла

Щоб продемонструвати підвищену доступність читання з пулу реплік замість одного сервера, залиште цикл SELECT @@server_id, запущений вище, активним, тоді як ви змушуєте Pod вийти зі стану Ready.

Збій проби готовності

Проба готовності для контейнера mysql виконує команду mysql -h 127.0.0.1 -e 'SELECT 1', щоб переконатися, що сервер запущений і може виконувати запити.

Одним зі способів змусити цю пробу готовності збоїти — це пошкодити цю команду:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off

Ця дія втручається у файлову систему контейнера Podʼа mysql-2 і перейменовує команду mysql, щоб проба готовності не могла її знайти. Через кілька секунд Pod повинен повідомити про один зі своїх контейнерів як неготовий, що можна перевірити за допомогою:

kubectl get pod mysql-2

Перевірте значення 1/2 у стовпці READY:

NAME      READY     STATUS    RESTARTS   AGE
mysql-2   1/2       Running   0          3m

На цьому етапі ви повинні бачити продовження роботи вашого циклу SELECT @@server_id, хоча він більше не повідомляє про 102. Нагадаємо, що скрипт init-mysql визначив server-id як 100 + $ordinal, тож ідентифікатор сервера 102 відповідає Поду mysql-2.

Тепер відновіть Pod і він повинен знову зʼявитися у виводі циклу через кілька секунд:

kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql

Видалення Podʼів

Контролер StatefulSet також створює Podʼи знову, якщо вони видалені, подібно до того, як це робить ReplicaSet для Podʼів без збереження стану.

kubectl delete pod mysql-2

Контролер StatefulSet помічає, що Podʼа mysql-2 більше не існує, і створює новий з тією ж назвою та повʼязаний з тим самим PersistentVolumeClaim. Ви повинні побачити, що ідентифікатор сервера 102 зникне з виводу циклу протягом певного часу і потім повернеться самостійно.

Виведення вузла з експлуатації

Якщо у вашому кластері Kubernetes є кілька вузлів, ви можете симулювати відмову вузла(наприклад, під час оновлення вузлів) за допомогою команди drain.

Спочатку визначте, на якому вузлі знаходиться один із Podʼів MySQL:

kubectl get pod mysql-2 -o wide

Назва вузла повинна зʼявитися у останньому стовпчику:

NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-2   2/2       Running   0          15m       10.244.5.27   kubernetes-node-9l2t

Потім виведіть вузол з експлуатації, виконавши наступну команду, яка забороняє новим Podʼам запускатися на цьому вузлі та видаляє будь-які існуючі Podʼи. Замість <node-name> підставте назву вузла, яку ви знайшли на попередньому кроці.

# Дивіться вище поради щодо впливу на інші завдання
kubectl drain <node-name> --force --delete-emptydir-data --ignore-daemonsets

Тепер ви можете спостерігати, як Pod переплановується на іншому вузлі:

kubectl get pod mysql-2 -o wide --watch

Це має виглядати приблизно так:

NAME      READY   STATUS          RESTARTS   AGE       IP            NODE
mysql-2   2/2     Terminating     0          15m       10.244.1.56   kubernetes-node-9l2t
[...]
mysql-2   0/2     Pending         0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:0/2        0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:1/2        0          20s       10.244.5.32   kubernetes-node-fjlm
mysql-2   0/2     PodInitializing 0          21s       10.244.5.32   kubernetes-node-fjlm
mysql-2   1/2     Running         0          22s       10.244.5.32   kubernetes-node-fjlm
mysql-2   2/2     Running         0          30s       10.244.5.32   kubernetes-node-fjlm

І знову, ви повинні побачити, що ідентифікатор сервера 102 зник з виводу циклу SELECT @@server_id протягом певного часу, а потім повернувся.

Тепер знову дозвольте вузлу приймати навантаження:

kubectl uncordon <node-name>

Масштабування кількості реплік

Коли ви використовуєте реплікацію MySQL, ви можете збільшувати кількість запитів на читання, додаючи репліки. Для StatefulSet це можна зробити однією командою:

kubectl scale statefulset mysql --replicas=5

Спостерігайте, як нові Podʼи запускаються, виконавши:

kubectl get pods -l app=mysql --watch

Коли вони будуть готові, ви побачите, що ідентифікатори серверів 103 та 104 починають зʼявлятися у виводі циклу SELECT @@server_id.

Ви також можете перевірити, що ці нові сервери мають дані, які ви додали до них до того, як вони існували:

kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-3.mysql -e "SELECT * FROM test.messages"
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted

Зменшити кількість реплік також можна однією командою:

kubectl scale statefulset mysql --replicas=3

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

kubectl get pvc -l app=mysql

Це покаже, що всі 5 PVC все ще існують, попри те, що StatefulSet був зменшений до 3:

NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
data-mysql-0   Bound     pvc-8acbf5dc-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-1   Bound     pvc-8ad39820-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-2   Bound     pvc-8ad69a6d-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-3   Bound     pvc-50043c45-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m
data-mysql-4   Bound     pvc-500a9957-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m

Якщо ви не збираєтеся повторно використовувати додаткові PVC, ви можете їх видалити:

kubectl delete pvc data-mysql-3
kubectl delete pvc data-mysql-4

Очищення

  1. Припинить цикл SELECT @@server_id, натиснувши Ctrl+C в його терміналі або виконавши наступне з іншого терміналу:

    kubectl delete pod mysql-client-loop --now
    
  2. Видаліть StatefulSet. Це також розпочне завершення Podʼів.

    kubectl delete statefulset mysql
    
  3. Перевірте, що Podʼи зникають. Вони можуть зайняти деякий час для завершення роботи.

    kubectl get pods -l app=mysql
    

    Ви будете знати, що Podʼи завершилися, коли вищезазначена команда поверне:

    No resources found.
    
  4. Видаліть ConfigMap, Services та PersistentVolumeClaims.

    kubectl delete configmap,service,pvc -l app=mysql
    
  5. Якщо ви вручну створювали PersistentVolumes, вам також потрібно вручну видалити їх, а також звільнити відповідні ресурси. Якщо ви використовували динамічний провізор, він автоматично видаляє PersistentVolumes, коли бачить, що ви видалили PersistentVolumeClaims. Деякі динамічні провізори (такі як ті, що стосуються EBS та PD) також звільняють відповідні ресурси при видаленні PersistentVolumes.

Що далі

8.4 - Масштабування StatefulSet

Це завдання показує, як масштабувати StatefulSet. Масштабування StatefulSet означає збільшення або зменшення кількості реплік.

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

  • StatefulSets доступні тільки в версії Kubernetes 1.5 або пізніше. Щоб перевірити вашу версію Kubernetes, виконайте kubectl version.

  • Не всі застосунки зі збереженням стану гарно масштабуються. Якщо ви не впевнені, чи масштабувати ваші StatefulSets, див. Концепції StatefulSet або Посібник StatefulSet для отримання додаткової інформації.

  • Ви повинні виконувати масштабування лише тоді, коли ви впевнені, що ваш кластер застосунку зі збереженням стану є повністю справним.

Масштабування StatefulSets

Використання kubectl для масштабування StatefulSets

Спочатку знайдіть StatefulSet, який ви хочете масштабувати.

kubectl get statefulsets <назва-stateful-set>

Змініть кількість реплік вашого StatefulSet:

kubectl scale statefulsets <назва-stateful-set> --replicas=<нові-репліки>

Виконання оновлень на місці для вашого StatefulSets

Альтернативно, ви можете виконати оновлення на місці для ваших StatefulSets.

Якщо ваш StatefulSet спочатку був створений за допомогою kubectl apply, оновіть .spec.replicas маніфестів StatefulSet, а потім виконайте kubectl apply:

kubectl apply -f <stateful-set-file-updated>

Інакше, відредагуйте це поле за допомогою kubectl edit:

kubectl edit statefulsets <stateful-set-name>

Або використовуйте kubectl patch:

kubectl patch statefulsets <назва-stateful-set> -p '{"spec":{"replicas":<нові-репліки>}}'

Усунення несправностей

Масштабування вниз не працює правильно

Ви не можете зменшити масштаб StatefulSet, коли будь-який з Stateful Podʼів, яким він керує, є нездоровим. Масштабування відбувається лише після того, як ці Stateful Podʼи стають запущеними та готовими.

Якщо spec.replicas > 1, Kubernetes не може визначити причину несправності Podʼа. Це може бути наслідком постійного збою або тимчасового збою. Тимчасовий збій може бути викликаний перезапуском, необхідним для оновлення або обслуговування.

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

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

Що далі

8.5 - Видалення StatefulSet

Це завдання показує, як видалити StatefulSet.

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

  • Це завдання передбачає, що у вас є застосунок, який працює на вашому кластері та представлений StatefulSet.

Видалення StatefulSet

Ви можете видалити StatefulSet так само як і інші ресурси в Kubernetes: використовуйте команду kubectl delete та вкажіть StatefulSet або файлом, або імʼям.

kubectl delete -f <file.yaml>
kubectl delete statefulsets <statefulset-name>

Після видалення StatefulSet може знадобитися окремо видалити повʼязаний headless service.

kubectl delete service <імʼя-сервісу>

Під час видалення StatefulSet через kubectl, StatefulSet масштабується до 0. Всі Podʼи, які є частиною цього робочого навантаження, також видаляються. Якщо ви хочете видалити лише StatefulSet і не Podʼи, використовуйте --cascade=orphan. Наприклад:

kubectl delete -f <файл.yaml> --cascade=orphan

Передачею --cascade=orphan до kubectl delete Podʼи, що керуються StatefulSet залишаються після того, як обʼєкт StatefulSet сам по собі буде видалений. Якщо Podʼи мають мітку app.kubernetes.io/name=MyApp, ви можете видалити їх наступним чином:

kubectl delete pods -l app.kubernetes.io/name=MyApp

Постійні томи

Видалення Podʼів у StatefulSet не призведе до видалення повʼязаних томів. Це зроблено для того, щоб ви мали можливість скопіювати дані з тому перед його видаленням. Видалення PVC після завершення роботи Podʼів може спричинити видалення зазначених постійних томів залежно від класу сховища та політики повторного використання. Ніколи не припускайте можливість доступу до тому після видалення заявки.

Повне видалення StatefulSet

Щоб видалити все у StatefulSet, включаючи повʼязані Podʼи, ви можете виконати серію команд, схожих на наступні:

grace=$(kubectl get pods <под-stateful-set> --template '{{.spec.terminationGracePeriodSeconds}}')
kubectl delete statefulset -l app.kubernetes.io/name=MyApp
sleep $grace
kubectl delete pvc -l app.kubernetes.io/name=MyApp

У вище наведеному прикладі Podʼи мають мітку app.kubernetes.io/name=MyApp; підставте вашу власну мітку за потреби.

Примусове видалення Podʼів StatefulSet

Якщо ви помітите, що деякі Podʼи у вашому StatefulSet застрягли у стані 'Terminating' або 'Unknown' протягом тривалого періоду часу, вам може знадобитися втрутитися вручну, щоб примусово видалити Podʼи з apiserverʼа. Це потенційно небезпечне завдання. Див. Примусове видалення Podʼів StatefulSet для отримання детальної інформації.

Що далі

Дізнайтеся більше про примусове видалення Podʼів StatefulSet.

8.6 - Примусове видалення Podʼів StatefulSet

Ця сторінка показує, як видаляти Podʼи, які є частиною StatefulSet, та пояснює важливі моменти, які слід враховувати під час цього.

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

  • Це досить високорівневе завдання і може порушити деякі властивості, притаманні StatefulSet.
  • Перед продовженням ознайомтеся з розглянутими нижче моментами.

Міркування про StatefulSet

При нормальному функціонуванні StatefulSet ніколи немає потреби у примусовому видаленні Podʼів StatefulSet. Контролер StatefulSet відповідає за створення, масштабування та видалення членів StatefulSet. Він намагається забезпечити, щоб зазначена кількість Podʼів від ordinal 0 до N-1 були справними та готовими. StatefulSet забезпечує те, що в будь-який момент часу в кластері працює не більше одного Podʼа з заданою ідентичністю. Це називається семантикою як максимум один, яку забезпечує StatefulSet.

Примусове видалення слід виконувати з обережністю, оскільки воно може порушити семантику "як максимум один", притаманну StatefulSet. StatefulSet можуть використовуватися для запуску розподілених і кластерних застосунків, які потребують стабільної мережевої ідентичності та стабільного сховища. Ці застосунки часто мають конфігурацію, яка ґрунтується на ансамблі фіксованої кількості членів з фіксованими ідентичностями. Наявність декількох членів із тією самою ідентичністю може бути руйнівною і може призвести до втрати даних (наприклад, у випадку "розщеплення мозку" в системах на основі кворуму).

Видалення Podʼів

Ви можете виконати коректне видалення Podʼа за допомогою наступної команди:

kubectl delete pods <pod>

Щоб таке видалення призвело до коректного завершення, необхідно вказати pod.Spec.TerminationGracePeriodSeconds більше 0. Практика встановлення pod.Spec.TerminationGracePeriodSeconds на 0 секунд є небезпечною та категорично не рекомендується для Podʼів StatefulSet. Коректне видалення є безпечним і забезпечить, що Pod коректно завершить роботу перед тим, як kubelet видалить імʼя з apiserver.

Pod не видаляється автоматично, коли вузол недоступний. Podʼи, які працюють на недоступному вузлі, потрапляють у стан 'Terminating' або 'Unknown' після тайм-ауту. Podʼи також можуть потрапляти в ці стани, коли користувач спробує коректне видалення Podʼа на недоступному вузлі. Єдині способи, якими Pod у такому стані може бути видалено з apiserver, наступні:

  • Обʼєкт Node видаляється (або вами, або Контролером Вузлів).
  • kubelet на недоступному Вузлі починає відповідати, припиняє роботу Pod та видаляє запис з apiserver.
  • Примусове видалення Podʼа користувачем.

Рекомендована практика — використовувати перший або другий підхід. Якщо вузол підтверджено є несправним (наприклад, постійно відключений від мережі, вимкнений тощо), то видаліть обʼєкт Node. Якщо вузол страждає від розділення мережі, то спробуйте вирішити це або зачекайте на його вирішення. Коли розділення мережі виправляється, kubelet завершить видалення Podʼа та звільнить його імʼя в apiserver.

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

Примусове видалення

Примусові видалення не чекають підтвердження від kubelet про те, що Pod було завершено. Незалежно від того, чи успішно примусове завершення Podʼа припиняє його роботу чи ні, воно негайно звільняє імʼя з apiserverʼа. Це дозволить контролеру StatefulSet створити новий Pod з тією самою ідентичністю; це може призвести до дублювання все ще працюючого Podʼа, і якщо цей Pod все ще може спілкуватися з іншими членами StatefulSet, це порушить семантику "як максимум один", яку StatefulSet має гарантувати.

Коли ви примусово видаляєте Pod StatefulSet, ви стверджуєте, що цей Pod більше ніколи не буде знову взаємодіяти з іншими Podʼами StatefulSet, і його імʼя може бути безпечно звільнено для створення заміни.

Якщо ви хочете примусово видалити Pod за допомогою kubectl версії >= 1.5, виконайте наступне:

kubectl delete pods <pod> --grace-period=0 --force

Якщо ви використовуєте будь-яку версію kubectl <= 1.4, ви повинні пропустити параметр --force і використовувати:

kubectl delete pods <pod> --grace-period=0

Якщо навіть після цих команд Pod застряг у стані Unknown, використайте наступну команду для видалення Podʼа з кластера:

kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'

Завжди виконуйте примусове видалення Podʼів StatefulSet обережно та з повним розумінням ризиків, повʼязаних із цим.

Що далі

Дізнайтеся більше про налагодження StatefulSet.

8.7 - Горизонтальне автомасштабування Podʼів

У Kubernetes HorizontalPodAutoscaler автоматично оновлює ресурс навантаження (наприклад, Deployment або StatefulSet), з метою автоматичного масштабування робочого навантаження у відповідь на попит.

Горизонтальне масштабування означає, що реакція на збільшення навантаження полягає у розгортанні додаткових Podʼів. Це відрізняється від вертикального масштабування, що для Kubernetes означає призначення додаткових ресурсів (наприклад, памʼяті або CPU) для уже запущених Podʼів робочого навантаження.

Якщо навантаження зменшується, а кількість Podʼів перевищує налаштований мінімум, HorizontalPodAutoscaler інструктує ресурс навантаження (Deployment, StatefulSet або інший схожий ресурс) масштабуватися вниз.

Горизонтальне автоматичне масштабування Podʼів не застосовується до обʼєктів, які не можна масштабувати (наприклад, DaemonSet).

HorizontalPodAutoscaler реалізований як ресурс API Kubernetes та контролер. Ресурс визначає поведінку контролера. Контролер горизонтального автоматичного масштабування Podʼів, який працює в панелі управління Kubernetes, періодично коригує бажану шкалу своєї цілі (наприклад, Deployment), щоб відповідати спостережуваним метрикам, таким як середнє використання CPU, середня використання памʼяті або будь-яка інша метрика, яку ви вказуєте.

Ось приклад використання горизонтального автоматичного масштабування Podʼів.

Як працює HorizontalPodAutoscaler?

graph BT hpa[Horizontal Pod Autoscaler] --> scale[Scale] subgraph rc[RC / Deployment] scale end scale -.-> pod1[Pod 1] scale -.-> pod2[Pod 2] scale -.-> pod3[Pod N] classDef hpa fill:#D5A6BD,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef rc fill:#F9CB9C,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef scale fill:#B6D7A8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; classDef pod fill:#9FC5E8,stroke:#1E1E1D,stroke-width:1px,color:#1E1E1D; class hpa hpa; class rc rc; class scale scale; class pod1,pod2,pod3 pod

Схема 1. HorizontalPodAutoscaler керує масштабуванням Deployment та його ReplicaSet

У Kubernetes горизонтальне автоматичне масштабування Podʼів реалізовано як цикл керування, що працює інтервально (це не постійний процес). Інтервал встановлюється параметром --horizontal-pod-autoscaler-sync-period для kube-controller-manager (а стандартне значення — 15 секунд).

Один раз протягом кожного періоду менеджер контролера запитує використання ресурсів відповідно до метрик, вказаних у визначенні кожного HorizontalPodAutoscaler. Менеджер контролера знаходить цільовий ресурс, визначений за допомогою scaleTargetRef, потім вибирає Podʼи на основі міток .spec.selector цільового ресурсу та отримує метрики зі специфічних метрик ресурсів API (для метрик ресурсів на кожен Pod) або API власних метрик (для всіх інших метрик).

  • Для метрик ресурсів на кожен Pod (наприклад, CPU) контролер отримує метрики з API метрик ресурсів для кожного Pod, на який впливає HorizontalPodAutoscaler. Потім, якщо встановлено значення цільового використання, контролер обчислює значення використання як відсоток еквівалентного ресурсного запиту на контейнери в кожному Pod. Якщо встановлено сирцеве цільове значення, використовуються сирі (необроблені) значення метрик. Потім контролер бере середнє значення використання або сире значення (залежно від типу вказаної цілі) для всіх цільових Podʼів і створює співвідношення, яке використовується для масштабування кількості бажаних реплік.

    Зверніть увагу, що якщо деякі контейнери Podʼів не мають відповідного ресурсного запиту, використання CPU для Pod не буде визначене, і автоматичний масштабувальник не буде виконувати жодних дій для цієї метрики. Дивіться розділ подробиці алгоритму нижче для отримання додаткової інформації про те, як працює алгоритм автомасштабування.

  • Для власних метрик на кожен Pod контролер працює аналогічно метрикам ресурсів на кожен Pod, за винятком того, що він працює з сирцевими значеннями, а не значеннями використання.

  • Для метрик обʼєктів та зовнішніх метрик витягується одна метрика, яка описує обʼєкт. Цю метрику порівнюють з цільовим значенням, щоб отримати співвідношення, як вище. У версії API autoscaling/v2 це значення можна за необхідності розділити на кількість Podʼів до порівняння.

Звичайне використання HorizontalPodAutoscaler — налаштувати його на витягування метрик з агрегованих API (metrics.k8s.io, custom.metrics.k8s.io або external.metrics.k8s.io). API metrics.k8s.io зазвичай надається Metrics Server, який потрібно запустити окремо. Для отримання додаткової інформації про метрики ресурсів див. Metrics Server.

Підтримка API метрик пояснює гарантії стабільності та статус підтримки цих різних API.

Контролер HorizontalPodAutoscaler має доступ до відповідних ресурсів робочого навантаження, які підтримують масштабування (такі як Deployment та StatefulSet). Ці ресурси мають субресурс з назвою scale, інтерфейс, який дозволяє динамічно встановлювати кількість реплік і переглядати поточний стан кожного з них. Для загальної інформації про субресурси в API Kubernetes див. Поняття API Kubernetes.

Алгоритм

По простому, контролер HorizontalPodAutoscaler працює зі співвідношенням між бажаним значенням метрики та поточним значенням метрики:

бажаніРепліки = ceil[поточніРепліки * ( поточнеЗначенняМетрики / бажанеЗначенняМетрики )]

Наприклад, якщо поточне значення метрики — 200м, а бажане значення — 100м, кількість реплік буде подвоєна, оскільки 200,0 / 100,0 == 2,0. Якщо поточне значення замість цього — 50м, кількість реплік буде зменшена вдвічі, оскільки 50,0 / 100,0 == 0,5. Панель управління пропускає будь-яку дію масштабування, якщо співвідношення достатньо близьке до 1,0 (в межах глобально налаштованої допустимості, типово 0.1).

Якщо вказано targetAverageValue або targetAverageUtilization, поточне значення метрики обчислюється шляхом визначення середнього значення вказаної метрики для всіх Podʼів у цільовому масштабі HorizontalPodAutoscaler.

Перед перевіркою допустимості та визначенням кінцевих значень панель управління також розглядає, чи є відсутні будь-які метрики, і скільки Podʼів є Ready. Всі Podʼи зі встановленим відбитками часу видалення (обʼєкти з відбитками часу видалення перебувають у процесі завершення роботи/видалення) ігноруються, а всі збійні Podʼи не враховуються.

Якщо конкретний Pod не маж метрики, його відкладено на потім; Podʼи з відсутніми метриками будуть використані для коригування кінцевої кількості масштабування.

При масштабуванні за CPU, якщо будь-який pod ще не став готовим (він все ще ініціалізується, або можливо, несправний) або остання точка метрики для Pod була до того, як він став готовим, цей Pod також відкладено.

Через технічні обмеження, контролер HorizontalPodAutoscaler не може точно визначити перший раз, коли Pod стає готовим при визначенні, чи відкласти певні метрики CPU. Замість цього вважається, що Pod "ще не готовий", якщо він ще не готовий і перейшов у готовий стан протягом короткого, налаштованого вікна часу з моменту початку. Це значення налаштоване за допомогою прапорця --horizontal-pod-autoscaler-initial-readiness-delay, і типово воно складає 30 секунд. Як тільки Pod став готовим, будь-який перехід в готовий стан вважається першим, якщо це сталося протягом довшого, налаштованого часу від початку. Це значення налаштоване за допомогою прапорця --horizontal-pod-autoscaler-cpu-initialization-period, і його стандартне значення — 5 хвилин.

Базове співвідношення масштабу currentMetricValue / desiredMetricValue потім обчислюється залишковими Podʼами, які не були відкладені або відкинуті вище.

Якщо були відсутні метрики, панель управління перераховує середнє значення більш консервативно, припускаючи, що ці Podʼи споживають 100% бажаного значення у випадку масштабування вниз і 0% у випадку масштабування вгору. Це зменшує величину можливого масштабу.

Крім того, якщо присутні будь-які ще не готові Podʼи, і робоче навантаження мало б масштабуватися вгору без врахування відсутніх метрик або ще не готових Podʼів, контролер консервативно припускає, що ще не готові Podʼи споживають 0% бажаної метрики, зменшуючи величини масштабу.

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

Зверніть увагу, що початкове значення для середнього використання повертається через статус HorizontalPodAutoscaler, без врахування ще не готових Podʼів або відсутніх метрик, навіть коли використовується нове відношення використання.

Якщо в HorizontalPodAutoscaler вказано кілька метрик, цей розрахунок виконується для кожної метрики, а потім обирається найбільше з бажаних кількостей реплік. Якщо будь-яка з цих метрик не може бути переведена у бажану кількість реплік (наприклад, через помилку отримання метрик з API метрик) і запропоновано масштабування вниз за метриками, які можна витягнути, масштабування пропускається. Це означає, що HPA все ще може масштабуватися вгору, якщо одна або декілька метрик дають значення desiredReplicas, більше, ніж поточне значення.

Нарешті, прямо перед тим, як HPA масштабує ціль, рекомендація масштабування записується. Контролер розглядає всі рекомендації в налаштованому вікні та обирає найвищу рекомендацію в межах цього вікна. Це значення можна налаштувати за допомогою прапорця --horizontal-pod-autoscaler-downscale-stabilization, який стандартно складає 5 хвилин. Це означає, що зменшення масштабу відбуватиметься поступово, згладжуючи вплив метрик, що швидко змінюються.

Обʼєкт API

Horizontal Pod Autoscaler є ресурсом API в групі API Kubernetes autoscaling. Поточна стабільна версія знаходиться в версії API autoscaling/v2, яка включає підтримку масштабування за памʼяттю та власними метриками. Нові поля, введені в autoscaling/v2, зберігаються як анотації при роботі з autoscaling/v1.

При створенні обʼєкта API HorizontalPodAutoscaler переконайтеся, що вказане імʼя є дійсним піддоменом DNS. Більше деталей про обʼєкт API можна знайти на сторінці Обʼєкт HorizontalPodAutoscaler.

Стабільність масштабування робочого навантаження

При керуванні масштабом групи реплік за допомогою HorizontalPodAutoscaler можливі часті коливання кількості реплік через динамічний характер оцінюваних метрик. Це іноді називають коливанням, або flapping. Це схоже на концепцію гістерезису в кібернетиці.

Масштабування під час поступового оновлення

Kubernetes дозволяє виконувати поступове оновлення для Deployment. У цьому випадку Deployment керує підлеглими ReplicaSets за вас. Коли ви налаштовуєте автомасштабування для Deployment, ви звʼязуєте HorizontalPodAutoscaler з одним Deployment. HorizontalPodAutoscaler керує полем replicas Deployment. Контролер розгортання відповідає за встановлення replicas підлеглих ReplicaSets так, щоб вони складали відповідну кількість під час розгортання, а також після його завершення.

Якщо ви виконуєте поступове оновлення StatefulSet, у якого є автомасштабована кількість реплік, StatefulSet безпосередньо керує своїм набором Podʼів (немає проміжного ресурсу, аналогічного ReplicaSet).

Підтримка метрик ресурсів

Будь-який обʼєкт HPA може бути масштабований на основі використання ресурсів у Podʼах цільового масштабування. При визначенні специфікації Podʼа повинні бути вказані запити ресурсів, такі як cpu та memory. Це використовується для визначення використання ресурсів і використовується контролером HPA для масштабування цілі вгору або вниз. Щоб використовувати масштабування на основі використання ресурсів, вкажіть джерело метрики так:

type: Resource
resource:
  name: cpu
  target:
    type: Utilization
    averageUtilization: 60

З цією метрикою контролер HPA буде підтримувати середнє використання ресурсів у Podʼах цільового масштабування на рівні 60%. Використання — це співвідношення між поточним використанням ресурсу та запитаними ресурсами Podʼа. Дивіться Алгоритм для отримання додаткової інформації про те, як обчислюється та усереднюється використання.

Метрики ресурсів контейнера

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.30 [stable]

API HorizontalPodAutoscaler також підтримує джерело метрик контейнера, де HPA може відстежувати використання ресурсів окремих контейнерів у наборі Podʼів, щоб масштабувати цільовий ресурс. Це дозволяє налаштовувати пороги масштабування для контейнерів, які мають найбільше значення в конкретному Pod. Наприклад, якщо у вас є вебзастосунок і контейнер допоміжного сервісу (sidecar), який надає логування, ви можете масштабувати на основі використання ресурсів вебзастосунка, ігноруючи контейнер допоміжного сервісу (sidecar) та його використання ресурсів.

Якщо ви переглянете цільовий ресурс, щоб мати нову специфікацію Pod з іншим набором контейнерів, ви повинні відредагувати специфікацію HPA, якщо цей ново доданий контейнер також має бути використаний для масштабування. Якщо вказаний контейнер у джерелі метрики відсутній або присутній лише в підмножині Podʼів, то ці Podʼи ігноруються, і рекомендація перераховується. Дивіться Алгоритм для отримання додаткової інформації про обчислення. Щоб використовувати ресурси контейнера для автомасштабування, визначте джерело метрики таким чином:

type: ContainerResource
containerResource:
  name: cpu
  container: application
  target:
    type: Utilization
    averageUtilization: 60

У вищенаведеному прикладі контролер HPA масштабує ціль так, що середнє використання cpu у контейнері application у всіх подах становить 60%.

Масштабування на основі власних метрик

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.23 [stable]

(версія API autoscaling/v2beta2 раніше надавала цю можливість як бета-функцію)

За умови використання версії API autoscaling/v2 ви можете налаштувати HorizontalPodAutoscaler на масштабування на основі власної метрики (яка не є вбудованою в Kubernetes або будь-який компонент Kubernetes). Потім контролер HorizontalPodAutoscaler запитує ці власні метрики з Kubernetes API.

Дивіться Підтримка API метрик для вимог.

Масштабування на основі декількох метрик

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.23 [stable]

(версія API autoscaling/v2beta2 раніше надавала цю можливість як бета-функцію)

За умови використання версії API autoscaling/v2 ви можете вказати декілька метрик для HorizontalPodAutoscaler для масштабування на їх основі. Потім контролер HorizontalPodAutoscaler оцінює кожну метрику і пропонує новий масштаб на основі цієї метрики. HorizontalPodAutoscaler бере максимальний масштаб, рекомендований для кожної метрики, і встановлює робоче навантаження на такий розмір (за умови, що це не більше загального максимуму, який ви налаштували).

Підтримка API метрик

Стандартно контролер HorizontalPodAutoscaler отримує метрики з низки API. Для того щоб мати доступ до цих API, адміністратори кластера повинні забезпечити:

  • Увімкнути шар агрегації API.

  • Відповідні API мають бути зареєстровані:

    • Для метрик ресурсі це metrics.k8s.io API, яке зазвичай надає metrics-server. Це може бути запущено як надбудова кластера.

    • Для власних метрик це custom.metrics.k8s.io API. Його надають сервери API "адаптера", які надають постачальники рішень метрик. Перевірте у своєї системи метрик, чи доступний адаптер метрик Kubernetes.

    • Для зовнішніх метрик це external.metrics.k8s.io API. Він може бути наданий адаптерами власних метрик, які наведено вище.

Для отримання додаткової інформації про ці різні шляхи метрик та їх відмінності дивіться відповідні пропозиції дизайну для HPA V2, custom.metrics.k8s.io та external.metrics.k8s.io.

Для прикладів використання дивіться посібник з використання власних метрик та посібник з використання зовнішніх метрик.

Налаштований механізм масштабування

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.23 [stable]

(версія API autoscaling/v2beta2 раніше надавала цю можливість як бета-функцію)

Якщо ви використовуєте API HorizontalPodAutoscaler v2, ви можете використовувати поле behavior (див. довідку API) для налаштування окремих поведінок масштабування вгору та вниз. Ви вказуєте ці поведінки, встановлюючи scaleUp та/або scaleDown у полі behavior.

Ви можете вказати вікно стабілізації, яке запобігає коливанню кількості реплік для цільового масштабування. Політики масштабування також дозволяють контролювати швидкість зміни реплік під час масштабування.

Політики масштабування

Одну або декілька політик масштабування можна вказати у розділі behavior специфікації. Коли вказано кілька політик, політика, яка дозволяє найбільше змін, є політикою, яка типово вибирається. Наступний приклад показує цю поведінку під час зменшення масштабу:

behavior:
  scaleDown:
    policies:
    - type: Pods
      value: 4
      periodSeconds: 60
    - type: Percent
      value: 10
      periodSeconds: 60

periodSeconds вказує на проміжок часу в минулому, протягом якого політика має бути істинною. Максимальне значення, яке можна встановити для periodSeconds, — 1800 (пів години). Перша політика (Pods) дозволяє зменшення максимум до 4 репліки протягом однієї хвилини. Друга політика (Percent) дозволяє зменшити максимум 10% поточних реплік протягом однієї хвилини.

Оскільки стандартно вибирається політика, яка дозволяє найбільше змін, друга політика буде використовуватися лише тоді, коли кількість реплік буде більше ніж 40. З 40 або менше реплік, буде застосована перша політика. Наприклад, якщо є 80 реплік, і ціль — зменшити масштаб до 10 реплік тоді під час першого кроку буде скорочено 8 реплік. На наступній ітерації, коли кількість реплік становить 72, 10% від реплік — 7,2, але число заокруглюється вгору до 8. На кожному кроці контролера автомасштабування перераховується кількість реплік, які мають бути змінені, на основі кількості поточних реплік. Коли кількість реплік падає нижче 40, застосовується перша політика (Pods) і зменшується по 4 репліки за раз.

Вибір політики можна змінити, вказавши поле selectPolicy для напрямку масштабування. Встановлення значення Min вибере політику, яка дозволяє найменшу зміну кількості реплік. Встановлення значення Disabled повністю вимикає масштабування в даному напрямку.

Вікно стабілізації

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

Наприклад, у наступному виразі фрагменту коду вказано вікно стабілізації для scaleDown.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300

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

Це наближається до ковзного максимуму і уникає ситуації, коли алгоритм масштабування часто видаляє Podʼи лише для того, щоб викликати повторне створення еквівалентного Podʼа за мить.

Стандартна поведінка

Для використання власного масштабування не всі поля повинні бути вказані. Можна вказати лише значення, які потрібно налаштувати. Ці власні значення злиті зі стандартними значеннями. Стандартні значення відповідають існуючій поведінці в алгоритмі HPA.

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15
    - type: Pods
      value: 4
      periodSeconds: 15
    selectPolicy: Max

Для зменшення масштабу вікно стабілізації становить 300 секунд (або значення параметра --horizontal-pod-autoscaler-downscale-stabilization, якщо воно вказане). Є лише одна політика для зменшення масштабу, яка дозволяє видалити 100% поточно запущених реплік, що означає, що ціль масштабування може бути зменшена до мінімально допустимих реплік. Для збільшення масштабу вікно стабілізації відсутнє. Коли метрики показують, що ціль повинна бути збільшена, ціль збільшується негайно. Є 2 політики, за якими кожні 15 секунд можна додати не більше 4 Podʼів або 100% поточно запущених реплік до тих пір, поки HPA не досягне стабільного стану.

Приклад: зміна вікна стабілізації для зменшення масштабу

Щоб вказати власне значення вікна стабілізації для зменшення масштабу тривалістю в 1 хвилину, в HPA буде додано наступну поведінку:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 60

Приклад: обмеження коєфіцієнту зменшення масштабу

Щоб обмежити коєфіцієнт, з яким Podʼи видаляються HPA, до 10% за хвилину, до HPA додається наступна поведінка:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60

Щоб переконатися, що за хвилину видаляється не більше 5 Podʼів, можна додати другу політику зменшення масштабу з фіксованим розміром 5 та встановити selectPolicy на значення Min. Встановлення selectPolicy на Min означає, що автомасштабувальник вибирає політику, яка впливає на найменшу кількість Podʼів:

behavior:
  scaleDown:
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
    - type: Pods
      value: 5
      periodSeconds: 60
    selectPolicy: Min

Приклад: вимкнення зменшення масштабу

Значення selectPolicy Disabled вимикає масштабування вказаного напрямку. Щоб запобігти зменшенню масштабу, буде використана наступна політика:

behavior:
  scaleDown:
    selectPolicy: Disabled

Підтримка HorizontalPodAutoscaler в kubectl

HorizontalPodAutoscaler, як і кожний ресурс API, підтримується стандартним чином у kubectl. Ви можете створити новий автомасштабувальник за допомогою команди kubectl create. Ви можете переглянути список автомасштабувальників за допомогою kubectl get hpa або отримати детальний опис за допомогою kubectl describe hpa. Нарешті, ви можете видалити автомасштабувальник за допомогою kubectl delete hpa.

Крім того, є спеціальна команда kubectl autoscale для створення обʼєкта HorizontalPodAutoscaler. Наприклад, виконання kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80 створить автомасштабувальник для ReplicaSet foo, з цільовим використанням процесора, встановленим на 80% і кількістю реплік від 2 до 5.

Неявне деактивування режиму підтримки

Ви можете неявно деактивувати HPA для цільового обʼєкта без необхідності змінювати конфігурацію HPA самостійно. Якщо бажана кількість реплік цільового обʼєкта встановлена на 0, а мінімальна кількість реплік HPA більше 0, HPA припиняє коригування цільового обʼєкта (і встановлює умову ScalingActive на собі в false) до тих пір, поки ви не активуєте його вручну, змінивши бажану кількість реплік цільового обʼєкта або мінімальну кількість реплік HPA.

Перехід Deployment та StatefulSet на горизонтальне масштабування

При увімкненні HPA рекомендується видалити значення spec.replicas з Deployment та/або StatefulSet в їхніх маніфестах. Якщо цього не зроблено, будь-яка зміна в цьому обʼєкті, наприклад за допомогою kubectl apply -f deployment.yaml, буде інструкцією Kubernetes масштабувати поточну кількість Podʼів до значення ключа spec.replicas. Це може бути небажаним і призводити до проблем, коли HPA активно працює.

Майте на увазі, що видалення spec.replicas може спричинити одноразове зниження кількості Podʼів, оскільки стандартне значення цього ключа — 1 (див. Репліки Deployment). Після оновлення всі Podʼи, крім одного, розпочнуть процедури їхнього завершення. Після цього будь-яке подальше розгортання застосунку буде працювати як звичайно і буде дотримуватися конфігурації плавного оновлення за бажанням. Ви можете уникнути цього зниження, обравши один із двох наступних методів в залежності від того, як ви модифікуєте свої розгортання:

  1. kubectl apply edit-last-applied deployment/<deployment_name>
  2. У редакторі видаліть spec.replicas. Після збереження та виходу з редактора, kubectl застосовує оновлення. На цьому етапі не відбувається змін кількості Podʼів.
  3. Тепер ви можете видалити spec.replicas з маніфеста. Якщо ви використовуєте систему управління вихідним кодом, також зафіксуйте ваші зміни або виконайте будь-які інші кроки для перегляду вихідного коду, які відповідають вашому способу відстеження оновлень.
  4. Відтепер ви можете запускати kubectl apply -f deployment.yaml

При використанні Server-Side Apply ви можете дотримуватися вказівок щодо передачі власності, які охоплюють цей саме випадок використання.

Що далі

Якщо ви налаштовуєте автомасштабування у вашому кластері, вам також може бути корисно розглянути використання автомасштабування кластера для забезпечення того, що ви запускаєте правильну кількість вузлів.

Для отримання додаткової інформації про HorizontalPodAutoscaler:

  • Прочитайте приклад для автоматичного горизонтального масштабування Podʼів.
  • Прочитайте документацію для kubectl autoscale.
  • Якщо ви бажаєте написати власний адаптер для власних метрик, перегляньте початковий код, щоб почати.
  • Ознайомтесь з Довідкою API для HorizontalPodAutoscaler.

8.8 - Покрокове керівництво HorizontalPodAutoscaler

HorizontalPodAutoscaler (HPA) автоматично оновлює ресурс робочого навантаження (наприклад, Deployment або StatefulSet), з метою автоматичного масштабування робочого навантаження, щоб відповідати попиту.

Горизонтальне масштабування означає, що відповідь на збільшене навантаження полягає в розгортанні додаткових Podʼів. Це відрізняється від вертикального масштабування, що для Kubernetes означає призначення додаткових ресурсів (наприклад: памʼять або CPU) для Podʼів, які вже працюють для робочого навантаження.

Якщо навантаження зменшується, а кількість Podʼів перевищує налаштований мінімум, HorizontalPodAutoscaler інструктує ресурс робочого навантаження (Deployment, StatefulSet або інший схожий ресурс) зменшити масштаб.

Цей документ детально розглядає приклад увімкнення HorizontalPodAutoscaler для автоматичного управління масштабуванням для прикладу вебзастосунку. Це приклад навантаження — Apache httpd, що виконує деякий код PHP.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.23. Для перевірки версії введіть kubectl version. Якщо ви використовуєте старі версії Kubernetes, зверніться до документації для цієї версії (див. доступні версії документації).

Для виконання рекомендацій цього посібника, вам також потрібно використовувати кластер, в якому розгорнутий і налаштований Metrics Server. Сервер метрик Kubernetes збирає метрики ресурсів з kubelets у вашому кластері та використовує ці метрики через Kubernetes API, використовуючи APIService, щоб додати нові види ресурсів, які представляють метричні показники.

Щоб дізнатися, як розгорнути Metrics Server, див. документацію metrics-server.

Якщо ви використовуєте Minikube, виконайте наступну команду для ввімкнення metrics-server:

minikube addons enable metrics-server

Запустіть та надайте доступ до сервера php-apache

Для демонстрації HorizontalPodAutoscaler ви спочатку запустите Deployment, який запускає контейнер за допомогою образу hpa-example, та експонуєте його як Service за допомогою наступного маніфесту:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  template:
    metadata:
      labels:
        run: php-apache
    spec:
      containers:
      - name: php-apache
        image: registry.k8s.io/hpa-example
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

Для цього виконайте наступну команду:

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache створено
service/php-apache створено

Створіть HorizontalPodAutoscaler

Тепер, коли сервер працює, створіть автомасштабувальник за допомогою kubectl. Підкоманда kubectl autoscale, частина kubectl, допоможе зробити це.

За мить ви виконаєте команду, яка створить HorizontalPodAutoscaler, що підтримує від 1 до 10 реплік контрольованих Podʼів за допомогою Deployment php-apache, який ви створили на першому етапі цих інструкцій.

Грубо кажучи, контролер контролер HPA збільшує або зменшує кількість реплік (шляхом оновлення Deployment) для підтримки середнього використання процесора на рівні 50% по всіх Podʼах. Deployment потім оновлює ReplicaSet — це частина всіх Deployment у Kubernetes — і потім ReplicaSet додає або видаляє Podʼи на основі змін у його .spec.

Оскільки кожен Pod запитує 200 мілі-ядер за допомогою kubectl run, це означає середнє використання процесора 100 мілі-ядер. Див. Деталі алгоритму для отримання додаткової інформації щодо алгоритму.

Створіть HorizontalPodAutoscaler:

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache масштабовано

Ви можете перевірити поточний стан нового HorizontalPodAutoscaler, виконавши:

# Ви можете використовувати "hpa" або "horizontalpodautoscaler"; обидва імені працюють добре.
kubectl get hpa

Вивід схожий на:

NAME         REFERENCE                     TARGET    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%  1         10        1          18s

(якщо ви бачите інші HorizontalPodAutoscalers з різними іменами, це означає, що вони вже існували, і це зазвичай не проблема).

Зверніть увагу, що поточне використання ЦП становить 0%, оскільки немає клієнтів, що відправляють запити на сервер (стовпець «TARGET» показує середнє значення по всіх Podʼах, що контролюються відповідним розгортанням).

Збільшення навантаження

Далі перегляньте, як автомасштабувальник реагує на збільшене навантаження. Для цього ви запустите інший Pod, який буде виступати в ролі клієнта. Контейнер всередині Podʼа клієнта працює у нескінченному циклі, надсилаючи запити до служби php-apache.

# Виконайте це в окремому терміналі,
# щоб навантаження продовжувалося, і ви могли продовжити роботу з рештою кроків
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

Тепер виконайте:

# натисніть Ctrl+C, щоб припинити перегляд, коли будете готові
kubectl get hpa php-apache --watch

Протягом хвилини ви повинні побачити вище навантаження процесора; наприклад:

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        1          3m

а потім більше реплік. Наприклад:

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        7          3m

Тут споживання ЦП збільшилося до 305% від запиту. В результаті Deployment був змінений до 7 реплік:

kubectl get deployment php-apache

Ви повинні побачити, що кількість реплік відповідає цифрі з HorizontalPodAutoscaler

NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   7/7      7           7           19m

Зупиніть генерування навантаження

Для завершення прикладу припиніть надсилання навантаження.

У терміналі, де ви створили Pod, який працює з образом busybox, зупиніть генерування навантаження, натиснувши <Ctrl> + C.

Потім перевірте результат (через хвилину чи так):

# натисніть Ctrl+C, щоб припинити перегляд, коли будете готові
kubectl get hpa php-apache --watch

Вивід схожий на:

NAME         REFERENCE                     TARGET       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%     1         10        1          11m

і Deployment також показує, що він зменшився:

kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           27m

Як тільки використання CPU знизилося до 0, HPA автоматично зменшив кількість реплік до 1.

Автоматичне масштабування реплік може зайняти кілька хвилин.

Автомасштабування за допомогою кількох метрик та власних метрик

Ви можете вводити додаткові метрики для використання при автомасштабуванні Deployment php-apache, використовуючи версію API autoscaling/v2.

Спочатку отримайте YAML вашого HorizontalPodAutoscaler у формі autoscaling/v2:

kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml

Відкрийте файл /tmp/hpa-v2.yaml у редакторі, і ви побачите YAML, що виглядає наступним чином:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0

Зверніть увагу, що поле targetCPUUtilizationPercentage було замінено масивом під назвою metrics. Метрика використання ЦП є ресурсною метрикою, оскільки вона представлена як відсоток ресурсу, вказаного в контейнерах Podʼа. Зверніть увагу, що ви можете вказати інші ресурсні метрики крім ЦП. Стандартно, єдиним іншим підтримуваним типом ресурсної метрики є memory. Ці ресурси не змінюють назви з кластера на кластер і завжди повинні бути доступними, поки API metrics.k8s.io доступне.

Ви також можете вказати ресурсні метрики у вигляді безпосередніх значень, а не як відсотки від запитаного значення, використовуючи target.type AverageValue замість Utilization, і встановлюючи відповідне поле target.averageValue замість target.averageUtilization.

  metrics:
  - type: Resource
    resource:
      name: memory
      target:
        type: AverageValue
        averageValue: 500Mi

Є ще два інші типи метрик, які обидва вважаються власними метриками: метрики Podʼів і метрики обʼєктів. Ці метрики можуть мати назви, які специфічні для кластера, і вимагають більш розширеної настройки моніторингу кластера.

Перший з цих альтернативних типів метрик — це метрики Podʼів. Ці метрики описують Podʼи і вони усереднюються разом по Podʼах і порівнюються з цільовим значенням для визначення кількості реплік. Вони працюють так само як ресурсні метрики, за винятком того, що вони тільки підтримують тип target AverageValue.

Метрики Podʼів вказуються за допомогою блоку метрики, подібного до цього:

type: Pods
pods:
  metric:
    name: packets-per-second
  target:
    type: AverageValue
    averageValue: 1k

Другий альтернативний тип метрики — це метрики обʼєктів. Ці метрики описують інший обʼєкт в тому ж просторі імен, замість опису Podʼів. Метрики не обовʼязково отримуються з обʼєкта; вони лише описують його. Метрики обʼєктів підтримують типи target як Value і AverageValue. З Value ціль порівнюється безпосередньо з метрикою отриманою з API. З AverageValue значення, повернене з API власних метрик, ділиться на кількість Podʼів перед порівнянням з цільовим значенням. Ось приклад YAML представлення метрики requests-per-second.

type: Object
object:
  metric:
    name: requests-per-second
  describedObject:
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    name: main-route
  target:
    type: Value
    value: 2k

Якщо ви надаєте кілька таких блоків метрик, HorizontalPodAutoscaler буде розглядати кожну метрику по черзі. HorizontalPodAutoscaler розраховуватиме запропоновані кількості реплік для кожної метрики, а потім вибере той, який має найбільшу кількість реплік.

Наприклад, якщо ваша система моніторингу збирає метрики про мережевий трафік, ви можете оновити визначення вище за допомогою kubectl edit так, щоб воно виглядало наступним чином:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      current:
        value: 10k

Тоді ваш HorizontalPodAutoscaler намагатиметься забезпечити, щоб кожен Pod витрачав приблизно 50% від своєї запитаної CPU, обслуговував 1000 пакетів за секунду та всі Podʼи за головним маршрутом Ingress обслуговували в загальному 10000 запитів за секунду.

Автомасштабування за більш конкретними метриками

Багато систем метрик дозволяють описувати метрики або за назвою, або за набором додаткових описів, які називаються мітками (labels). Для всіх типів метрик, крім ресурсних (Podʼів, обʼєктів і зовнішніх, описаних нижче), ви можете вказати додатковий селектор міток, який передається вашій системі метрик. Наприклад, якщо ви збираєте метрику http_requests з міткою verb, ви можете вказати наступний блок метрики, щоб масштабувати тільки на запити GET:

type: Object
object:
  metric:
    name: http_requests
    selector: {matchLabels: {verb: GET}}

Цей селектор використовує такий же синтаксис, як і повні селектори міток Kubernetes. Система моніторингу визначає, як зведені кілька серій в одне значення, якщо імʼя та селектор відповідають кільком серіям. Селектор є додатковим, і не може вибирати метрики, що описують обʼєкти, які не є цільовим обʼєктом (цільові Podʼи у випадку типу Pods, та описаний обʼєкт у випадку типу Object).

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

Для використання зовнішніх метрик потрібно знання вашої системи моніторингу; налаштування аналогічно необхідному при використанні власних метрик. Зовнішні метрики дозволяють автоматично масштабувати ваш кластер на основі будь-якої метрики, доступної в вашій системі моніторингу. Надайте блок metric з name та selector, як вище, і використовуйте тип метрики External замість Object. Якщо кілька часових рядів відповідають metricSelector, то сума їх значень використовується HorizontalPodAutoscaler. Зовнішні метрики підтримують як типи цілей Value, так і AverageValue, які працюють точно так само, як коли ви використовуєте тип Object.

Наприклад, якщо ваш додаток обробляє завдання з хостової служби черги, ви можете додати наступний розділ до вашого маніфесту HorizontalPodAutoscaler, щоб вказати, що вам потрібен один робочий процес на кожні 30 невиконаних завдань.

- type: External
  external:
    metric:
      name: queue_messages_ready
      selector:
        matchLabels:
          queue: "worker_tasks"
    target:
      type: AverageValue
      averageValue: 30

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

Додаток: Умови стану горизонтального автомасштабування Podʼів

При використанні форми autoscaling/v2 HorizontalPodAutoscaler ви зможете бачити умови стану, встановлені Kubernetes на HorizontalPodAutoscaler. Ці умови стану вказують, чи може або не може HorizontalPodAutoscaler масштабуватися, а також чи є в цей час будь-які обмеження.

Умови зʼявляються у полі status.conditions. Щоб побачити умови, які впливають на HorizontalPodAutoscaler, ми можемо використати kubectl describe hpa:

kubectl describe hpa cm-test
Name:                           cm-test
Namespace:                      prom
Labels:                         <none>
Annotations:                    <none>
CreationTimestamp:              Fri, 16 Jun 2017 18:09:22 +0000
Reference:                      ReplicationController/cm-test
Metrics:                        ( current / target )
  "http_requests" on pods:      66m / 500m
Min replicas:                   1
Max replicas:                   4
ReplicationController pods:     1 current / 1 desired
Conditions:
  Type                  Status  Reason                  Message
  ----                  ------  ------                  -------
  AbleToScale           True    ReadyForNewScale        the last scale time was sufficiently old as to warrant a new scale
  ScalingActive         True    ValidMetricFound        the HPA was able to successfully calculate a replica count from pods metric http_requests
  ScalingLimited        False   DesiredWithinRange      the desired replica count is within the acceptable range
Events:

Для цього HorizontalPodAutoscaler ви можете побачити кілька умов у справному стані. Перше, AbleToScale, вказує, чи може або не може HPA отримувати та оновлювати масштаби, а також чи будь-які умови затримки повʼязані з масштабуванням. Друге, ScalingActive, вказує, чи увімкнений HPA (тобто кількість реплік цілі не дорівнює нулю) та чи може розраховувати потрібні масштаби. Якщо це False, це, як правило, вказує на проблеми з отриманням метрик. Нарешті, остання умова, ScalingLimited, вказує на те, що потрібний масштаб був обмежений максимальним або мінімальним значенням HorizontalPodAutoscaler. Це свідчить про те, що ви можливо захочете збільшити або зменшити мінімальну або максимальну кількість реплік на вашому HorizontalPodAutoscaler.

Кількості

Усі метрики в HorizontalPodAutoscaler та API метрик вказуються за спеціальною цільною числовою нотацією, відомою в Kubernetes як кількість. Наприклад, кількість 10500m буде записана як 10.5 у десятковій нотації. API метрик повертатимуть цілі числа без суфікса, якщо це можливо, і зазвичай повертають кількості в міліодиницях у протилежному випадку. Це означає, що ви можете бачити зміну вашого значення метрики між 1 і 1500m, або між 1 і 1.5, коли воно записане в десятковій нотації.

Інші можливі сценарії

Створення автомасштабування декларативно

Замість використання команди kubectl autoscale для створення HorizontalPodAutoscaler імперативно, ми можемо використати наступний маніфест, щоб створити його декларативно:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50

Потім створіть автомасштабування, виконавши наступну команду:

kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created

8.9 - Вказання бюджету розладів для вашого застосунку

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.21 [stable]

На цій сторінці показано, як обмежити кількість одночасних розладів, які очікує ваш застосунок, що дозволяє підвищити доступність і дозволяє адміністратору кластера управляти вузлами кластера.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.21. Для перевірки версії введіть kubectl version.

Захист застосунку за допомогою PodDisruptionBudget

  1. Визначте, який застосунок ви хочете захистити за допомогою PodDisruptionBudget (PDB).
  2. Подумайте про те, як ваш застосунок реагує на розлади.
  3. Створіть визначення PDB у вигляді файлу YAML.
  4. Створіть обʼєкт PDB з файлу YAML.

Визначення застосунку для захисту

Найбільш поширене використання полягає в захисті застосунку, який визначено одним із вбудованих контролерів Kubernetes:

  • Deployment (Розгортання)
  • ReplicationController (Контролер Реплікації)
  • ReplicaSet (Набір Реплік)
  • StatefulSet (Набір зі Станом)

У цьому випадку обовʼязково відзначте .spec.selector контролера; той самий селектор використовується в .spec.selector PDBs.

Починаючи з версії 1.15, PDB підтримують власні контролери, де включено субресурс масштабування.

Також можна використовувати PDB зі сторонніми Podʼами, які не контролюються одним із вищевказаних контролерів, або з довільними групами Podʼів, але є деякі обмеження, описані у Довільні робочі навантаження та довільні селектори.

Подумайте про реакцію вашого застосунку на відключення

Вирішіть, скільки екземплярів може бути вимкнено одночасно на короткий період часу через добровільні відключення.

  • Фронтенди без збереження стану:
    • Застереження: не зменшуйте потужність обслуговування більш ніж на 10%.
      • Рішення: використовуйте PDB з minAvailable 90%, наприклад.
  • Одноекземплярний застосунок зі збереженням стану:
    • Застереження: не закривайте цей застосунок без спілкування зі мною.
      • Можливе рішення 1: Не використовуйте PDB і терпіть випадковий простій.
      • Можливе рішення 2: Встановіть PDB з maxUnavailable=0. Маючи розуміння (поза Kubernetes), що оператор кластера повинен звʼязатися з вами перед закриттям. Коли оператор кластера звертається до вас, готуйтеся до простою, а потім видаліть PDB, щоб показати готовність до відключення. Після цього створіть новий.
  • Багатоекземплярний застосунок зі збереженням стану, такий як Consul, ZooKeeper або etcd:
    • Застереження: не зменшуйте кількість екземплярів нижче кворуму, в іншому випадку записи будуть невдалі.
      • Можливе рішення 1: встановіть maxUnavailable на рівень 1 (працює з різними масштабами застосунку).
      • Можливе рішення 2: встановіть minAvailable рівним розміру кворуму (наприклад, 3 при масштабуванні 5). (Дозволяє більше відключень одночасно).
  • Пакетне завдання (Job) з перезапуском:
    • Застереження: завдання повинно завершитися у разі добровільного відключення.
      • Можливе рішення: Не створюйте PDB. Контролер завдань створить Pod на заміну.

Логіка округлення при вказанні відсотків

Значення для minAvailable або maxUnavailable можуть бути виражені як цілі числа або як відсотки.

  • Коли ви вказуєте ціле число, воно представляє кількість Podʼів. Наприклад, якщо ви встановите minAvailable на 10, то завжди повинно бути доступно 10 Podʼів, навіть під час відключення.
  • Коли ви вказуєте відсоток, встановивши значення як рядкове представлення відсотка (наприклад, "50%"), воно представляє відсоток від загальної кількості Podʼів. Наприклад, якщо ви встановите minAvailable на "50%", то принаймні 50% Podʼів залишаться доступними під час відключення.

Коли ви вказуєте значення як відсоток, це може не відповідати точній кількості Podʼів. Наприклад, якщо у вас є 7 Podʼів і ви встановите minAvailable на "50%", то не зразу очевидно, чи має це означати, що повинно бути доступно 3 або 4 Podʼи. Kubernetes округлює до найближчого цілого числа, тому в цьому випадку повинно бути доступно 4 Podʼи. Коли ви вказуєте значення maxUnavailable як відсоток, Kubernetes округлює кількість Podʼів, які можуть бути відключені. Таким чином, відключення може перевищувати ваш визначений відсоток maxUnavailable. Ви можете ознайомитися з кодом, який керує такою поведінкою.

Вказання PodDisruptionBudget

PodDisruptionBudget має три поля:

  • Селектор міток .spec.selector, щоб вказати набір Podʼів, до яких він застосовується. Це поле обовʼязкове.
  • .spec.minAvailable — це опис кількості Podʼів з цього набору, які повинні залишитися доступними після відключення, навіть у відсутності відключеного Podʼа. minAvailable може бути абсолютним числом або відсотком.
  • .spec.maxUnavailable (доступний у Kubernetes 1.7 та вище) — це опис кількості Podʼів з цього набору, які можуть бути недоступними після відключення. Це також може бути абсолютним числом або відсотком.

Ви можете вказати лише один із maxUnavailable або minAvailable в одному PodDisruptionBudget. maxUnavailable може бути використаний лише для контролю відключення Podʼів, які мають повʼязаний контролер, який керує ними. У наведених нижче прикладах, "бажані репліки" — це масштаб контролера, що керує Podʼами, які вибрані PodDisruptionBudget.

Приклад 1: З minAvailable 5, відключення дозволяються, поки залишаються 5 або більше справних Podʼів серед тих, які вибрані селектором PodDisruptionBudget.

Приклад 2: З minAvailable 30%, відключення дозволяються, якщо принаймні 30% від кількості бажаних реплік є справними.

Приклад 3: З maxUnavailable 5, відключення дозволяються, поки є не більше 5 несправних реплік серед загальної кількості бажаних реплік.

Приклад 4: З maxUnavailable 30%, відключення дозволяються, якщо кількість несправних реплік не перевищує 30% від загальної кількості бажаних реплік, округленої до найближчого цілого числа. Якщо загальна кількість бажаних реплік — лише одна, ця єдина репліка все ще допускається до відключення, що призводить до ефективної недоступності на 100%.

У типовому використанні один бюджет використовуватиметься для збірки Podʼів, керованих контролером — наприклад, Podʼів у одному ReplicaSet або StatefulSet.

Якщо ви встановите maxUnavailable на 0% або 0, або ви встановите minAvailable на 100% або рівну кількості реплік, ви вимагаєте нульових добровільних виселень. Коли ви встановлюєте нульові добровільні відключення для робочого навантаження, такого як ReplicaSet, тоді ви не зможете успішно вивести з експлуатації вузол, на якому працює один з цих Podʼів. Якщо ви намагаєтеся вивести з експлуатації вузол, де працює невиселяємий Pod, відключення ніколи не завершиться. Це допускається згідно з семантикою PodDisruptionBudget.

Ви можете знайти приклади визначення бюджетів відключення Podʼів нижче. Вони відповідають Podʼам з міткою app: zookeeper.

Приклад PDB з використанням minAvailable:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

Приклад PDB з використанням maxUnavailable:

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: zookeeper

Наприклад, якщо вищезгаданий обʼєкт zk-pdb вибирає Podʼи з StatefulSet розміром 3, обидві специфікації мають точно таке ж значення. Рекомендується використання maxUnavailable, оскільки він автоматично реагує на зміни кількості реплік відповідного контролера.

Створення обʼєкта PodDisruptionBudget

Для створення або оновлення обʼєкта PDB використовуйте наступну команду kubectl:

kubectl apply -f mypdb.yaml

Перевірка статусу обʼєкта PodDisruptionBudget

Щоб перевірити статус обʼєкта PDB, скористайтеся наступною командою kubectl.

Якщо в вашому просторі імен відсутні Podʼи, що відповідають мітці app: zookeeper, ви побачите щось подібне:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               0                     7s

Якщо є Podʼи, які відповідають умовам (скажімо, 3), то ви побачите щось на кшталт:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               1                     7s

Ненульове значення для ALLOWED DISRUPTIONS означає, що контролер відключення Podʼів бачив Podʼи, підрахував відповідні Podʼи і оновив статус PDB.

Ви можете отримати більше інформації про статус PDB за допомогою цієї команди:

kubectl get poddisruptionbudgets zk-pdb -o yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  annotations:

  creationTimestamp: "2020-03-04T04:22:56Z"
  generation: 1
  name: zk-pdb

status:
  currentHealthy: 3
  desiredHealthy: 2
  disruptionsAllowed: 1
  expectedPods: 3
  observedGeneration: 1

Справність Pod

Поточна реалізація вважає Podʼи справними, якщо вони мають елемент .status.conditions з type="Ready" і status="True". Ці Podʼи відстежуються через поле .status.currentHealthy в статусі PDB.

Політика виселення несправних Podʼів

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [stable]

PodDisruptionBudget, який охороняє застосунок, забезпечує, що кількість Podʼів зі статусом .status.currentHealthy не опуститься нижче, ніж кількість, вказана у .status.desiredHealthy, не дозволяючи видалення справних Podʼів. Використовуючи .spec.unhealthyPodEvictionPolicy, ви також можете визначити критерії, коли несправні Podʼи повинні розглядатися для видалення. Стандартна поведінка, коли політика не вказана, відповідає політиці IfHealthyBudget.

Політики:

IfHealthyBudget
Запущені Podʼи (.status.phase="Running"), але ще не справні, можуть бути виселені тільки якщо охоронюваний застосунок не відключений (.status.currentHealthy щонайменше дорівнює .status.desiredHealthy).

Ця політика забезпечує, що запущені Podʼи вже відключеного застосунку мають кращі шанси стати справними. Це має негативні наслідки для виведення вузлів з експлуатації, які можуть бути заблоковані неправильно працюючими застосунками, які охороняються PDB. Зокрема, застосунки з Podʼами у стані CrashLoopBackOff (через помилку або неправильну конфігурацію), або Podʼи, яким просто не вдається повідомити умову Ready.

AlwaysAllow
Запущені Podʼи (.status.phase="Running"), але ще не справні вважаються відключеними та можуть бути видалені незалежно від того, чи виконуються критерії в PDB.

Це означає, що майбутні запущені Podʼи відключеного застосунку можуть не мати можливості стати справними. Використовуючи цю політику, менеджери кластера можуть легко вивести з експлуатації неправильно працюючі застосунки, які охороняються PDB. Зокрема, застосунки з Podʼами у стані CrashLoopBackOff (через помилку або неправильну конфігурацію), або Podʼи, які просто не вдаються відправити умову Ready.

Довільні робочі навантаження та селектори

Ви можете пропустити цей розділ, якщо ви використовуєте PDB лише зі вбудованими ресурсами навантаження (Deployment, ReplicaSet, StatefulSet та ReplicationController) або з власними ресурсами, які реалізують субресурс scale, і де селектор PDB точно відповідає селектору власного ресурсу Podʼа.

Ви можете використовувати PDB з підпроцесами, керованими іншим ресурсом, "оператором" або голими підпроцесами, але з такими обмеженнями:

  • можна використовувати лише .spec.minAvailable, а не .spec.maxUnavailable.
  • можна використовувати лише ціле значення з .spec.minAvailable, а не відсоток.

Неможливо використовувати інші конфігурації доступності, оскільки Kubernetes не може вивести загальну кількість Podʼів без підтримуваного власного ресурсу.

Ви можете використовувати селектор, який вибирає підмножину або надмножину Podʼів, що належать ресурсу навантаження. Eviction API не дозволить виселення будь-якого Podʼа, покритого кількома PDB, тому більшість користувачів захочуть уникати перетинаючих селекторів. Одним розумним використанням перетинаючих PDB є перехід Podʼів з одного PDB до іншого.

8.10 - Отримання доступу до API Kubernetes з Pod

Цей посібник демонструє, як отримати доступ до API Kubernetes з середини Pod.

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

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

Отримання доступу до API з середини Pod

При доступі до API з середини Pod, пошук та автентифікація до сервера API відрізняються трохи від зовнішнього клієнта.

Найпростіший спосіб використовувати API Kubernetes з Pod — використовувати одну з офіційних клієнтських бібліотек. Ці бібліотеки можуть автоматично визначати сервер API та автентифікувати.

Використання офіційних клієнтських бібліотек

З середини Pod рекомендовані способи підключення до API Kubernetes:

У кожному випадку облікові дані облікового службово запису Pod використовуються для забезпечення безпечного звʼязку з сервером API.

Прямий доступ до REST API

Під час роботи в Pod ваш контейнер може створити HTTPS URL для сервера API Kubernetes, отримавши змінні середовища KUBERNETES_SERVICE_HOST та KUBERNETES_SERVICE_PORT_HTTPS. Внутрішня адреса сервера API також публікується для Service з іменем kubernetes в просторі імен default, щоб Pod можна було використовувати kubernetes.default.svc як DNS-імʼя для локального сервера API.

Рекомендований спосіб автентифікації на сервері API — за допомогою облікового службового запису. Стандартно, Pod повʼязаний з обліковим службовим записом, і обліковий запис (токен) для цього облікового службового запису розміщується в дереві файлової системи кожного контейнера в цьому Pod, у /var/run/secrets/kubernetes.io/serviceaccount/token.

Якщо доступно, пакет сертифікатів розміщується в дереві файлової системи кожного контейнера за адресою /var/run/secrets/kubernetes.io/serviceaccount/ca.crt, і його слід використовувати для перевірки сертифіката сервера API.

Нарешті, простір імен default для операцій з просторовими іменами API розміщується в файлі у /var/run/secrets/kubernetes.io/serviceaccount/namespace в кожному контейнері.

Використання kubectl proxy

Якщо ви хочете запитувати API без офіційної клієнтської бібліотеки, ви можете запустити kubectl proxy як команду нового контейнера sidecar в Pod. Таким чином, kubectl proxy буде автентифікуватися до API та викладати його на інтерфейс localhost Pod, щоб інші контейнери в Pod могли використовувати його безпосередньо.

Без використання проксі

Можливо уникнути використання kubectl proxy, передаючи токен автентифікації прямо на сервер API. Внутрішній сертифікат забезпечує зʼєднання.

# Вказати імʼя хоста внутрішнього API-сервера
APISERVER=https://kubernetes.default.svc

# Шлях до токена ServiceAccount
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# Прочитати простір імен цього Pod
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# Прочитати токен облікового службового запису
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# Звертатися до внутрішнього центру сертифікації (CA)
CACERT=${SERVICEACCOUNT}/ca.crt

# Досліджувати API за допомогою TOKEN
curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api

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

{
  "kind": "APIVersions",
  "versions": ["v1"],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

9 - Запуск Job

Запуск Job з використанням паралельної обробки.

9.1 - Виконання автоматизованих завдань за допомогою CronJob

Ця сторінка показує, як виконувати автоматизовані завдання за допомогою обʼєкта Kubernetes CronJob.

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

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

Створення CronJob

Для роботи з CronJob потрібен файл конфігурації. Нижче подано маніфест для CronJob, який запускає просте демонстраційне завдання кожну хвилину:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

Запустіть приклад CronJob за допомогою цієї команди:

kubectl create -f https://k8s.io/examples/application/job/cronjob.yaml

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

cronjob.batch/hello created

Після створення CronJob отримайте його статус за допомогою цієї команди:

kubectl get cronjob hello

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

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          10s

Як видно з результатів команди, CronJob ще не планував або не запускав жодних завдань. Спостерігайте (watch) за створенням завдання протягом хвилини:

kubectl get jobs --watch

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

NAME               COMPLETIONS   DURATION   AGE
hello-4111706356   0/1                      0s
hello-4111706356   0/1           0s         0s
hello-4111706356   1/1           5s         5s

Тепер ви бачите одне запущене завдання, заплановане cron job "hello". Ви можете припинити спостереження за завданням і переглянути cron job ще раз, щоб побачити, що він запланував завдання:

kubectl get cronjob hello

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

NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        50s             75s

Ви повинні побачити, що cron job hello успішно запланував завдання в час, вказаний у LAST SCHEDULE. Наразі немає активних завдань, що означає, що завдання завершилось або зазнало невдачі.

Тепер знайдіть Podʼи, які створило останнє заплановане завдання, та перегляньте стандартний вивід одного з них.

# Замініть "hello-4111706356" на назву завдання у вашій системі
pods=$(kubectl get pods --selector=job-name=hello-4111706356 --output=jsonpath={.items[*].metadata.name})

Покажіть лог Pod:

kubectl logs $pods

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

Fri Feb 22 11:02:09 UTC 2019
Hello from the Kubernetes cluster

Видалення CronJob

Коли вам більше не потрібно cron job, видаліть його за допомогою kubectl delete cronjob <cronjob name>:

kubectl delete cronjob hello

Видалення cron job призводить до видалення всіх створених ним завдань і Podʼів і припинення створення додаткових завдань. Докладніше про видалення завдань читайте в збиранні сміття.

9.2 - Груба паралельна обробка за допомогою черги роботи

У цьому прикладі ви запустите Job Kubernetes з кількома паралельними робочими процесами.

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

Ось огляд кроків у цьому прикладі:

  1. Запустіть службу черги повідомлень. У цьому прикладі ви використовуєте RabbitMQ, але ви можете використовувати іншу. На практиці ви налаштовували б службу черги повідомлень один раз і використовували б її для багатьох робочих завдань.
  2. Створіть чергу та заповніть її повідомленнями. Кожне повідомлення являє собою одне завдання для виконання. У цьому прикладі повідомлення — це ціле число, на якому ми будемо виконувати тривалі обчислення.
  3. Запустіть Job, що працює над завданнями з черги. Job створює декілька Podʼів. Кожен Pod бере одне завдання з черги повідомлень, обробляє його та завершує роботу.

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

Ви вже маєте бути знайомі з основним, не-паралельним використанням Job.

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

Вам знадобиться реєстр образів контейнерів, куди ви можете завантажувати образи для запуску у своєму кластері.

Цей приклад завдання також передбачає, що у вас вже встановлений Docker локально.

Запуск служби черги повідомлень

У цьому прикладі використовується RabbitMQ, проте ви можете адаптувати приклад для використання іншої служби повідомлень типу AMQP.

На практиці ви можете налаштувати службу черги повідомлень один раз у кластері та використовувати її для багатьох завдань, а також для служб з тривалим виконанням.

Запустіть RabbitMQ таким чином:

# Створіть Service для використання StatefulSet
kubectl create -f https://kubernetes.io/examples/application/job/rabbitmq/rabbitmq-service.yaml
service "rabbitmq-service" created
kubectl create -f https://kubernetes.io/examples/application/job/rabbitmq/rabbitmq-statefulset.yaml
statefulset "rabbitmq" created

Тестування служби черги повідомлень

Тепер ми можемо спробувати доступ до черги повідомлень. Ми створимо тимчасовий інтерактивний Pod, встановимо деякі інструменти на ньому, і спробуємо працювати з чергами.

Спочатку створіть тимчасовий інтерактивний Pod.

# Створіть тимчасовий інтерактивний контейнер
kubectl run -i --tty temp --image ubuntu:22.04
Waiting for pod default/temp-loe07 to be running, status is Pending, pod ready: false
... [ previous line repeats several times .. hit return when it stops ] ...

Зверніть увагу, що ваше імʼя Pod та запрошення командного рядка будуть відрізнятися.

Далі встановіть amqp-tools, щоб працювати з чергами повідомлень. Наступні команди показують, що вам потрібно запустити в середовищі інтерактивної оболонки у цьому Pod:

apt-get update && apt-get install -y curl ca-certificates amqp-tools python3 dnsutils

Пізніше ви створите образ контейнера, який включає ці пакети.

Далі перевірте, що ви можете виявити Service для RabbitMQ:

# Виконайте ці команди всередині Pod
# Зауважте, що для служби rabbitmq-service існує DNS-ім\я, яке надається Kubernetes:
nslookup rabbitmq-service
Server:        10.0.0.10
Address:    10.0.0.10#53

Name:    rabbitmq-service.default.svc.cluster.local
Address: 10.0.147.152

(адреси IP будуть відрізнятися)

Якщо надбудова kube-dns не налаштована правильно, попередній крок може не працювати для вас. Ви також можете знайти IP-адресу для цього Service у змінній середовища:

# виконайте цю перевірку всередині Pod
env | grep RABBITMQ_SERVICE | grep HOST
RABBITMQ_SERVICE_SERVICE_HOST=10.0.147.152

(IP-адреса буде відрізнятися)

Далі перевірте, що ви можете створити чергу, і публікувати та отримувати повідомлення.

# Виконайте ці команди всередині Pod
# У наступному рядку rabbitmq-service - це імʼя хоста, за яким можна звертатися до rabbitmq-service.
# 5672 - це стандартний порт для rabbitmq.
export BROKER_URL=amqp://guest:guest@rabbitmq-service:5672
# Якщо ви не могли отримати доступ до "rabbitmq-service" на попередньому кроці,
# використовуйте цю команду натомість:
BROKER_URL=amqp://guest:guest@$RABBITMQ_SERVICE_SERVICE_HOST:5672

# Тепер створіть чергу:

/usr/bin/amqp-declare-queue --url=$BROKER_URL -q foo -d
foo

Опублікуйте одне повідомлення в черзі:

/usr/bin/amqp-publish --url=$BROKER_URL -r foo -p -b Hello

# І отримайте його назад.

/usr/bin/amqp-consume --url=$BROKER_URL -q foo -c 1 cat && echo 1>&2
Hello

У останній команді інструмент amqp-consume взяв одне повідомлення (-c 1) з черги, і передав це повідомлення на стандартний ввід довільної команди. У цьому випадку програма cat виводить символи, зчитані зі стандартного вводу, а echo додає символ нового рядка, щоб приклад був читабельним.

Заповнення черги завданнями

Тепер заповніть чергу деякими симульованими завданнями. У цьому прикладі завдання — це рядки, які потрібно надрукувати.

На практиці вміст повідомлень може бути таким:

  • назви файлів, які потрібно обробити
  • додаткові прапорці для програми
  • діапазони ключів у таблиці бази даних
  • параметри конфігурації для симуляції
  • номери кадрів сцени для рендерингу

Якщо є великі дані, які потрібно лише для читання всіма Podʼами Job, зазвичай це розміщують на спільній файловій системі, наприклад NFS, і монтується це на всі Podʼи тільки для читання, або напишіть програму в Pod так, щоб вона могла нативно читати дані з кластерної файлової системи (наприклад: HDFS).

У цьому прикладі ви створите чергу та заповните її, використовуючи інструменти командного рядка AMQP. На практиці ви можете написати програму для заповнення черги, використовуючи бібліотеку клієнтів AMQP.

# Виконайте це на вашому компʼютері, а не в Pod
/usr/bin/amqp-declare-queue --url=$BROKER_URL -q job1  -d
job1

Додайте елементи до черги:

for f in apple banana cherry date fig grape lemon melon
do
  /usr/bin/amqp-publish --url=$BROKER_URL -r job1 -p -b $f
done

Ви додали 8 повідомлень у чергу.

Створення образу контейнера

Тепер ви готові створити образ, який ви будете запускати як Job.

Job буде використовувати утиліту amqp-consume для читання повідомлення з черги та виконання реальної роботи. Ось дуже простий приклад програми:

#!/usr/bin/env python

# Просто виводить стандартний вивід і очікує протягом 10 секунд.
import sys
import time
print("Processing " + sys.stdin.readlines()[0])
time.sleep(10)

Дайте скрипту дозвіл на виконання:

chmod +x worker.py

Тепер створіть образ. Створіть тимчасову теку, перейдіть в неї, завантажте Dockerfile, і worker.py. У будь-якому випадку, створіть образ за допомогою цієї команди:

docker build -t job-wq-1 .

Для Docker Hub позначте ваш образ застосунка вашим імʼям користувача і завантажте його на Hub за допомогою таких команд. Замініть <username> на ваше імʼя користувача Hub.

docker tag job-wq-1 <username>/job-wq-1
docker push <username>/job-wq-1

Якщо ви використовуєте альтернативний реєстр образів контейнерів, позначте образ і завантажте його туди замість цього.

Визначення Job

Ось маніфест для Job. Вам потрібно зробити копію маніфеста Job (назвіть його ./job.yaml), і змініть імʼя образу контейнера, щоб відповідало імені, яке ви використовували.

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-1
spec:
  completions: 8
  parallelism: 2
  template:
    metadata:
      name: job-wq-1
    spec:
      containers:
      - name: c
        image: gcr.io/<project>/job-wq-1
        env:
        - name: BROKER_URL
          value: amqp://guest:guest@rabbitmq-service:5672
        - name: QUEUE
          value: job1
      restartPolicy: OnFailure

У цьому прикладі кожен Pod працює над одним елементом з черги, а потім виходить. Таким чином, кількість завершень завдання відповідає кількості виконаних робочих елементів. Тому у прикладі маніфесту .spec.completions встановлено на 8.

Запуск Job

Тепер запустіть завдання:

# це передбачає, що ви вже завантажили та відредагували маніфест
kubectl apply -f ./job.yaml

Ви можете зачекати, доки завдання успішно виконається, використовуючи тайм-аут:

# Перевірка для умови що імена нечутливі до регістру
kubectl wait --for=condition=complete --timeout=300s job/job-wq-1

Далі перевірте стан завдання:

kubectl describe jobs/job-wq-1
Name:             job-wq-1
Namespace:        default
Selector:         controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-1
Annotations:      <none>
Parallelism:      2
Completions:      8
Start Time:       Wed, 06 Sep 2022 16:42:02 +0000
Pods Statuses:    0 Running / 8 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=41d75705-92df-11e7-b85e-fa163ee3c11f
                job-name=job-wq-1
  Containers:
   c:
    Image:      container-registry.example/causal-jigsaw-637/job-wq-1
    Port:
    Environment:
      BROKER_URL:       amqp://guest:guest@rabbitmq-service:5672
      QUEUE:            job1
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen  LastSeen   Count    From    SubobjectPath    Type      Reason              Message
  ─────────  ────────   ─────    ────    ─────────────    ──────    ──────              ───────
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-hcobb
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-weytj
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-qaam5
  27s        27s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-b67sr
  26s        26s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-xe5hj
  15s        15s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-w2zqe
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-d6ppa
  14s        14s        1        {job }                   Normal    SuccessfulCreate    Created pod: job-wq-1-p17e0

Усі Podʼи для цього завдання успішно виконалися! У вас вийшло.

Альтернативи

Цей підхід має перевагу у тому, що вам не потрібно змінювати вашу "робочу" програму, щоб вона була обізнана, що є черга роботи. Ви можете включити незмінену робочу програму у свій контейнерний образ.

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

Цей підхід створює Pod для кожного робочого елемента. Якщо ваші робочі елементи займають лише декілька секунд, створення Podʼа для кожного робочого елемента може додати багато накладних витрат. Розгляньте інший дизайн, наприклад, у прикладі тонкої паралельної черги робочих елементів, що виконує кілька робочих елементів на кожному Pod.

У цьому прикладі ви використовували утиліту amqp-consume для читання повідомлення з черги та запуску реальної програми. Це має перевагу у тому, що вам не потрібно модифікувати свою програму, щоб вона була обізнана про існування черги. У прикладі тонкої паралельної черги робочих елементів показано, як спілкуватися з чергою робочих елементів за допомогою клієнтської бібліотеки.

Обмеження

Якщо кількість завершень встановлена меншою, ніж кількість елементів у черзі, то не всі елементи будуть оброблені.

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

Існують малоймовірні перегони для цього патерну. Якщо контейнер був завершений між часом, коли повідомлення було підтверджено командою amqp-consume, і часом, коли контейнер вийшов з успішним завершенням, або якщо вузол зазнає збою, перш ніж kubelet зможе розмістити успіх Podʼа назад до сервера API, то завдання не буде здаватися завершеним, навіть якщо всі елементи у черзі були оброблені.

9.3 - Тонка паралельна обробка за допомогою черги роботи

In цьому прикладі ви запустите Job Kubernetes, яке виконує декілька паралельних завдань як робочі процеси, кожен з яких працює як окремий Pod.

У цьому прикладі, при створенні кожного Podʼа, він бере одиницю роботи з черги завдань, обробляє її та повторює цей процес до досягнення кінця черги.

Ось загальний огляд кроків у цьому прикладі:

  1. Запустіть службу зберігання, щоб зберігати чергу завдань. У цьому прикладі ви використаєте Redis для зберігання робочих елементів. У попередньому прикладі, ви використали RabbitMQ. У цьому прикладі ви будете використовувати Redis та власну бібліотеку клієнтів черг завдань; це тому, що AMQP не надає зручний спосіб клієнтам виявити, коли скінчиться черга робочих елементів з обмеженою довжиною. На практиці ви налаштуєте сховище, таке як Redis, один раз і повторно використовуватимете його для черг робочих завдань багатьох завдань та іншого.
  2. Створіть чергу та заповніть її повідомленнями. Кожне повідомлення представляє одне завдання, яке потрібно виконати. У цьому прикладі повідомленням є ціле число, над яким ми виконаємо тривалі обчислення.
  3. Запустіть завдання, яке працює над завданнями з черги. Завдання запускає декілька Podʼів. Кожний Pod бере одне завдання з черги повідомлень, обробляє його та повторює цей процес до досягнення кінця черги.

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

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

Вам знадобиться реєстр контейнерних образів, де ви можете завантажувати образи для запуску у вашому кластері. У прикладі використовується Docker Hub, але ви можете адаптувати його до іншого реєстру контейнерних образів.

Цей приклад передбачає, що у вас встановлено Docker локально. Ви будете використовувати Docker для створення контейнерних образів.

Ви маєти бути знайомі з базовим, не-паралельним використанням Job.

Запуск Redis

У цьому прикладі, для спрощення, ви запустите один екземпляр Redis. Дивіться Приклад Redis для прикладу розгортання Redis масштабовано та надійно.

Ви також можете завантажити наступні файли безпосередньо:

Для запуску одного екземпляра Redis вам потрібно створити Pod Redis та Service Redis:

kubectl apply -f https://k8s.io/examples/application/job/redis/redis-pod.yaml
kubectl apply -f https://k8s.io/examples/application/job/redis/redis-service.yaml

Заповнення черги завданнями

Тепер заповнімо чергу деякими "задачами". У цьому прикладі завданнями є рядки, які потрібно вивести.

Запустіть тимчасовий інтерактивний Pod для використання Redis CLI.

kubectl run -i --tty temp --image redis --command "/bin/sh"
Waiting for pod default/redis2-c7h78 to be running, status is Pending, pod ready: false
Hit enter for command prompt

Тепер натисніть Enter, запустіть Redis CLI та створіть список з деякими елементами роботи в ньому.

redis-cli -h redis
redis:6379> rpush job2 "apple"
(integer) 1
redis:6379> rpush job2 "banana"
(integer) 2
redis:6379> rpush job2 "cherry"
(integer) 3
redis:6379> rpush job2 "date"
(integer) 4
redis:6379> rpush job2 "fig"
(integer) 5
redis:6379> rpush job2 "grape"
(integer) 6
redis:6379> rpush job2 "lemon"
(integer) 7
redis:6379> rpush job2 "melon"
(integer) 8
redis:6379> rpush job2 "orange"
(integer) 9
redis:6379> lrange job2 0 -1
1) "apple"
2) "banana"
3) "cherry"
4) "date"
5) "fig"
6) "grape"
7) "lemon"
8) "melon"
9) "orange"

Отже, список з ключем job2 буде чергою роботи.

Примітка: якщо у вас неправильно налаштовано Kube DNS, вам може знадобитися змінити перший крок вищезазначеного блоку на redis-cli -h $REDIS_SERVICE_HOST.

Створення образу контейнера

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

Ви будете використовувати робочу програму на Python з клієнтом Redis для читання повідомлень з черги повідомлень.

Надається проста бібліотека клієнтів черги роботи Redis, яка називається rediswq.py (Завантажити).

Програма "робітник" в кожному Pod Job використовує бібліотеку клієнтів черги роботи, щоб отримати роботу. Ось вона:

#!/usr/bin/env python

import time
import rediswq

host="redis"
# Якщо у вас немає працюючого Kube-DNS, розкоментуйте наступні два рядки.
# import os
# host = os.getenv("REDIS_SERVICE_HOST")

q = rediswq.RedisWQ(name="job2", host=host)
print("Worker with sessionID: " +  q.sessionID())
print("Initial queue state: empty=" + str(q.empty()))
while not q.empty():
  item = q.lease(lease_secs=10, block=True, timeout=2) 
  if item is not None:
    itemstr = item.decode("utf-8")
    print("Working on " + itemstr)
    time.sleep(10) # Put your actual work here instead of sleep.
    q.complete(item)
  else:
    print("Waiting for work")
print("Queue empty, exiting")

Ви також можете завантажити файли worker.py, rediswq.py та Dockerfile, а потім побудувати контейнерний образ. Ось приклад використання Docker для побудови образу:

docker build -t job-wq-2 .

Збереження образу в реєстрі

Для Docker Hub, позначте свій образ програми імʼям користувача та завантажте його до Hub за допомогою наступних команд. Замість <username> вкажіть своє імʼя користувача Hub.

docker tag job-wq-2 <username>/job-wq-2
docker push <username>/job-wq-2

Вам потрібно завантажити в публічний репозиторій або налаштувати кластер для доступу до вашого приватного репозиторію.

Визначення завдання

Ось маніфест для створення Job:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-wq-2
spec:
  parallelism: 2
  template:
    metadata:
      name: job-wq-2
    spec:
      containers:
      - name: c
        image: gcr.io/myproject/job-wq-2
      restartPolicy: OnFailure

У цьому прикладі кожний Pod працює з кількома елементами черги, а потім виходить, коли елементи закінчуються. Оскільки самі робочі процеси виявляють порожнечу робочої черги, а контролер завдань не володіє інформацією про робочу чергу, він покладається на робочі процеси, щоб сигналізувати, коли вони закінчили роботу. Робочі процеси сигналізують, що черга порожня, вийшовши з успіхом. Таким чином, як тільки будь-який робочий процес виходить з успіхом, контролер знає, що робота виконана, і що Podʼи скоро вийдуть. Тому вам потрібно залишити лічильник завершення завдання невизначеним. Контролер завдань зачекає, доки інші Podʼи завершаться також.

Запуск завдання

Отже, зараз запустіть завдання:

# передбачається, що ви вже завантажили та відредагували маніфест
kubectl apply -f ./job.yaml

Тепер зачекайте трохи, а потім перевірте стан завдання:

kubectl describe jobs/job-wq-2
Name:             job-wq-2
Namespace:        default
Selector:         controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
Labels:           controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                  job-name=job-wq-2
Annotations:      <none>
Parallelism:      2
Completions:      <unset>
Start Time:       Mon, 11 Jan 2022 17:07:59 +0000
Pods Statuses:    1 Running / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       controller-uid=b1c7e4e3-92e1-11e7-b85e-fa163ee3c11f
                job-name=job-wq-2
  Containers:
   c:
    Image:              container-registry.example/exampleproject/job-wq-2
    Port:
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From            SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----            -------------    --------    ------            -------
  33s          33s         1        {job-controller }                Normal      SuccessfulCreate  Created pod: job-wq-2-lglf8

Ви можете зачекати, поки завдання завершиться успішно, з тайм-аутом:

# Перевірка умови назви нечутлива до регістру
kubectl wait --for=condition=complete --timeout=300s job/job-wq-2
kubectl logs pods/job-wq-2-7r7b2
Worker with sessionID: bbd72d0a-9e5c-4dd6-abf6-416cc267991f
Initial queue state: empty=False
Working on banana
Working on date
Working on lemon

Як бачите, один з Podʼів для цього завдання працював над кількома робочими одиницями.

Альтернативи

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

Якщо у вас є постійний потік фонової обробки, яку потрібно виконувати, то розгляньте запуск ваших фонових робітників за допомогою ReplicaSet, і розгляньте використання бібліотеки фонової обробки, такої як https://github.com/resque/resque.

9.4 - Індексоване завдання (Job) для паралельної обробки з фіксованим призначенням роботи

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.24 [stable]

У цьому прикладі ви запустите Job Kubernetes, яке використовує кілька паралельних робочих процесів. Кожен робочий процес — це різний контейнер, що працює у власному Podʼі. Podʼи мають індексний номер, який автоматично встановлює панель управління, що дозволяє кожному Podʼу визначити, над якою частиною загального завдання працювати.

Індекс Podʼа доступний в анотації batch.kubernetes.io/job-completion-index у вигляді рядка, який представляє його десяткове значення. Щоб контейнеризований процес завдання отримав цей індекс, можна опублікувати значення анотації за допомогою механізму downward API. Для зручності панель управління автоматично встановлює downward API для експонування індексу в змінну середовища JOB_COMPLETION_INDEX.

Нижче наведено огляд кроків у цьому прикладі:

  1. Визначте маніфест завдання з використанням індексованого завершення. Downward API дозволяє передавати індекс Podʼа як змінну середовища або файл до контейнера.
  2. Запустіть Indexed завдання на основі цього маніфесту.

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

Ви маєти бути знайомі з базовим, не-паралельним використанням Job.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.21. Для перевірки версії введіть kubectl version.

Оберіть підхід

Щоб отримати доступ до робочого елемента з програми робочого процесу, у вас є кілька варіантів:

  1. Прочитайте змінну середовища JOB_COMPLETION_INDEX. Job контролер автоматично повʼязує цю змінну з анотацією, що містить індекс завершення.
  2. Прочитайте файл, який містить індекс завершення.
  3. Припускаючи, що ви не можете змінити програму, ви можете обгорнути її сценарієм, який читає індекс за допомогою будь-якого з методів вище і перетворює його в щось, що програма може використовувати як вхід.

У цьому прикладі уявіть, що ви вибрали третій варіант, і ви хочете запустити rev утиліту. Ця програма приймає файл як аргумент і виводить зміст у зворотньому порядку.

rev data.txt

Ви будете використовувати інструмент rev з контейнерного образу busybox.

Оскільки це лише приклад, кожен Pod робить лише невеликий шматок роботи (реверсування короткого рядка). У реальному навантаженні ви, наприклад, можете створити Job, що представляє задачу рендерингу 60 секунд відео на основі даних про сцену. Кожен робочий елемент в завданні відеорендерингу буде створювати певний кадр цього відеокліпу. Індексоване завершення означатиме, що кожен Pod у Job знає, який кадр рендерити та опублікувати, рахуючи кадри від початку кліпу.

Визначте Indexed Job

Ось приклад маніфесту Job, який використовує режим завершення Indexed:

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      initContainers:
      - name: 'input'
        image: 'docker.io/library/bash'
        command:
        - "bash"
        - "-c"
        - |
          items=(foo bar baz qux xyz)
          echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt          
        volumeMounts:
        - mountPath: /input
          name: input
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        emptyDir: {}

У вищенаведеному прикладі ви використовуєте вбудовану змінну середовища JOB_COMPLETION_INDEX, яку встановлює контролер завдань для всіх контейнерів. Контейнер ініціалізації відображає індекс на статичне значення та записує його в файл, який спільно використовується з контейнером, що виконує робочий процес через том emptyDir. Додатково ви можете визначити свою власну змінну середовища через downward API для публікації індексу в контейнерах. Ви також можете вибрати завантаження списку значень з ConfigMap як змінної середовища або файлу.

У іншому варіанті ви можете безпосередньо використовувати downward API для передачі значення анотації як файлу тому, як показано у наступному прикладі:

apiVersion: batch/v1
kind: Job
metadata:
  name: 'indexed-job'
spec:
  completions: 5
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: 'worker'
        image: 'docker.io/library/busybox'
        command:
        - "rev"
        - "/input/data.txt"
        volumeMounts:
        - mountPath: /input
          name: input
      volumes:
      - name: input
        downwardAPI:
          items:
          - path: "data.txt"
            fieldRef:
              fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']

Запуск Job

Тепер запустіть Job:

# Це використовує перший підхід (покладаючись на $JOB_COMPLETION_INDEX)
kubectl apply -f https://kubernetes.io/examples/application/job/indexed-job.yaml

При створенні цього Завдання панель управління створює серію Podʼів, по одному для кожного вказаного індексу. Значення .spec.parallelism визначає, скільки може працювати одночасно, в той час, як .spec.completions визначає, скільки Podʼів створює Job в цілому.

Оскільки .spec.parallelism менше, ніж .spec.completions, панель управління чекає, поки деякі з перших Podʼів завершаться, перш ніж запустити ще.

Ви можете зачекати, поки Завдання завершиться успішно, з тайм-аутом:

# Перевірка умови назви нечутлива до регістру
kubectl wait --for=condition=complete --timeout=300s job/indexed-job

Тепер опишіть Завдання та переконайтеся, що воно виконалося успішно.

kubectl describe jobs/indexed-job

Вивід схожий на:

Name:              indexed-job
Namespace:         default
Selector:          controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
Labels:            controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
                   job-name=indexed-job
Annotations:       <none>
Parallelism:       3
Completions:       5
Start Time:        Thu, 11 Mar 2021 15:47:34 +0000
Pods Statuses:     2 Running / 3 Succeeded / 0 Failed
Completed Indexes: 0-2
Pod Template:
  Labels:  controller-uid=bf865e04-0b67-483b-9a90-74cfc4c3e756
           job-name=indexed-job
  Init Containers:
   input:
    Image:      docker.io/library/bash
    Port:       <none>
    Host Port:  <none>
    Command:
      bash
      -c
      items=(foo bar baz qux xyz)
      echo ${items[$JOB_COMPLETION_INDEX]} > /input/data.txt

    Environment:  <none>
    Mounts:
      /input from input (rw)
  Containers:
   worker:
    Image:      docker.io/library/busybox
    Port:       <none>
    Host Port:  <none>
    Command:
      rev
      /input/data.txt
    Environment:  <none>
    Mounts:
      /input from input (rw)
  Volumes:
   input:
    Type:       EmptyDir (тимчасова тека, яка поділяє життя Podʼа)
    Medium:
    SizeLimit:  <unset>
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-njkjj
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-9kd4h
  Normal  SuccessfulCreate  4s    job-controller  Created pod: indexed-job-qjwsz
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-fdhq5
  Normal  SuccessfulCreate  1s    job-controller  Created pod: indexed-job-ncslj

У цьому прикладі ви запускаєте Job з власними значеннями для кожного індексу. Ви можете оглянути вивід одного з Podʼів:

kubectl logs indexed-job-fdhq5 # Змініть це, щоб відповідати імені Podʼа з цього Завдання ```

Вивід схожий на:

xuq

9.5 - Завдання (Job) з комунікацією Pod-Pod

У цьому прикладі ви запустите Job в Indexed completion mode, налаштований таким чином, щоб Podʼи, створені Job, могли комунікувати один з одним, використовуючи назви хостів Podʼів, а не IP-адреси Podʼів.

Podʼи в межах Job можуть потребувати комунікації між собою. Робоче навантаження користувача, що виконується в кожному Podʼі, може звертатися до сервера API Kubernetes для отримання IP-адрес інших Podʼів, але набагато простіше покладатися на вбудований DNS-резолвер Kubernetes.

Job в Indexed completion mode автоматично встановлюють назви хостів Podʼів у форматі ${jobName}-${completionIndex}. Ви можете використовувати цей формат для детермінованого створення назв хостів Podʼів та забезпечення комунікації між Podʼами без необхідності створювати клієнтське з’єднання з панеллю управління Kubernetes для отримання назв хостів/IP-адрес Podʼів через API-запити.

Ця конфігурація корисна для випадків, коли необхідна мережева взаємодія Podʼів, але ви не хочете залежати від мережевого з’єднання з сервером API Kubernetes.

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

Ви повинні вже бути знайомі з основами використання Job.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.21. Для перевірки версії введіть kubectl version.

Запуск роботи з комунікацією між Podʼами

Щоб увімкнути комунікацію між Podʼами з використанням назв хостів Podʼів у Job, ви повинні зробити наступне:

  1. Налаштуйте headless Service з дійсним селектором міток для Podʼів, створених вашим Job. Headless Service має бути в тому ж просторі імен, що й Job. Один із простих способів зробити це — використати селектор job-name: <your-job-name>, оскільки мітка job-name буде додана Kubernetes автоматично. Ця конфігурація активує систему DNS для створення записів назв хостів Podʼів, що виконують ваш обʼєкт Job.

  2. Налаштуйте headless Service як піддомен для Podʼів Job, включивши наступне значення у ваш шаблон специфікації Job:

    subdomain: <headless-svc-name>
    

Приклад

Нижче наведено робочий приклад Job з увімкненою комунікацією між Podʼами через назви хостів Podʼів. Job завершується лише після того, як усі Podʼи успішно пінгують один одного за допомогою назв хостів.


apiVersion: v1
kind: Service
metadata:
  name: headless-svc
spec:
  clusterIP: None # clusterIP має бути None для створення headless service
  selector:
    job-name: example-job # має відповідати імені Job
---
apiVersion: batch/v1
kind: Job
metadata:
  name: example-job
spec:
  completions: 3
  parallelism: 3
  completionMode: Indexed
  template:
    spec:
      subdomain: headless-svc # має відповідати імені Service
      restartPolicy: Never
      containers:
      - name: example-workload
        image: bash:latest
        command:
        - bash
        - -c
        - |
          for i in 0 1 2
          do
            gotStatus="-1"
            wantStatus="0"             
            while [ $gotStatus -ne $wantStatus ]
            do                                       
              ping -c 1 example-job-${i}.headless-svc > /dev/null 2>&1
              gotStatus=$?                
              if [ $gotStatus -ne $wantStatus ]; then
                echo "Failed to ping pod example-job-${i}.headless-svc, retrying in 1 second..."
                sleep 1
              fi
            done                                                         
            echo "Successfully pinged pod: example-job-${i}.headless-svc"
          done          

Після застосування наведеного вище прикладу, Podʼи зможуть звертатись один до одного в мережі, використовуючи: <pod-hostname>.<headless-service-name>. Ви повинні побачити вихідні дані, подібні до наступних:

kubectl logs example-job-0-qws42
Failed to ping pod example-job-0.headless-svc, retrying in 1 second...
Successfully pinged pod: example-job-0.headless-svc
Successfully pinged pod: example-job-1.headless-svc
Successfully pinged pod: example-job-2.headless-svc

9.6 - Паралельна обробка з розширенням

Це завдання демонструє запуск кількох Jobs на основі загального шаблону. Ви можете використовувати цей підхід для обробки пакетних завдань паралельно.

У цьому прикладі є лише три елементи: apple, banana та cherry. Приклади Job обробляють кожен елемент, виводячи рядок, а потім очікуючи дії користувача.

Див. використання Job у реальних навантаженнях, щоб дізнатися, як цей підхід вписується у більш реалістичні випадки використання.

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

Вам слід бути знайомим з базовим, не-паралельним використанням Job.

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

Для базового шаблонування вам потрібна утиліта командного рядка sed.

Для виконання розширеного прикладу шаблонування вам потрібен встановлений Python та бібліотека шаблонів Jinja2 для Python.

Після налаштування Python ви можете встановити Jinja2, виконавши:

pip install --user jinja2

Створення Job на основі шаблону

Спочатку завантажте наступний шаблон Job у файл з назвою job-tmpl.yaml. Ось що ви завантажите:

apiVersion: batch/v1
kind: Job
metadata:
  name: process-item-$ITEM
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox:1.28
        command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
      restartPolicy: Never
# Використайте curl для завантаження job-tmpl.yaml
curl -L -s -O https://k8s.io/examples/application/job/job-tmpl.yaml

Завантажений вами файл ще не є валідним маніфестом Kubernetes. Замість цього цей шаблон є YAML-представленням об’єкта Job з деякими заповнювачами, які потрібно замінити перед використанням. Синтаксис $ITEM не має значення для Kubernetes.

Створення маніфестів з шаблону

Наступний код використовує sed для заміни $ITEM на значення змінної з циклу, зберігаючи результат в тимчасову теку jobs:

# Розмноження шаблону на кілька файлів, по одному для коженого процесу
mkdir -p ./jobs
for item in apple banana cherry
do
  cat job-tmpl.yaml | sed "s/\$ITEM/$i/g" > ./jobs/job-$i.yaml
done

На виході ви маєте отримати три файли:

job-apple.yaml
job-banana.yaml
job-cherry.yaml

Створення Завдань (Job) з маніфестів

Далі, створіть всі три Job, використовуючи файли, які ви створили:

kubectl apply -f ./jobs

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

job.batch/process-item-apple created
job.batch/process-item-banana created
job.batch/process-item-cherry created

Тепер ви можете перевірити стан Job:

kubectl get jobs -l jobgroup=jobexample

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

NAME                  COMPLETIONS   DURATION   AGE
process-item-apple    1/1           14s        22s
process-item-banana   1/1           12s        21s
process-item-cherry   1/1           12s        20s

Використання опції -l для kubectl вибирає лише Job, які є частиною цієї групи Job (в системі можуть бути інші не Завдання (Job)).

Ви також можете перевірити стан Podʼів, використовуючи той самий селектор міток:

kubectl get pods -l jobgroup=jobexample

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

NAME                        READY     STATUS      RESTARTS   AGE
process-item-apple-kixwv    0/1       Completed   0          4m
process-item-banana-wrsf7   0/1       Completed   0          4m
process-item-cherry-dnfu9   0/1       Completed   0          4m

Ви можете використати цю одну команду для перевірки виводу всіх Job одночасно:

kubectl logs -f -l jobgroup=jobexample

Вивід має бути таким:

Processing item apple
Processing item banana
Processing item cherry

Очищення

# Видаліть створені Job
# Ваш кластер автомтично видаляє пов’язані з Job Pod
kubectl delete jobs -l jobgroup=jobexample

Використання розширених параметрів шаблонів

В першому прикладі, кожен екземпляр шаблону мав один параметр, цей параметр тако використовувався в назві Job. Однак, назви обмежені набором символів, які можна використовувати.

Ось трохи складніший шаблон Jinja, для створення маніфестів та обʼєктів на їх основі, з кількома параметрами для кожного Завдання (Job).

В першій частині завдання скористайтесь однорядковим скриптом Python для перетворення шаблонів в набір маніфестів.

Спочатку скопіюйте та вставте наступний шаблон обʼєкта Job у файл з назвою job.yaml.jinja2:

{% set params = [{ "name": "apple", "url": "http://dbpedia.org/resource/Apple", },
                  { "name": "banana", "url": "http://dbpedia.org/resource/Banana", },
                  { "name": "cherry", "url": "http://dbpedia.org/resource/Cherry" }]
%}
{% for p in params %}
{% set name = p["name"] %}
{% set url = p["url"] %}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: jobexample-{{ name }}
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox:1.28
        command: ["sh", "-c", "echo Processing URL {{ url }} && sleep 5"]
      restartPolicy: Never
{% endfor %}

Наведений шаблон визначає два параметри для кожного обʼєкта Job за допомогою списку словників Python (рядки 1-4). Цикл for генерує один маніфест Job для кожного набору параметрів (інші рядки).

Цей приклад використовує можливості YAML. Один файл YAML може містити кілька документів (у цьому випадку маніфестів Kubernetes), розділених рядком ---.

Ви можете передати вивід безпосередньо до kubectl, щоб створити Jobs.

Далі використовуйте цю однорядковий скрипт Python для розширення шаблону:

alias render_template='python -c "from jinja2 import Template; import sys; print(Template(sys.stdin.read()).render());"'

Використовуйте render_template для конвертації параметрів та шаблону в один файл YAML, що містить маніфести Kubernetes:

# Це вимагає визначеного раніше аліасу
cat job.yaml.jinja2 | render_template > jobs.yaml

Ви можете переглянути jobs.yaml, щоб переконатися, що скрипт render_template працює правильно.

Коли ви переконаєтесь, що render_template працює так, як ви задумали, ви можете передати його вивід до kubectl:

cat job.yaml.jinja2 | render_template | kubectl apply -f -

Kubernetes прийме та запустить створені вами Jobs.

Очищення

# Видалення створених вами Jobs
# Ваш кластер автоматично очищає їхні Pods
kubectl delete job -l jobgroup=jobexample

Використання Jobs у реальних робочих навантаженнях

У реальному випадку використання кожен Job виконує деяку суттєву обчислювальну задачу, наприклад, рендеринг кадру фільму або обробку діапазону рядків у базі даних. Якщо ви рендерите відео, ви будете встановлювати $ITEM на номер кадру. Якщо ви обробляєте рядки з таблиці бази даних, ви будете встановлювати $ITEM для представлення діапазону рядків бази даних, які потрібно обробити.

У завданні ви виконували команду для збирання виводу з Podʼів, отримуючи їхні логи. У реальному випадку використання кожен Pod для Job записує свій вивід у надійне сховище перед завершенням. Ви можете використовувати PersistentVolume для кожного Job або зовнішню службу зберігання даних. Наприклад, якщо ви рендерите кадри для відео, використовуйте HTTP PUT щоб включити дані обробленого кадру до URL, використовуючи різні URL для кожного кадру.

Мітки на Job та Podʼах

Після створення Job, Kubernetes автоматично додає додаткові мітки, які вирізняють Podʼи одного Job від Podʼів іншого Job.

У цьому прикладі кожен Job та його шаблон Pod мають мітку: jobgroup=jobexample.

Сам Kubernetes не звертає уваги на мітки з іменем jobgroup. Встановлення мітки для всіх Job, які ви створюєте за шаблоном, робить зручним керування всіма цими Jobs одночасно. У першому прикладі ви використовували шаблон для створення кількох Job. Шаблон гарантує, що кожен Pod також отримує ту саму мітку, тому ви можете перевірити всі Podʼи для цих шаблонних Job за допомогою однієї команди.

Альтернативи

Якщо ви плануєте створити велику кількість обʼєктів Job, ви можете виявити, що:

  • Навіть використовуючи мітки, керування такою кількістю Job є громіздким.
  • Якщо ви створюєте багато Job одночасно, ви можете створити високе навантаження на панель управління Kubernetes. Крім того, сервер API Kubernetes може обмежити швидкість запитів, тимчасово відхиляючи ваші запити зі статусом 429.
  • Ви обмежені квотою ресурсів на Job: сервер API постійно відхиляє деякі ваші запити, коли ви створюєте велику кількість роботи за один раз.

Існують інші шаблони роботи з Job, які ви можете використовувати для обробки значного обсягу роботи без створення великої кількості обʼєктів Job.

Ви також можете розглянути можливість написання власного контролера, щоб автоматично керувати обʼєктами Job.

9.7 - Обробка повторюваних і неповторюваних помилок Pod за допомогою політики збоїв Pod

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [stable]

Цей документ показує, як використовувати політику збоїв Pod, у поєднанні з типовою політикою відмови Podʼа, для покращення контролю над обробкою збоїв на рівні контейнера або Pod у Job.

Визначення політики збоїв Pod може допомогти вам:

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

Ви повинні вже бути знайомі з основним використанням Job.

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.25. Для перевірки версії введіть kubectl version.

Використання політики збоїв Pod для уникнення непотрібних повторних запусків Pod

В наступному прикладі ви можете навчитися використовувати політику збоїв Pod, щоб уникати непотрібних перезапусків Pod, коли збій Pod вказує на неповторювану помилку програмного забезпечення.

Спочатку створіть Job на основі конфігурації:

apiVersion: batch/v1
kind: Job
metadata:
  name: job-pod-failure-policy-failjob
spec:
  completions: 8
  parallelism: 2
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: main
        image: docker.io/library/bash:5
        command: ["bash"]
        args:
        - -c
        - echo "Hello world! I'm going to exit with 42 to simulate a software bug." && sleep 30 && exit 42
  backoffLimit: 6
  podFailurePolicy:
    rules:
    - action: FailJob
      onExitCodes:
        containerName: main
        operator: In
        values: [42]

виконавши команду:

kubectl create -f job-pod-failure-policy-failjob.yaml

Через приблизно 30 секунд весь Job повинен завершитися. Перевірте статус Job, виконавши команду:

kubectl get jobs -l job-name=job-pod-failure-policy-failjob -o yaml

У статусі Job відображаються такі умови:

  • Умова FailureTarget: має поле reason, встановлене в PodFailurePolicy, і поле message з додатковою інформацією про завершення, наприклад, Container main for pod default/job-pod-failure-policy-failjob-8ckj8 failed with exit code 42 matching FailJob rule at index 0. Контролер Job додає цю умову, як тільки Job вважається невдалим. Для отримання деталей дивіться Завершення Job Podʼів.
  • Умова Failed: те ж саме значення для reason і message, що й в умови FailureTarget. Контролер Job додає цю умову після того, як усі Podʼи Job завершено.

Для порівняння, якщо політика збоїв Pod була вимкнена, це б зайняло 6 спроб повторного запуску Pod, на що треба щонайменше 2 хвилини.

Прибирання

Видаліть створений Job:

kubectl delete jobs/job-pod-failure-policy-failjob

Кластер автоматично очищає Pod.

Використання політики збоїв Pod для ігнорування розладів Pod

На наступному прикладі ви можете навчитися використовувати політику збоїв Pod, щоб ігнорувати розлади Pod, які збільшують лічильник повторних спроб Pod до межі .spec.backoffLimit.

  1. Створіть Job на основі конфігурації:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-ignore
    spec:
      completions: 4
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: docker.io/library/bash:5
            command: ["bash"]
            args:
            - -c
            - echo "Hello world! I'm going to exit with 0 (success)." && sleep 90 && exit 0
      backoffLimit: 0
      podFailurePolicy:
        rules:
        - action: Ignore
          onPodConditions:
          - type: DisruptionTarget
    

    виконавши команду:

    kubectl create -f job-pod-failure-policy-ignore.yaml
    
  2. Виконайте цю команду, щоб перевірити nodeName, на якому розміщено Pod:

    nodeName=$(kubectl get pods -l job-name=job-pod-failure-policy-ignore -o jsonpath='{.items[0].spec.nodeName}')
    
  3. Запустить очищення вузла, щоб виселити Pod до завершення його роботи (протягом 90 секунд):

    kubectl drain nodes/$nodeName --ignore-daemonsets --grace-period=0
    
  4. Перевірте .status.failed, щоб переконатися, що лічильник для Job не збільшено:

    kubectl get jobs -l job-name=job-pod-failure-policy-ignore -o yaml
    
  5. Зніміть блокування з вузла:

    kubectl uncordon nodes/$nodeName
    

Job відновиться і завершиться успішно.

Для порівняння, якщо політика збоїв Pod була вимкнена, розлад Pod призведе до завершення всього Job (оскільки .spec.backoffLimit встановлено на 0).

Прибирання

Видаліть створений Job:

kubectl delete jobs/job-pod-failure-policy-ignore

Кластер автоматично очищає Pod.

Використання політики збоїв Pod для уникнення непотрібних повторних запусків Pod на основі власних умов Pod

В наступному прикладі ви можете навчитися використовувати політику збоїв Pod, щоб уникати непотрібних перезапусків Pod на основі власних умов Pod.

  1. Спочатку створіть Job на основі конфігурації:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-pod-failure-policy-config-issue
    spec:
      completions: 8
      parallelism: 2
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: main
            image: "non-existing-repo/non-existing-image:example"
      backoffLimit: 6
      podFailurePolicy:
        rules:
        - action: FailJob
          onPodConditions:
          - type: ConfigIssue
    

    виконавши команду:

    kubectl create -f job-pod-failure-policy-config-issue.yaml
    

    Зверніть увагу, що образ налаштоване неправильно, оскільки його не існує.

  2. Перевірте статус Pod Job, виконавши команду:

    kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    Ви побачите результат, подібний до цього:

    containerStatuses:
    - image: non-existing-repo/non-existing-image:example
       ...
       state:
       waiting:
          message: Back-off pulling image "non-existing-repo/non-existing-image:example"
          reason: ImagePullBackOff
          ...
    phase: Pending
    

    Зверніть увагу, що Pod залишається у фазі Pending, оскільки йому не вдається завантажити неправильно налаштований образ. Це, в принципі, може бути тимчасовою проблемою, і образ може бути завантажений. Однак у цьому випадку образу не існує, тому ми вказуємо на цей факт за допомогою власної умови.

  3. Додайте власну умову. Спочатку підготуйте патч, виконавши команду:

    cat <<EOF > patch.yaml
    status:
      conditions:
      - type: ConfigIssue
        status: "True"
        reason: "NonExistingImage"
        lastTransitionTime: "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
    EOF
    

    По-друге, виберіть один із Pod, створених Job, виконавши команду:

    podName=$(kubectl get pods -l job-name=job-pod-failure-policy-config-issue -o jsonpath='{.items[0].metadata.name}')
    

    Потім застосуйте патч до одного з Pod, виконавши наступну команду:

    kubectl patch pod $podName --subresource=status --patch-file=patch.yaml
    

    Якщо патч успішно застосовано, ви отримаєте повідомлення такого типу:

    pod/job-pod-failure-policy-config-issue-k6pvp patched
    
  4. Видаліть Pod для переходу його до фази Failed, виконавши команду:

    kubectl delete pods/$podName
    
  5. Перевірте статус Job, виконавши:

    kubectl get jobs -l job-name=job-pod-failure-policy-config-issue -o yaml
    

    У статусі Job перегляньте умову Failed з полем reason, рівним PodFailurePolicy. Додатково, поле message містить більш детальну інформацію про завершення завдання, таку як: Pod default/job-pod-failure-policy-config-issue-k6pvp має умову ConfigIssue, яка відповідає правилу FailJob за індексом 0.

Очищення

Видаліть створене вами завдання:

kubectl delete jobs/job-pod-failure-policy-config-issue

Кластер автоматично очищує поди.

Альтернативи

Ви можете покладатись виключно на політику відмови Pod backoff, вказавши поле .spec.backoffLimit завдання. Однак у багатьох ситуаціях важко знайти баланс між встановленням низького значення для .spec.backoffLimit для уникнення непотрібних повторних спроб виконання Podʼів, але достатньо великого, щоб забезпечити, що Job не буде припинено через втручання у роботу Podʼів.

10 - Доступ до застосунків у кластері

Налаштування балансування навантаження, перенаправлення портів або налаштування фаєрволу або DNS-конфігурацій для доступу до застосунків у кластері.

10.1 - Розгортання та доступ до Інфопанелі Kubernetes

Розгорніть вебінтерфейс (Kubernetes Dashboard) та отримайте до нього доступ.

Інфопанель Kubernetes — це вебінтерфейс для кластерів Kubernetes. Ви можете використовувати Інфопанель для розгортання контейнерізованих застосунків в кластері Kubernetes, керувати ресурсами кластера, відстежувати та виправляти проблеми в роботі застосунків. Ви можете використовувати Інфопанель для перегляду роботи застосунків у вашому кластері, створення або зміни ресурсів Kubernetes (таких як Deployment, Job, DaemonSet та інші). Наприклад, ви можете масштабувати Deployment, ініціювати поступове оновлення, перезапустити Pod чи розгорнути новий застосунок за допомоги помічника з розгортання.

Інфопанель також надає інформацію про стан ресурсів Kubernetes у вашому кластері та про помилки, що можуть виникати.

Kubernetes Dashboard UI

Розгортання Dashboard UI

Стандартно Інфопанель не встановлена в кластері Kubernetes. Щоб розгорнути Інфопанель, ви повинні виконати наступну команду:

# Додайте репозиторій kubernetes-dashboard
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
# Розгорніть Helm Release з назвою "kubectl-dashboard" скориставшись чартом kubernetes-dashboard
helm upgrade \
    --install \
      kubernetes-dashboard \
      kubernetes-dashboard/kubernetes-dashboard \
    --create-namespace \
    --namespace kubernetes-dashboard

Отримання доступу до Dashboard UI

Для захисту даних вашого кластера, Dashboard типово розгортається з мінімальною конфігурацією RBAC. Наразі Dashboard підтримує лише вхід за допомогою токену на предʼявника (Bearer Token). Для створення токена для цього демо ви можете скористатися нашим посібником зі створення користувача.

Проксі з командного рядка

Ви можете увімкнути доступ до Dashboard за допомогою інструменту командного рядка kubectl, виконавши наступну команду:

kubectl proxy

Kubectl зробить Dashboard доступним за адресою http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/.

Інтерфейс можна лише відкрити з машини, на якій виконується команда. Дивіться kubectl proxy --help для отримання додаткових опцій.

Вітальна сторінка

Коли ви отримуєте доступ до Інфопанелі в порожньому кластері, ви побачите вітальну сторінку. Ця сторінка містить посилання на цей документ, а також кнопку для розгортання вашого першого застосунку. Крім того, ви можете побачити, які системні застосунки запускаються стандартно у просторі імен kube-system вашого кластера, наприклад, сама Інфопанель.

Вітальна сторінка Kubernetes Dashboard

Розгортання контейнеризованих застосунків

Dashboard дозволяє створювати та розгортати контейнеризований застосунок як Deployment та необов’язковий Service за допомогою простого майстра. Ви можете або вручну вказати деталі застосунку, або завантажити YAML або JSON файл маніфесту, що містить конфігурацію застосунку.

Натисніть кнопку CREATE у верхньому правому куті будь-якої сторінки, щоб розпочати.

Встановлення параметрів застосунку

Майстер розгортання очікує, що ви надасте наступну інформацію:

  • Назва застосунку (обов’язково): Назва вашого застосунку. Мітка з цією назвою буде додана до Deployment та Service (якщо є), які будуть розгорнуті.

    Назва застосунку повинна бути унікальною в межах вибраного простору імен Kubernetes. Вона повинна починатися з малої літери та закінчуватися малою літерою або цифрою, і містити лише малі літери, цифри та дефіси (-). Обмеження на довжину становить 24 символи. Початкові та кінцеві пробіли ігноруються.

  • Образ контейнера (обов’язково): URL публічного Docker образу контейнера у будь-якому реєстрі, або приватного образу (зазвичай розміщеного в Google Container Registry або Docker Hub). Специфікація образу контейнера повинна закінчуватися двокрапкою.

  • Кількість Podʼів (обов’язково): Кількість Podʼів, з використанням яких ви хочете розгорнути свій застосунок. Значення повинно бути позитивним цілим числом.

    Буде створено Deployment, щоб підтримувати потрібну кількість Podʼів у вашому кластері.

  • Service (необов’язково): Для деяких частин вашого застосунку (наприклад, фронтендів) ви можете захотіти експонувати Service на зовнішню, можливо, публічну IP-адресу за межами вашого кластера (зовнішній Service).

    Інші Service, які видно тільки всередині кластера, називаються внутрішніми Service.

    Незалежно від типу Service, якщо ви вирішите створити Service і ваш контейнер слухає на порту (вхідному), вам потрібно вказати два порти. Service буде створено, шляхом зіставлення порту (вхідного) на цільовий порт, який бачить контейнер. Цей Service буде переспрямовувати трафік на ваші розгорнуті Podʼи. Підтримувані протоколи — TCP та UDP. Внутрішнє DNS-імʼя для цього Service буде значенням, яке ви вказали як назву застосунку вище.

Якщо необхідно, ви можете розгорнути розділ Advanced options, де можна вказати більше налаштувань:

  • Опис: Текст, який ви введете тут, буде додано як анотацію до Deployment та відображатиметься в деталях застосунку.

  • Мітки: Стандартні мітки, що використовуються для вашого застосунку, - це назва застосунку та версія. Ви можете вказати додаткові мітки, які будуть застосовані до Deployment, Service (якщо є) та Podʼів, такі як release, environment, tier, partition та release track.

    Приклад:

    release=1.0
    tier=frontend
    environment=pod
    track=stable
    
  • Простір імен: Kubernetes підтримує кілька віртуальних кластерів, що працюють поверх одного фізичного кластера. Ці віртуальні кластери називаються просторами імен. Вони дозволяють розділити ресурси на логічно названі групи.

    Dashboard пропонує всі доступні простори імен у розгортаючомуся списку та дозволяє створити новий простір імен. Назва простору імен може містити максимум 63 алфавітно-цифрових символи та дефіси (-), але не може містити великі літери. Назви просторів імен не повинні складатися лише з цифр. Якщо назва встановлена як число, наприклад 10, Pod буде розміщений у просторі імен default.

    У разі успішного створення простору імен він обирається стандартним. Якщо створення не вдається, вибирається перший простір імен.

  • Secret завантаження образу: У випадку, якщо вказаний Docker образ контейнера є приватним, він може вимагати облікові дані Secret завантаження.

    Dashboard пропонує всі доступні Secret у розгортаючомуся списку та дозволяє створити новий. Назва Secret повинна відповідати синтаксису доменного імені DNS, наприклад new.image-pull.secret. Вміст Secret повинен бути закодований у base64 і вказаний у файлі .dockercfg. Назва Secret може складатися максимум з 253 символів.

    У разі успішного створення Secret завантаження образу він обирається стандартним. Якщо створення не вдається, жоден Secret не застосовується.

  • Вимога до процесора (ядра) та Вимога до памʼяті (MiB): Ви можете вказати мінімальні обмеження ресурсів для контейнера. Стандартно Podʼи працюють без обмежень на процесор і памʼять.

  • Команда запуску та Аргументи команди запуску: Стандартно ваші контейнери виконують вказану Docker образом контейнера команду запуску. Ви можете використовувати опції команд і аргументів для перевизначення стандартної команди.

  • Запуск у привілейованому режимі: Це налаштування визначає, чи є процеси в привілейованих контейнерах еквівалентними процесам, що виконуються з правами root на хості. Привілейовані контейнери можуть використовувати можливості, такі як маніпулювання мережею та доступ до пристроїв.

  • Змінні середовища: Kubernetes експонує Service через змінні середовища. Ви можете складати змінні середовища або передавати аргументи до ваших команд, використовуючи значення змінних середовища. Вони можуть використовуватися в застосунках для пошуку Service. Значення можуть посилатися на інші змінні за допомогою синтаксису $(VAR_NAME).

Завантаження YAML або JSON файлу

Kubernetes підтримує декларативну конфігурацію. У цьому стилі всі конфігурації зберігаються в маніфестах (конфігураційних файлах YAML або JSON). Маніфести використовують схеми ресурсів API Kubernetes.

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

Використання Dashboard

Наступні розділи описують вигляди інтерфейсу користувача Dashboard Kubernetes; що вони забезпечують і як їх можна використовувати.

Коли у кластері визначено обʼєкти Kubernetes, Dashboard показує їх у початковому вигляді. Типово показуються лише обʼєкти з простору імен default, це можна змінити за допомогою селектора простору імен, розташованого в меню навігації.

Dashboard показує більшість типів обʼєктів Kubernetes та групує їх у кілька категорій меню.

Огляд адміністратора

Для адміністраторів кластерів та просторів імен Dashboard перелічує вузли (Nodes), простори імен (Namespaces) та постійні томи (PersistentVolumes) і має детальні елементи для них. Список вузлів містить метрики використання ЦП та памʼяті, агреговані по всіх вузлах. Детальний вигляд показує метрики для вузла, його специфікацію, стан, виділені ресурси, події та Podʼи, що працюють на вузлі.

Навантаження

Показує всі застосунки, що працюють у вибраному просторі імен. Елемент перераховує застосунки за типом навантаження (наприклад: Deployments, ReplicaSets, StatefulSets). Кожен тип навантаження можна переглядати окремо. Списки узагальнюють корисну інформацію про навантаження, таку як кількість готових Podʼів для ReplicaSet або поточне використання памʼяті для Pod.

Детальні панелі для навантажень показують інформацію про стан та специфікацію і показують звʼязки між обʼєктами. Наприклад, Podʼи, які контролює ReplicaSet, або нові ReplicaSets та HorizontalPodAutoscalers для Deployments.

Services

Показує ресурси Kubernetes, які дозволяють експонувати Service для зовнішнього світу та виявляти їх всередині кластеру. З цієї причини, панелі Service та Ingress показують Podʼи, на які вони спрямовані, внутрішні точки доступу для мережевих зʼєднань в кластері та зовнішні точки доступу для зовнішніх користувачів.

Сховище

Панель сховища показує ресурси PersistentVolumeClaim, які використовуються застосунками для зберігання даних.

ConfigMaps та Secrets

Показує всі ресурси Kubernetes, які використовуються для поточної конфігурації застосунків, що працюють у кластерах. Панель дозволяє редагувати та керувати обʼєктами конфігурації та показує Secret, які є типово прихованими.

Переглядач логів

Списки Podʼів та детальні сторінки мають посилання на переглядача логів, вбудованого у Dashboard. Переглядач дозволяє переглядати логи з контейнерів, що належать окремому Podʼу.

Переглядач логів

Що далі

Для отримання додаткової інформації дивіться сторінку проєкту Kubernetes Dashboard.

10.2 - Доступ до кластерів

В цій темі обговорюється кілька способів взаємодії з кластерами.

Перший доступ з kubectl

При першому доступі до Kubernetes API ми пропонуємо використовувати Kubernetes CLI, kubectl.

Щоб отримати доступ до кластера, вам потрібно знати розташування кластера та мати облікові дані для доступу до нього. Зазвичай це налаштовується автоматично, коли ви проходите Посібник Початок роботи, або хтось інший налаштував кластер і надав вам облікові дані та його розташування.

Перевірте розташування та облікові дані, про які знає kubectl, за допомогою цієї команди:

kubectl config view

Багато з прикладів надають введення до використання kubectl, а повна документація знаходиться у довіднику kubectl.

Прямий доступ до REST API

Kubectl опрацьовує розташування та автентифікацію до apiserver. Якщо ви хочете безпосередньо звертатися до REST API за допомогою http-клієнта, такого як curl або wget, або вебоглядача, існує кілька способів знайти розташування та автентифікуватись:

  • Запустіть kubectl в режимі проксі.
    • Рекомендований підхід.
    • Використовує збережене розташування apiserver.
    • Перевіряє особу apiserver за допомогою самопідписного сертифікату. Немає можливості MITM.
    • Виконує автентифікацію на apiserver.
    • У майбутньому може здійснювати інтелектуальне балансування навантаження та відмовостійкість на стороні клієнта.
  • Надайте розташування та облікові дані безпосередньо http-клієнту.
    • Альтернативний підхід.
    • Працює з деякими типами клієнтського коду, які не працюють з проксі.
    • Потрібно імпортувати кореневий сертифікат у ваш оглядач для захисту від MITM.

Використання kubectl proxy

Наступна команда запускає kubectl в режимі, де він діє як зворотний проксі. Вона обробляє розташування apiserver та автентифікацію. Запустіть її так:

kubectl proxy --port=8080

Дивіться опис kubectl proxy для отримання додаткової інформації.

Після цього ви можете досліджувати API за допомогою curl, wget або оглядача, замінюючи localhost на [::1] для IPv6, ось так:

curl http://localhost:8080/api/

Вихід буде схожий на це:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

Без kubectl proxy

Використовуйте kubectl apply і kubectl describe secret... для створення токена для стандартного облікового запису за допомогою grep/cut:

Спочатку створіть Secret, запитуючи токен для облікового запису стандартного ServiceAccount:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: default-token
  annotations:
    kubernetes.io/service-account.name: default
type: kubernetes.io/service-account-token
EOF

Далі, зачекайте, поки контролер токенів не заповнить Secret токеном:

while ! kubectl describe secret default-token | grep -E '^token' >/dev/null; do
  echo "waiting for token..." >&2
  sleep 1
done

Отримайте і використовуйте згенерований токен:

APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
TOKEN=$(kubectl describe secret default-token | grep -E '^token' | cut -f2 -d':' | tr -d " ")

curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

Вихід буде схожий на це:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

Використовуючи jsonpath:

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl get secret default-token -o jsonpath='{.data.token}' | base64 --decode)

curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure

Вихід буде схожий на це:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.1.149:443"
    }
  ]
}

Вищенаведені приклади використовують прапорець --insecure. Це залишає їх вразливими до атак MITM. Коли kubectl отримує доступ до кластера, він використовує збережений кореневий сертифікат та клієнтські сертифікати для доступу до сервера. (Вони встановлюються в теку ~/.kube). Оскільки сертифікати кластера зазвичай самопідписні, це може вимагати спеціальної конфігурації, щоб змусити вашого http-клієнта використовувати кореневий сертифікат.

У деяких кластерах apiserver не вимагає автентифікації; він може працювати на localhost або бути захищеним файрволом. Для цього немає стандарту. Контроль доступу до API описує, як адміністратор кластера може це налаштувати.

Програмний доступ до API

Kubernetes офіційно підтримує клієнтські бібліотеки для Go та Python.

Клієнт Go

  • Щоб отримати бібліотеку, виконайте наступну команду: go get k8s.io/client-go@kubernetes-<kubernetes-version-number>, дивіться INSTALL.md для детальних інструкцій з встановлення. Дивіться https://github.com/kubernetes/client-go щоб дізнатися, які версії підтримуються.
  • Напишіть додаток на основі клієнтів client-go. Зверніть увагу, що client-go визначає свої власні API обʼєкти, тому, якщо необхідно, будь ласка, імпортуйте визначення API з client-go, а не з основного репозиторію, наприклад, import "k8s.io/client-go/kubernetes" є правильним.

Go клієнт може використовувати той же файл kubeconfig, що й CLI kubectl для знаходження та автентифікації до apiserver. Дивіться цей приклад.

Якщо застосунок розгорнуто як Pod у кластері, будь ласка, зверніться до наступного розділу.

Клієнт Python

Щоб використовувати клієнта Python, виконайте наступну команду: pip install kubernetes. Дивіться сторінку Python Client Library для інших варіантів встановлення.

Клієнт Python може використовувати той же файл kubeconfig, що й CLI kubectl для знаходження та автентифікації до apiserver. Дивіться цей приклад.

Інші мови

Існують клієнтські бібліотеки для доступу до API з інших мов. Дивіться документацію для інших бібліотек щодо того, як вони автентифікуються.

Доступ до API з Pod

При доступі до API з Pod, розташування та автентифікація на API сервері дещо відрізняються.

Будь ласка, перевірте Доступ до API з Pod для отримання додаткової інформації.

Доступ до сервісів, що працюють на кластері

Попередній розділ описує, як підʼєднатися до API сервера Kubernetes. Для інформації про підключення до інших сервісів, що працюють на кластері Kubernetes, дивіться Доступ до сервісів кластера.

Запит перенаправлення

Можливості перенаправлення були визнані застарілими та видалені. Будь ласка, використовуйте проксі (дивіться нижче) замість цього.

Так багато проксі

Існує кілька різних проксі, які ви можете зустріти при використанні Kubernetes:

  1. kubectl proxy:

    • працює на десктопі користувача або в Pod
    • проксі з локальної адреси до Kubernetes apiserver
    • клієнт проксі використовує HTTP
    • проксі apiserver використовує HTTPS
    • знаходить apiserver
    • додає заголовки автентифікації
  2. apiserver proxy:

    • є бастіоном, вбудованим у apiserver
    • зʼєднує користувача ззовні кластера з IP-адресами кластера, які інакше можуть бути недосяжні
    • працює в процесах apiserver
    • клієнт проксі використовує HTTPS (або http, якщо apiserver налаштований відповідним чином)
    • проксі може використовувати HTTP або HTTPS, як обрано проксі, використовуючи доступну інформацію
    • може використовуватися для доступу до Node, Pod або Service
    • забезпечує балансування навантаження при використанні для доступу до Service
  3. kube proxy:

    • працює на кожному вузлі
    • проксі UDP та TCP
    • не розуміє HTTP
    • забезпечує балансування навантаження
    • використовується лише для доступу до Service
  4. Проксі/Балансувальник навантаження перед apiserver(ами):

    • існування та реалізація варіюється від кластера до кластера (наприклад, nginx)
    • знаходиться між усіма клієнтами та одним або декількома apiserver
    • діє як балансувальник навантаження, якщо є кілька apiserver.
  5. Хмарні балансувальники навантаження на зовнішніх сервісах:

    • надаються деякими постачальниками хмарних послуг (наприклад, AWS ELB, Google Cloud Load Balancer)
    • створюються автоматично, коли сервіс Kubernetes має тип LoadBalancer
    • використовують лише UDP/TCP
    • реалізація варіюється серед постачальників хмарних послуг.

Користувачі Kubernetes зазвичай не повинні турбуватися про будь-що, крім перших двох типів. Адміністратор кластера зазвичай забезпечить правильне налаштування останніх типів.

10.3 - Налаштування доступу до декількох кластерів

Ця сторінка показує, як налаштувати доступ до декількох кластерів за допомогою конфігураційних файлів. Після того, як ваші кластери, користувачі та контексти визначені в одному або декількох конфігураційних файлах, ви можете швидко перемикатися між кластерами, використовуючи команду kubectl config use-context.

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

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

Щоб перевірити, чи встановлений kubectl, виконайте команду kubectl version --client. Версія kubectl повинна бути в межах однієї мінорної версії API сервера вашого кластера.

Визначення кластерів, користувачів і контекстів

Припустимо, у вас є два кластери: один для розробки, а інший для тестування. У кластері development ваші фронтенд розробники працюють в просторі імен frontend, а розробники, які опікуються зберіганням даних працюють в просторі імен storage. У кластері test розробники працюють в стандартному просторі імен default або створюють додаткові простори імен за потреби. Доступ до кластера розробки вимагає автентифікації за сертифікатом. Доступ до тестового кластера вимагає автентифікації за іменем користувача та паролем.

Створіть теку з назвою config-exercise. У вашій теці config-exercise створіть файл з назвою config-demo з наступним вмістом:

apiVersion: v1
kind: Config
preferences: {}

clusters:
- cluster:
  name: development
- cluster:
  name: test

users:
- name: developer
- name: experimenter

contexts:
- context:
  name: dev-frontend
- context:
  name: dev-storage
- context:
  name: exp-test

Конфігураційний файл описує кластери, користувачів і контексти. Ваш файл config-demo має структуру для опису двох кластерів, двох користувачів і трьох контекстів.

Перейдіть до теки config-exercise. Виконайте ці команди, щоб додати деталі кластера до вашого конфігураційного файлу:

kubectl config --kubeconfig=config-demo set-cluster development --server=https://1.2.3.4 --certificate-authority=fake-ca-file
kubectl config --kubeconfig=config-demo set-cluster test --server=https://5.6.7.8 --insecure-skip-tls-verify

Додайте відомості про користувачів до вашого конфігураційного файлу:

kubectl config --kubeconfig=config-demo set-credentials developer --client-certificate=fake-cert-file --client-key=fake-key-file
kubectl config --kubeconfig=config-demo set-credentials experimenter --username=exp --password=some-password

Додайте деталі контексту до вашого конфігураційного файлу:

kubectl config --kubeconfig=config-demo set-context dev-frontend --cluster=development --namespace=frontend --user=developer
kubectl config --kubeconfig=config-demo set-context dev-storage --cluster=development --namespace=storage --user=developer
kubectl config --kubeconfig=config-demo set-context exp-test --cluster=test --namespace=default --user=experimenter

Відкрийте ваш файл config-demo, щоб побачити додані деталі. Як альтернативу відкриттю файлу config-demo, можна використовувати команду config view.

kubectl config --kubeconfig=config-demo view

Вивід показує два кластери, двох користувачів і три контексти:

apiVersion: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
- cluster:
    insecure-skip-tls-verify: true
    server: https://5.6.7.8
  name: test
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: test
    namespace: default
    user: experimenter
  name: exp-test
current-context: ""
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file
- name: experimenter
  user:
    # Примітка до документації (цей коментар НЕ є частиною виводу команди).
    # Зберігання паролів у конфігурації клієнта Kubernetes є ризикованим.
    # Кращою альтернативою буде використання втулка облікових даних
    # і зберігати облікові дані окремо.
    # Див. https://kubernetes.io/docs/reference/access-authn-authz/authentication/client-go-credential-plugins
    password: some-password
    username: exp

Файли fake-ca-file, fake-cert-file та fake-key-file вище є заповнювачами для шляхів до сертифікатів. Вам потрібно замінити їх на реальні шляхи до сертифікатів у вашому середовищі.

Іноді ви можете захотіти використовувати дані у форматі Base64 замість окремих файлів сертифікатів; у такому випадку вам потрібно додати суфікс -data до ключів, наприклад, certificate-authority-data, client-certificate-data, client-key-data.

Кожен контекст є трійкою (кластер, користувач, простір імен). Наприклад, контекст dev-frontend означає: "Використовувати облікові дані користувача developer для доступу до простору імен frontend у кластері development".

Встановіть поточний контекст:

kubectl config --kubeconfig=config-demo use-context dev-frontend

Тепер, коли ви введете команду kubectl, дія буде застосовуватися до кластера і простору імен, вказаних у контексті dev-frontend. І команда буде використовувати облікові дані користувача, вказаного у контексті dev-frontend.

Щоб побачити лише інформацію про конфігурацію, повʼязану з поточним контекстом, використовуйте прапорець --minify.

kubectl config --kubeconfig=config-demo view --minify

Вивід показує інформацію про конфігурацію, повʼязану з контекстом dev-frontend:

api

Version: v1
clusters:
- cluster:
    certificate-authority: fake-ca-file
    server: https://1.2.3.4
  name: development
contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
current-context: dev-frontend
kind: Config
preferences: {}
users:
- name: developer
  user:
    client-certificate: fake-cert-file
    client-key: fake-key-file

Тепер припустимо, що ви хочете попрацювати деякий час у тестовому кластері.

Змініть поточний контекст на exp-test:

kubectl config --kubeconfig=config-demo use-context exp-test

Тепер будь-яка команда kubectl, яку ви введете, буде застосовуватися до стандартного простору імен default кластера test. І команда буде використовувати облікові дані користувача, вказаного у контексті exp-test.

Перегляньте конфігурацію, повʼязану з новим поточним контекстом exp-test.

kubectl config --kubeconfig=config-demo view --minify

Нарешті, припустимо, що ви хочете попрацювати деякий час у просторі імен storage кластера development.

Змініть поточний контекст на dev-storage:

kubectl config --kubeconfig=config-demo use-context dev-storage

Перегляньте конфігурацію, повʼязану з новим поточним контекстом dev-storage.

kubectl config --kubeconfig=config-demo view --minify

Створення другого конфігураційного файлу

У вашій теці config-exercise створіть файл з назвою config-demo-2 з наступним вмістом:

apiVersion: v1
kind: Config
preferences: {}

contexts:
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up

Вищезазначений конфігураційний файл визначає новий контекст з назвою dev-ramp-up.

Встановіть змінну середовища KUBECONFIG

Перевірте, чи є у вас змінна середовища з назвою KUBECONFIG. Якщо так, збережіть поточне значення вашої змінної середовища KUBECONFIG, щоб ви могли відновити її пізніше. Наприклад:

Linux

export KUBECONFIG_SAVED="$KUBECONFIG"

Windows PowerShell

$Env:KUBECONFIG_SAVED=$ENV:KUBECONFIG

Змінна середовища KUBECONFIG є списком шляхів до конфігураційних файлів. Список розділяється двокрапкою для Linux і Mac та крапкою з комою для Windows. Якщо у вас є змінна середовища KUBECONFIG, ознайомтеся з конфігураційними файлами у списку.

Тимчасово додайте два шляхи до вашої змінної середовища KUBECONFIG. Наприклад:

Linux

export KUBECONFIG="${KUBECONFIG}:config-demo:config-demo-2"

Windows PowerShell

$Env:KUBECONFIG=("config-demo;config-demo-2")

У вашій теці config-exercise виконайте цю команду:

kubectl config view

Вивід показує обʼєднану інформацію з усіх файлів, зазначених у вашій змінній середовища KUBECONFIG. Зокрема, зверніть увагу, що обʼєднана інформація містить контекст dev-ramp-up з файлу config-demo-2 і три контексти з файлу config-demo:

contexts:
- context:
    cluster: development
    namespace: frontend
    user: developer
  name: dev-frontend
- context:
    cluster: development
    namespace: ramp
    user: developer
  name: dev-ramp-up
- context:
    cluster: development
    namespace: storage
    user: developer
  name: dev-storage
- context:
    cluster: test
    namespace: default
    user: experimenter
  name: exp-test

Для отримання додаткової інформації про те, як обʼєднуються файли kubeconfig, дивіться Організація доступу до кластерів за допомогою файлів kubeconfig.

Ознайомтеся з текою $HOME/.kube

Якщо у вас вже є кластер і ви можете використовувати kubectl для взаємодії з кластером, то, ймовірно, у вас є файл з назвою config у теці $HOME/.kube.

Перейдіть до теки $HOME/.kube і перегляньте, які файли там знаходяться. Зазвичай, там є файл з назвою config. Також можуть бути інші конфігураційні файли у цій теці. Ознайомтеся зі змістом цих файлів.

Додайте $HOME/.kube/config до вашої змінної середовища KUBECONFIG

Якщо у вас є файл $HOME/.kube/config і він ще не зазначений у вашій змінній середовища KUBECONFIG, додайте його до вашої змінної середовища KUBECONFIG зараз. Наприклад:

Linux

export KUBECONFIG="${KUBECONFIG}:${HOME}/.kube/config"

Windows PowerShell

$Env:KUBECONFIG="$Env:KUBECONFIG;$HOME\.kube\config"

Перегляньте інформацію про конфігурацію, обʼєднану з усіх файлів, які зараз зазначені у вашій змінній середовища KUBECONFIG. У вашій теці config-exercise введіть:

kubectl config view

Очищення

Поверніть вашу змінну середовища KUBECONFIG до її оригінального значення. Наприклад:

Linux

export KUBECONFIG="$KUBECONFIG_SAVED"

Windows PowerShell

$Env:KUBECONFIG=$ENV:KUBECONFIG_SAVED

Перевірте субʼєкт, представлений kubeconfig

Не завжди очевидно, які атрибути (імʼя користувача, групи) ви отримаєте після автентифікації в кластері. Це може бути ще складніше, якщо ви керуєте більше ніж одним кластером одночасно.

Існує підкоманда kubectl для перевірки атрибутів субʼєкта, таких як імʼя користувача, для вашого вибраного контексту клієнта Kubernetes: kubectl auth whoami.

Детальніше читайте у Доступ до інформації автентифікації клієнта через API.

Що далі

10.4 - Використання перенаправлення портів для доступу до застосунків у кластері

Ця сторінка показує, як використовувати kubectl port-forward для підключення до сервера MongoDB, який працює у кластері Kubernetes. Такий тип підключення може бути корисним для налагодження бази даних.

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

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

    Версія вашого Kubernetes сервера має бути не старішою ніж v1.10. Для перевірки версії введіть kubectl version.
  • Встановіть MongoDB Shell.

Створення розгортання та сервісу MongoDB

  1. Створіть Deployment, що запускає MongoDB:

    kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-deployment.yaml
    

    Вивід успішної команди підтверджує, що Deployment створено:

    deployment.apps/mongo created
    

    Перегляньте стан Podʼа, щоб переконатися, що він готовий:

    kubectl get pods
    

    Вивід відображає показує Pod:

    NAME                     READY   STATUS    RESTARTS   AGE
    mongo-75f59d57f4-4nd6q   1/1     Running   0          2m4s
    

    Перегляньте стан Deployment:

    kubectl get deployment
    

    Вивід відображає, що Deployment було створено:

    NAME    READY   UP-TO-DATE   AVAILABLE   AGE
    mongo   1/1     1            1           2m21s
    

    Deployment автоматично керує ReplicaSet. Перегляньте стан ReplicaSet, використовуючи:

    kubectl get replicaset
    

    Вивід показує, що ReplicaSet був створений:

    NAME               DESIRED   CURRENT   READY   AGE
    mongo-75f59d57f4   1         1         1       3m12s
    
  2. Створіть Service для доступу до MongoDB в мережі:

    kubectl apply -f https://k8s.io/examples/application/mongodb/mongo-service.yaml
    

    Вивід успішної команди підтверджує, що Service був створений:

    service/mongo created
    

    Перевірте створений Service:

    kubectl get service mongo
    

    Вивід показує створений Service:

    NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)     AGE
    mongo   ClusterIP   10.96.41.183   <none>        27017/TCP   11s
    
  3. Переконайтеся, що сервер MongoDB працює у Pod та слухає на порту 27017:

    # Замініть mongo-75f59d57f4-4nd6q на імʼя Pod
    kubectl get pod mongo-75f59d57f4-4nd6q --template='{{(index (index .spec.containers 0).ports 0).containerPort}}{{"\n"}}'
    

    Вивід показує порт для MongoDB у цьому Pod:

    27017
    

    27017 є офіційним TCP портом для MongoDB.

Перенаправлення локального порту на порт Pod

  1. kubectl port-forward дозволяє використовувати імʼя ресурсу, такого як імʼя Podʼа, для вибору відповідного Podʼа для перенаправлення портів.

    # Замініть mongo-75f59d57f4-4nd6q на імʼя Pod
    kubectl port-forward mongo-75f59d57f4-4nd6q 28015:27017
    

    що те саме, що і

    kubectl port-forward pods/mongo-75f59d57f4-4nd6q 28015:27017
    

    або

    kubectl port-forward deployment/mongo 28015:27017
    

    або

    kubectl port-forward replicaset/mongo-75f59d57f4 28015:27017
    

    або

    kubectl port-forward service/mongo 28015:27017
    

    Будь-яка з наведених вище команд працює. Вивід схожий на це:

    Forwarding from 127.0.0.1:28015 -> 27017
    Forwarding from [::1]:28015 -> 27017
    
  2. Запустіть інтерфейс командного рядка MongoDB:

    mongosh --port 28015
    
  3. На командному рядку MongoDB введіть команду ping:

    db.runCommand( { ping: 1 } )
    

    Успішний запит ping повертає:

    { ok: 1 }
    

Дозвольте kubectl вибрати локальний порт

Якщо вам не потрібен конкретний локальний порт, ви можете дозволити kubectl вибрати та призначити локальний порт і таким чином позбавити себе від необхідності керувати конфліктами локальних портів, з дещо простішим синтаксисом:

kubectl port-forward deployment/mongo :27017

Інструмент kubectl знаходить номер локального порту, який не використовується (уникаючи низьких номерів портів, оскільки вони можуть використовуватися іншими застосунками). Вивід схожий на:

Forwarding from 127.0.0.1:63753 -> 27017
Forwarding from [::1]:63753 -> 27017

Обговорення

Підключення до локального порту 28015 пересилаються на порт 27017 Podʼа, який запускає сервер MongoDB. З цим підключенням ви можете використовувати свій локальний робочий компʼютер для налагодження бази даних, яка працює у Pod.

Що далі

Дізнайтеся більше про kubectl port-forward.

10.5 - Використання Service для доступу до застосунку у кластері

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

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

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

Цілі

  • Запустити два екземпляри застосунку Hello World.
  • Створити обʼєкт Service, який експонує порт вузла.
  • Використовувати обʼєкт Service для доступу до запущеного застосунку.

Створення Service для застосунку, який працює у двох Podʼах

Ось конфігураційний файл для Deployment застосунку:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
spec:
  selector:
    matchLabels:
      run: load-balancer-example
  replicas: 2
  template:
    metadata:
      labels:
        run: load-balancer-example
    spec:
      containers:
        - name: hello-world
          image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0
          ports:
            - containerPort: 8080
              protocol: TCP
  1. Запустіть застосунок Hello World у вашому кластері: Створіть Deployment застосунку, використовуючи файл вище:

    kubectl apply -f https://k8s.io/examples/service/access/hello-application.yaml
    

    Попередня команда створює Deployment та повʼязаний з ним ReplicaSet. ReplicaSet має два Podʼи кожен з яких запускає застосунок Hello World.

  2. Перегляньте інформацію про Deployment:

    kubectl get deployments hello-world
    kubectl describe deployments hello-world
    
  3. Перегляньте інформацію про ваші обʼєкти ReplicaSet:

    kubectl get replicasets
    kubectl describe replicasets
    
  4. Створіть обʼєкт Service, який експонує Deployment:

    kubectl expose deployment hello-world --type=NodePort --name=example-service
    
  5. Перегляньте інформацію про Service:

    kubectl describe services example-service
    

    Вивід буде схожий на цей:

    Name:                   example-service
    Namespace:              default
    Labels:                 run=load-balancer-example
    Annotations:            <none>
    Selector:               run=load-balancer-example
    Type:                   NodePort
    IP:                     10.32.0.16
    Port:                   <unset> 8080/TCP
    TargetPort:             8080/TCP
    NodePort:               <unset> 31496/TCP
    Endpoints:              10.200.1.4:8080, 10.200.2.5:8080
    Session Affinity:       None
    Events:                 <none>
    

    Занотуйте значення NodePort для Service. Наприклад, у попередньому виводі значення NodePort становить 31496.

  6. Перегляньте Podʼи, що запускають застосунок Hello World:

    kubectl get pods --selector="run=load-balancer-example" --output=wide
    

    Вивід буде схожий на цей:

    NAME                           READY   STATUS    ...  IP           NODE
    hello-world-2895499144-bsbk5   1/1     Running   ...  10.200.1.4   worker1
    hello-world-2895499144-m1pwt   1/1     Running   ...  10.200.2.5   worker2
    
  7. Отримайте публічну IP-адресу одного з ваших вузлів, що запускає Pod Hello World. Як ви отримаєте цю адресу залежить від того, як ви налаштували свій кластер. Наприклад, якщо ви використовуєте Minikube, ви можете побачити адресу вузла, виконавши команду kubectl cluster-info. Якщо ви використовуєте trptvgkzhb Google Compute Engine, ви можете використати команду gcloud compute instances list для перегляду публічних адрес ваших вузлів.

  8. На обраному вами вузлі створіть правило брандмауера, яке дозволяє TCP-трафік на вашому порту вузла. Наприклад, якщо ваш Service має значення NodePort 31568, створіть правило брандмауера, яке дозволяє TCP-трафік на порт 31568. Різні постачальники хмарних послуг пропонують різні способи налаштування правил брандмауера.

  9. Використовуйте адресу вузла та порт вузла для доступу до застосунку Hello World:

    curl http://<public-node-ip>:<node-port>
    

    де <public-node-ip> — це публічна IP-адреса вашого вузла, а <node-port> — це значення NodePort для вашого Service. Відповідь на успішний запит буде повідомленням з привітанням:

    Hello, world!
    Version: 2.0.0
    Hostname: hello-world-cdd4458f4-m47c8
    

Використання конфігураційного файлу Service

Як альтернатива використанню kubectl expose, ви можете використовувати конфігураційний файл Service для створення Service.

Очищення

Щоб видалити Service, введіть цю команду:

kubectl delete services example-service

Щоб видалити Deployment, ReplicaSet та Podʼи, що запускають застосунок Hello World, введіть цю команду:

 kubectl delete deployment hello-world

Що далі

Ознайомтесь з посібником Підключення застосунків за допомогою Service.

10.6 - Зʼєднання фронтенду з бекендом за допомогою Service

Це завдання показує, як створити мікросервіси frontend та backend. Мікросервіс бекенд є сервісом для привітання. Фронтенд експонує backend за допомогою nginx та обʼєкта Kubernetes Service.

Цілі

  • Створити та запустити зразок мікросервісу бекенд hello за допомогою обʼєкта Deployment.
  • Використовувати обʼєкт Service для надсилання трафіку до кількох реплік мікросервісу бекенд.
  • Створити та запустити мікросервіс фронтенд nginx, також використовуючи обʼєкт Deployment.
  • Налаштувати мікросервіс фронтенд для надсилання трафіку до мікросервісу бекенд.
  • Використовувати обʼєкт Service типу LoadBalancer для експонування мікросервісу фронтенд назовні кластера.

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

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

Для перевірки версії введіть kubectl version.

Це завдання використовує Service з зовнішніми балансувальниками навантаження, які вимагають підтримуваного середовища. Якщо ваше середовище не підтримує це, ви можете використовувати Service типу NodePort.

Створення бекенд за допомогою Deployment

Бекенд — це простий мікросервіс для привітань. Ось конфігураційний файл для розгортання:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  selector:
    matchLabels:
      app: hello
      tier: backend
      track: stable
  replicas: 3
  template:
    metadata:
      labels:
        app: hello
        tier: backend
        track: stable
    spec:
      containers:
        - name: hello
          image: "gcr.io/google-samples/hello-go-gke:1.0"
          ports:
            - name: http
              containerPort: 80
...

Створіть Deployment для бекенду:

kubectl apply -f https://k8s.io/examples/service/access/backend-deployment.yaml

Перегляньте інформацію про Deployment:

kubectl describe deployment backend

Вивід буде схожий на цей:

Name:                           backend
Namespace:                      default
CreationTimestamp:              Mon, 24 Oct 2016 14:21:02 -0700
Labels:                         app=hello
                                tier=backend
                                track=stable
Annotations:                    deployment.kubernetes.io/revision=1
Selector:                       app=hello,tier=backend,track=stable
Replicas:                       3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:                   RollingUpdate
MinReadySeconds:                0
RollingUpdateStrategy:          1 max unavailable, 1 max surge
Pod Template:
  Labels:       app=hello
                tier=backend
                track=stable
  Containers:
   hello:
    Image:              "gcr.io/google-samples/hello-go-gke:1.0"
    Port:               80/TCP
    Environment:        <none>
    Mounts:             <none>
  Volumes:              <none>
Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable
OldReplicaSets:                 <none>
NewReplicaSet:                  hello-3621623197 (3/3 replicas created)
Events:
...

Створення обʼєкта Service hello

Ключовим елементом для надсилання запитів з фронтенду до бекенду є бекенд Service. Service створює постійну IP-адресу та запис DNS, так що мікросервіс бекенд завжди може бути доступним. Service використовує селектори, щоб знайти Podʼи, до яких треба спрямувати трафік.

Спочатку ознайомтеся з конфігураційним файлом Service:

---
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
    tier: backend
  ports:
  - protocol: TCP
    port: 80
    targetPort: http
...

У конфігураційному файлі можна побачити, що Service з назвою hello маршрутизує трафік до Podʼів з мітками app: hello та tier: backend.

Створіть Service для бекенду:

kubectl apply -f https://k8s.io/examples/service/access/backend-service.yaml

На цьому етапі у вас є Deployment backend, що виконує три репліки вашого hello застосунку, та Service, який може маршрутизувати трафік до них. Проте цей Service не є доступним та не може бути доступний за межами кластера.

Створення frontend

Тепер, коли ваш бекенд запущено, ви можете створити frontend, який буде доступним за межами кластера та підключатиметься до backend, проксуючи запити до нього.

Фронтенд надсилає запити до Podʼів backend, використовуючи DNS-імʼя, надане Serviceʼу бекенд. DNS-імʼя — це hello, яке є значенням поля name у конфігураційному файлі examples/service/access/backend-service.yaml.

Podʼи у Deployment фронтенд запускають образ nginx, налаштований для проксіювання запитів до Service бекенда hello. Ось конфігураційний файл nginx:

# The identifier Backend is internal to nginx, and used to name this specific upstream
upstream Backend {
    # hello is the internal DNS name used by the backend Service inside Kubernetes
    server hello;
}

server { listen 80;

location / {
    # The following statement will proxy traffic to the upstream named Backend
    proxy_pass http://Backend;
}

}

Подібно до бекенду, фронтенд має Deployment та Service. Важливою відмінністю між Serviceʼами бекендаа та фронтенда є те, що конфігурація для Service фронтенда має type: LoadBalancer, що означає, що Service використовує балансувальник навантаження, який надається вашим хмарним провайдером, і буде доступним за межами кластеру.

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  selector:
    app: hello
    tier: frontend
  ports:
  - protocol: "TCP"
    port: 80
    targetPort: 80
  type: LoadBalancer
...
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  selector:
    matchLabels:
      app: hello
      tier: frontend
      track: stable
  replicas: 1
  template:
    metadata:
      labels:
        app: hello
        tier: frontend
        track: stable
    spec:
      containers:
        - name: nginx
          image: "gcr.io/google-samples/hello-frontend:1.0"
          lifecycle:
            preStop:
              exec:
                command: ["/usr/sbin/nginx","-s","quit"]
...

Створіть Deployment та Service фронтенд:

kubectl apply -f https://k8s.io/examples/service/access/frontend-deployment.yaml
kubectl apply -f https://k8s.io/examples/service/access/frontend-service.yaml

Вивід підтверджує, що обидва ресурси створено:

deployment.apps/frontend created
service/frontend created

Взаємодія з Service фронтенду

Після створення Service типу LoadBalancer, ви можете використати цю команду, щоб знайти зовнішню IP-адресу:

kubectl get service frontend --watch

Вивід показує конфігурацію Service frontend та спостерігає за змінами. Спочатку зовнішня IP-адреса вказана як <pending>:

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   <pending>     80/TCP   10s

Як тільки зовнішня IP-адреса буде надана, конфігурація оновлюється і включає нову IP-адресу під заголовком EXTERNAL-IP:

NAME       TYPE           CLUSTER-IP      EXTERNAL-IP        PORT(S)  AGE
frontend   LoadBalancer   10.51.252.116   XXX.XXX.XXX.XXX    80/TCP   1m

Цю IP-адресу тепер можна використовувати для взаємодії з Service frontend ззовні кластера.

Надсилання трафіку через фронтенд

Тепер фронтенд та бекенд зʼєднані. Ви можете звернутися до точки доступу використовуючи команду curl з зовнішньою IP-адресою вашого Service фронтенду.

curl http://${EXTERNAL_IP} # замініть це на EXTERNAL-IP, який ви бачили раніше

Вивід показує повідомлення, згенероване бекендом:

{"message":"Hello"}

Очищення

Щоб видалити Serviceʼи, введіть цю команду:

kubectl delete services frontend backend

Щоб видалити Deploymentʼи, ReplicaSet та Podʼи, які запускають бекенд та фронтенд застосунки, введіть цю команду:

kubectl delete deployment frontend backend

Що далі

10.7 - Створення зовнішнього балансувальника навантаження

Ця сторінка показує, як створити зовнішній балансувальник навантаження.

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

Ви також можете використовувати Ingress замість Service. Для отримання додаткової інформації ознайомтеся з документацією Ingress.

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

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

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

Створення Service

Створення Service з маніфесту

Щоб створити зовнішній балансувальник навантаження, додайте до специфікації вашого маніфесту Service наступний рядок :

    type: LoadBalancer

Ваш маніфест може виглядати так:

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example
  ports:
    - port: 8765
      targetPort: 9376
  type: LoadBalancer

Створення Service за допомогою kubectl

Ви можете також створити Service за допомогою команди kubectl expose та її прапорця --type=LoadBalancer:

kubectl expose deployment example --port=8765 --target-port=9376 \
        --name=example-service --type=LoadBalancer

Ця команда створює новий Service, використовуючи ті ж селектори, що й вказаний ресурс (у випадку наведеного прикладу — Deployment під назвою example).

Для отримання додаткової інформації, включаючи необовʼязкові прапорці, зверніться до довідника команди kubectl expose.

Пошук вашої IP-адреси

Ви можете знайти IP-адресу, створену для вашого Service, отримавши інформацію про Service через kubectl:

kubectl describe services example-service

що повинно дати результат, схожий на цей:

Name:                     example-service
Namespace:                default
Labels:                   app=example
Annotations:              <none>
Selector:                 app=example
Type:                     LoadBalancer
IP Families:              <none>
IP:                       10.3.22.96
IPs:                      10.3.22.96
LoadBalancer Ingress:     192.0.2.89
Port:                     <unset>  8765/TCP
TargetPort:               9376/TCP
NodePort:                 <unset>  30593/TCP
Endpoints:                172.17.0.3:9376
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

IP-адреса балансувальника навантаження вказана поруч з LoadBalancer Ingress.

Збереження вихідної IP-адреси клієнта

Типово, вихідна IP-адреса, яку бачить цільовий контейнер, не є оригінальною вихідною IP-адресою клієнта. Щоб увімкнути збереження IP-адреси клієнта, можна налаштувати наступні поля в .spec Service:

  • .spec.externalTrafficPolicy — вказує, чи бажає цей Service маршрутизувати зовнішній трафік до вузлів локально або по всьому кластеру. Є два доступні варіанти: Cluster (стандартно) і Local. Cluster приховує вихідну IP-адресу клієнта та може спричинити другий перехід на інший вузол, але має гарне загальне розподілення навантаження. Local зберігає вихідну IP-адресу клієнта та уникає другого переходу для сервісів типу LoadBalancer та NodePort, але є ризик потенційно нерівномірного розподілення трафіку.
  • .spec.healthCheckNodePort — вказує порт для перевірки стану вузлів (числовий номер порту) для Service. Якщо ви не вкажете healthCheckNodePort, контролер Service виділить порт з діапазону NodePort вашого кластера. Ви можете налаштувати цей діапазон, встановивши параметр командного рядка API-сервера --service-node-port-range. Service використовуватиме значення healthCheckNodePort, якщо ви його вкажете, за умови, що type Service встановлено як LoadBalancer і externalTrafficPolicy встановлено як Local.

Встановлення externalTrafficPolicy на Local у маніфесті Service активує цю функцію. Наприклад:

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example
  ports:
    - port: 8765
      targetPort: 9376
  externalTrafficPolicy: Local
  type: LoadBalancer

Застереження та обмеження при збереженні вихідних IP-адрес

Сервіси балансування навантаження деяких хмарних провайдерів не дозволяють налаштувати різну вагу для кожної цілі.

Коли кожна ціль має однакову вагу для надсилання трафіку на вузли, зовнішній трафік нерівномірно розподіляється між різними Podʼами. Зовнішній балансувальник навантаження не знає кількість Podʼів на кожному вузлі, які використовуються як ціль.

Якщо NumServicePods << NumNodes або NumServicePods >> NumNodes, спостерігається майже рівномірний розподіл, навіть без коефіцієнтів.

Внутрішній трафік між Podʼами повинен поводитися подібно до Service типу ClusterIP, з рівною ймовірністю для всіх Podʼів.

Балансувальники для збору сміття

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.17 [stable]

У звичайному випадку, відповідні ресурси балансувальника навантаження у хмарного провайдера повинні бути видалені незабаром після видалення Service типу LoadBalancer. Але відомо, що є різні крайні випадки, коли хмарні ресурси залишаються після видалення асоційованого Service. Для запобігання цьому було введено захист за допомогою завершувачів для Service LoadBalancers. Використовуючи завершувачі, ресурс Service ніколи не буде видалено, поки відповідні ресурси балансувальника навантаження також не будуть видалені.

Зокрема, якщо Service має type LoadBalancer, контролер Service додасть завершувач з назвою service.kubernetes.io/load-balancer-cleanup. Завершувач буде видалений тільки після видалення ресурсу балансувальника навантаження. Це запобігає залишенню ресурсів балансувальника навантаження навіть у крайніх випадках, таких як падіння контролера сервісу.

Постачальники зовнішніх балансувальників навантаження

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

Коли type Service встановлено як LoadBalancer, Kubernetes забезпечує функціональність, еквівалентну type ClusterIP для Podʼів у кластері, і розширює її, програмуючи (зовнішній для Kubernetes) балансувальник навантаження з записами для вузлів, що є місцем розташування відповідних Podʼів Kubernetes. Панель управління Kubernetes автоматизує створення зовнішнього балансувальника навантаження, перевірки стану (якщо необхідно), і правила фільтрації пакетів (якщо необхідно). Після того як хмарний провайдер виділить IP-адресу для балансувальника навантаження, панель управління знаходить цю зовнішню IP-адресу та вносить її в обʼєкт Service.

Що далі

10.8 - Отримання переліку всіх образів контейнерів, що працюють у кластері

Ця сторінка показує, як використовувати kubectl для отримання переліку всіх образів контейнерів для Podʼів, що працюють у кластері.

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

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

Для перевірки версії введіть kubectl version.

У цьому завданні ви використовуватимете kubectl для отримання всіх Podʼів, що працюють у кластері, і форматування виводу для отримання списку контейнерів для кожного з них.

Перелік всіх образів контейнерів у всіх просторах імен

  • Отримайте всі Podʼи у всіх просторах імен за допомогою kubectl get pods --all-namespaces.
  • Форматуйте вивід для включення лише списку імен образів контейнерів, використовуючи -o jsonpath={.items[*].spec['initContainers', 'containers'][*].image}. Це рекурсивно розбирає поле image з отриманого JSON.
  • Форматуйте вивід за допомогою стандартних інструментів: tr, sort, uniq.
    • Використовуйте tr для заміни пробілів на нові рядки.
    • Використовуйте sort для сортування результатів.
    • Використовуйте uniq для агрегування кількості образів.
kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec['initContainers', 'containers'][*].image}" |\
tr -s '[[:space:]]' '\n' |\
sort |\
uniq -c

Jsonpath інтерпретується наступним чином:

  • .items[*]: для кожного отриманого значення.
  • .spec: отримати spec.
  • ['initContainers', 'containers'][*]: для кожного контейнера.
  • .image: отримати образ.

Отримання переліку образів контейнерів в розрізі Podʼів

Форматування може бути додатково налаштоване за допомогою операції range для ітерації по елементах індивідуально.

kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.image}{", "}{end}{end}' |\
sort

Отримання переліку образів контейнерів за мітками Podʼів

Щоб опрацьовувати лише Podʼи, які відповідають конкретній мітці, використовуйте прапорець -l. Наступне відповідає збігам лише для Podʼів з мітками, що відповідають app=nginx.

kubectl get pods --all-namespaces -o jsonpath="{.items[*].spec.containers[*].image}" -l app=nginx

Отримання переліку образів контейнерів в розрізі просторів імен Podʼів

Щоб опрацьовувати лише Podʼи в конкретному просторі імен, використовуйте прапорець namespace. Наступне відповідає збігам лише для Podʼів у просторі імен kube-system.

kubectl get pods --namespace kube-system -o jsonpath="{.items[*].spec.containers[*].image}"

Отримання переліку образів контейнерів з використанням go-template замість jsonpath

Як альтернативу jsonpath, Kubectl підтримує використання go-templates для форматування виходу:

kubectl get pods --all-namespaces -o go-template --template="{{range .items}}{{range .spec.containers}}{{.image}} {{end}}{{end}}"

Що далі

Довідники

10.9 - Налаштування Ingress у Minikube з використанням NGINX Ingress Controller

Ingress — це API-обʼєкт, який визначає правила, що дозволяють зовнішній доступ до Serviceʼів у кластері. Ingress-контролер виконує правила, встановлені в Ingress.

Ця сторінка показує, як налаштувати простий Ingress, який маршрутизує запити до Service 'web' або 'web2' залежно від HTTP URI.

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

Це завдання передбачає, що ви використовуєте minikube для запуску локального Kubernetes кластера. Відвідайте сторінкуВстановлення інструментів, щоб дізнатися, як встановити minikube.

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.19. Для перевірки версії введіть kubectl version. Якщо ви використовуєте старішу версію Kubernetes, використовуйте документацію для цієї версії.

Створіть кластер minikube

Якщо ви ще не налаштували кластер локально, виконайте minikube start, щоб створити кластер.

Увімкніть Ingress-контролер

  1. Для увімкнення NGINX Ingress Controller, виконайте наступну команду:

    minikube addons enable ingress
    
  2. Переконайтеся, що NGINX Ingress Controller працює:

    kubectl get pods -n ingress-nginx
    

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

    NAME                                        READY   STATUS      RESTARTS    AGE
    ingress-nginx-admission-create-g9g49        0/1     Completed   0          11m
    ingress-nginx-admission-patch-rqp78         0/1     Completed   1          11m
    ingress-nginx-controller-59b45fb494-26npt   1/1     Running     0          11m
    

Розгорніть застосунок hello world

  1. Створіть Deployment за допомогою наступної команди:

    kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
    

    Вивід має бути:

    deployment.apps/web created
    

    Переконайтеся, що Deployment перебуває у стані Ready:

    kubectl get deployment web 
    

    Вивід має бути подібний до:

    NAME   READY   UP-TO-DATE   AVAILABLE   AGE
    web    1/1     1            1           53s
    
  2. Опублікуйте Deployment:

    kubectl expose deployment web --type=NodePort --port=8080
    

    Вивід має бути:

    service/web exposed
    
  3. Переконайтеся, що Service створено і він доступний на порті вузла:

    kubectl get service web
    

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

    NAME      TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
    web       NodePort   10.104.133.249   <none>        8080:31637/TCP   12m
    
  4. Відвідайте Service через NodePort, використовуючи команду minikube service. Дотримуйтесь інструкцій для вашої платформи:

    minikube service web --url
    

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

    http://172.17.0.15:31637
    

    Виконайте запит до URL, отриманого у попередньому кроці:

    curl http://172.17.0.15:31637 
    

    # Команду потрібно виконати в окремому терміналі.
    minikube service web --url 
    

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

    http://127.0.0.1:62445
    ! Оскільки ви використовуєте драйвер Docker на darwin, термінал має бути відкритий для його запуску.
    

    В іншому терміналі виконайте запит до URL, отриманого у попередньому кроці:

    curl http://127.0.0.1:62445 
    

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

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    

    Тепер ви можете отримати доступ до застосунку прикладу через IP-адресу Minikube і NodePort. Наступний крок дозволяє отримати доступ до застосунку, використовуючи ресурс Ingress.

Створіть Ingress

Наступний маніфест визначає Ingress, який надсилає трафік до вашого Service через hello-world.example.

  1. Створіть файл example-ingress.yaml з наступним вмістом:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: example-ingress
    spec:
      ingressClassName: nginx
      rules:
        - host: hello-world.example
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: web
                    port:
                      number: 8080
  2. Створіть обʼєкт Ingress, виконавши наступну команду:

    kubectl apply -f https://k8s.io/examples/service/networking/example-ingress.yaml
    

    Вивід має бути:

    ingress.networking.k8s.io/example-ingress created
    
  3. Переконайтеся, що IP-адреса встановлена:

    kubectl get ingress
    

    Ви повинні побачити IPv4-адресу у стовпці ADDRESS; наприклад:

    NAME              CLASS   HOSTS                 ADDRESS        PORTS   AGE
    example-ingress   nginx   hello-world.example   172.17.0.15    80      38s
    
  4. Перевірте, що Ingress-контролер спрямовує трафік, дотримуючись інструкцій для вашої платформи:

    curl --resolve "hello-world.example:80:$( minikube ip )" -i http://hello-world.example
    

    minikube tunnel
    

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

    Tunnel successfully started
    
    NOTE: Please do not close this terminal as this process must stay alive for the tunnel to be accessible ...
    
    The service/ingress example-ingress requires privileged ports to be exposed: [80 443]
    sudo permission will be asked for it.
    Starting tunnel for service example-ingress.
    

    В іншому терміналі виконайте наступну команду:

    curl --resolve "hello-world.example:80:127.0.0.1" -i http://hello-world.example
    

    Ви повинні побачити:
    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    
  5. За бажанням ви також можете відвідати hello-world.example зі свого оглядача.

    Додайте рядок у кінець файлу /etc/hosts на вашому компʼютері (потрібні права адміністратора):

    Знайдіть зовнішню IP-адресу, як вказано у звіті minikube

      minikube ip 
    

      172.17.0.15 hello-world.example
    

    127.0.0.1 hello-world.example
    

    Після цього ваш вебоглядач надсилатиме запити на URL-адреси hello-world.example до Minikube.

Створіть другий Deployment

  1. Створіть інший Deployment, виконавши наступну команду:

    kubectl create deployment web2 --image=gcr.io/google-samples/hello-app:2.0
    

    Вивід має бути:

    deployment.apps/web2 created
    

    Переконайтеся, що Deployment перебуває у стані Ready:

    kubectl get deployment web2 
    

    Вихід має бути подібний до:

    NAME   READY   UP-TO-DATE   AVAILABLE   AGE
    web2   1/1     1            1           16s
    
  2. Опублікуйте другий Deployment:

    kubectl expose deployment web2 --port=8080 --type=NodePort
    

    Вивід має бути:

    service/web2 exposed
    

Редагування поточного Ingress

  1. Відредагуйте поточний маніфест example-ingress.yaml та додайте наступні рядки в кінці:

    - path: /v2
      pathType: Prefix
      backend:
        service:
          name: web2
          port:
            number: 8080
    
  2. Застосуйте зміни:

    kubectl apply -f example-ingress.yaml
    

    Ви маєте побачити:

    ingress.networking/example-ingress configured
    

Перевірка вашого Ingress

  1. Отримайте доступ до першої версії застосунку Hello World.

    curl --resolve "hello-world.example:80:$( minikube ip )" -i http://hello-world.example
    

    minikube tunnel
    

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

    Tunnel successfully started
    
    NOTE: Please do not close this terminal as this process must stay alive for the tunnel to be accessible ...
    
    The service/ingress example-ingress requires privileged ports to be exposed: [80 443]
    sudo permission will be asked for it.
    Starting tunnel for service example-ingress.
    

    В іншому терміналі виконайте наступну команду:

    curl --resolve "hello-world.example:80:127.0.0.1" -i http://hello-world.example
    

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

    Hello, world!
    Version: 1.0.0
    Hostname: web-55b8c6998d-8k564
    
  2. Отримайте доступ до другої версії застосунку Hello World.

    curl --resolve "hello-world.example:80:$( minikube ip )" -i http://hello-world.example/v2
    

    minikube tunnel
    

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

    Tunnel successfully started
    
    NOTE: Please do not close this terminal as this process must stay alive for the tunnel to be accessible ...
    
    The service/ingress example-ingress requires privileged ports to be exposed: [80 443]
    sudo permission will be asked for it.
    Starting tunnel for service example-ingress.
    

    В іншому терміналі виконайте наступну команду:

    curl --resolve "hello-world.example:80:127.0.0.1" -i http://hello-world.example/v2
    

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

    Hello, world!
    Version: 2.0.0
    Hostname: web2-75cd47646f-t8cjk
    

Що далі

10.10 - Спілкування між контейнерами в одному Podʼі за допомогою спільного тому

Ця сторінка показує, як використовувати Том для спілкування між двома контейнерами, що працюють в одному Podʼі. Також дивіться, як дозволити процесам спілкуватися між контейнерами через спільний простір процесів.

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

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

Для перевірки версії введіть kubectl version.

Створення Pod, що запускає два контейнери

У цьому завданні ви створите Pod, який запускає два контейнери. Ці два контейнери спільно використовують Том, який вони можуть використовувати для спілкування. Ось конфігураційний файл для Podʼа:

apiVersion: v1
kind: Pod
metadata:
  name: two-containers
spec:

  restartPolicy: Never

  volumes:
  - name: shared-data
    emptyDir: {}

  containers:

  - name: nginx-container
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html

  - name: debian-container
    image: debian
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data
    command: ["/bin/sh"]
    args: ["-c", "echo Hello from the debian container > /pod-data/index.html"]

У конфігураційному файлі видно, що Pod має Том з назвою shared-data.

Перший контейнер, зазначений у конфігураційному файлі, запускає сервер nginx. Шлях монтування для спільного тому — /usr/share/nginx/html. Другий контейнер базується на образі debian і має шлях монтування /pod-data. Другий контейнер виконує наступну команду і потім завершується.

echo Hello from the debian container > /pod-data/index.html

Зверніть увагу, що другий контейнер записує файл index.html в кореневу теку сервера nginx.

Створіть Pod і два контейнери:

kubectl apply -f https://k8s.io/examples/pods/two-container-pod.yaml

Перегляньте інформацію про Pod та контейнери:

kubectl get pod two-containers --output=yaml

Ось частина вихідних даних:

apiVersion: v1
kind: Pod
metadata:
    ...
    name: two-containers
    namespace: default
    ...
spec:
    ...
    containerStatuses:

    - containerID: docker://c1d8abd1 ...
    image: debian
    ...
    lastState:
        terminated:
        ...
    name: debian-container
    ...

    - containerID: docker://96c1ff2c5bb ...
    image: nginx
    ...
    name: nginx-container
    ...
    state:
        running:
    ...

Ви бачите, що контейнер debian завершив роботу, а контейнер nginx все ще працює.

Отримайте доступ до shell контейнера nginx:

kubectl exec -it two-containers -c nginx-container -- /bin/bash

У вашому shell перевірте, що nginx працює:

root@two-containers:/# apt-get update
root@two-containers:/# apt-get install curl procps
root@two-containers:/# ps aux

Вихідні дані схожі на це:

USER       PID  ...  STAT START   TIME COMMAND
root         1  ...  Ss   21:12   0:00 nginx: master process nginx -g daemon off;

Згадайте, що контейнер debian створив файл index.html в кореневій теці nginx. Використовуйте curl, щоб надіслати GET запит на сервер nginx:

root@two-containers:/# curl localhost

Вихідні дані показують, що nginx обслуговує вебсторінку, написану контейнером debian:

Hello from the debian container

Обговорення

Основна причина, через яку Podʼи можуть мати кілька контейнерів, полягає у підтримці допоміжних застосунків, що допомагають основному застосунку. Типові приклади допоміжних застосунків включають інструменти для завантаження, надсилання даних та проксі. Допоміжні та основні застосунки часто потребують спілкування між собою. Зазвичай це робиться через спільну файлову систему, як показано в цій вправі, або через інтерфейс локальної мережі, localhost. Прикладом цього шаблону є вебсервер разом із допоміжним застосунком, яка перевіряє репозиторій Git на наявність нових оновлень.

Том у цьому завданні надає спосіб спілкування контейнерів під час життя Pod. Якщо Pod видалено та створено знову, всі дані, збережені в спільному томі, будуть втрачені.

Що далі

10.11 - Налаштування DNS для кластера

Kubernetes пропонує надбудову DNS для кластера, яку типово увімкнено у більшості підтримуваних середовищ. У Kubernetes версії 1.11 і пізніших версіях рекомендовано використовувати CoreDNS, який стандартно встановлюється з kubeadm.

Для отримання додаткової інформації про налаштування CoreDNS для кластера Kubernetes дивіться Налаштування DNS-сервісу. Приклад, який демонструє, як використовувати Kubernetes DNS з kube-dns, дивіться у прикладі втулка Kubernetes DNS.

10.12 - Доступ до Service, що працюють в кластерах

Ця сторінка показує, як підʼєднатись до Serviceʼів, що працюють у кластері Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Доступ до Serviceʼів, що працюють у кластері

У Kubernetes Вузли, Podʼи та Serviceʼи мають власні IP-адреси. У багатьох випадках IP-адреси вузлів, Podʼів та деякі IP-адреси Service у кластері не можуть бути маршрутизовані, тому вони не будуть досяжними з машини за межами кластера, такої як ваш настільний компʼютер.

Способи приєднання

Ви маєте кілька варіантів приєднання до вузлів, Podʼів та Serviceʼів ззовні кластера:

  • Доступ до Servicʼів через публічні IP-адреси.
    • Використовуйте Service з типом NodePort або LoadBalancer, щоб зробити Service доступним ззовні кластера. Дивіться документацію Service та kubectl expose.
    • Залежно від середовища вашого кластера, це може тільки відкрити Service для вашої корпоративної мережі, або ж зробити його доступним в інтернеті. Подумайте, чи є Service безпечним для відкриття. Чи має він власну автентифікацію?
    • Розміщуйте Podʼи за Serviceʼами. Щоб отримати доступ до одного конкретного Pod з набору реплік, наприклад, для налагодження, додайте унікальну мітку до Podʼа і створіть новий Service, який обирає цю мітку.
    • У більшості випадків розробнику застосунків не потрібно безпосередньо звертатися до вузлів за їх IP-адресами.
  • Доступ до Serviceʼів, вузлів або Podʼів за допомогою проксі-дієслова (Proxy Verb).
    • Виконує автентифікацію та авторизацію на api-сервері перед доступом до віддаленого Service. Використовуйте це, якщо Service недостатньо безпечні для відкриття в інтернеті, або для доступу до портів на IP-адресі вузла, або для налагодження.
    • Проксі можуть викликати проблеми для деяких вебзастосунків.
    • Працює тільки для HTTP/HTTPS.
    • Описано тут.
  • Доступ з вузла або Podʼа в кластері.
    • Запустіть Pod та отримайте доступ до shell у ньому за допомогою kubectl exec. Підʼєднуйтесь до інших вузлів, Podʼів та Serviceʼів з цього shell.
    • Деякі кластери можуть дозволити вам підʼєднатись по SSH до вузла в кластері. Звідти ви можете отримати доступ до Serviceʼів кластера. Це нестандартний метод і працюватиме на одних кластерах, але на інших — ні. Оглядачі та інші інструменти можуть бути встановлені або не встановлені. DNS кластера може не працювати.

Виявлення вбудованих Service

Зазвичай у кластері є кілька Serviceʼів, які запускаються у просторі імен kube-system. Отримайте їх список за допомогою команди kubectl cluster-info:

kubectl cluster-info

Вихідні дані подібні до цього:

Kubernetes master is running at https://192.0.2.1
elasticsearch-logging is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy
kibana-logging is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/kibana-logging/proxy
kube-dns is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/kube-dns/proxy
grafana is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
heapster is running at https://192.0.2.1/api/v1/namespaces/kube-system/services/monitoring-heapster/proxy

Це показує URL з проксі-дієсловом для доступу до кожного Service. Наприклад, у цьому кластері ввімкнено кластерне логування (з використанням Elasticsearch), до якого можна звернутися за адресою https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/ за умови наявності відповідних облікових даних або через проксі kubectl за адресою: http://localhost:8080/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/.

Ручне створення URL-адрес проксі api-сервера

Як згадувалося вище, ви використовуєте команду kubectl cluster-info, щоб отримати URL проксі для Service. Щоб створити URL-адреси проксі, що включають точки доступу Service, суфікси та параметри, додайте до URL проксі Serviceʼу: http://kubernetes_master_address/api/v1/namespaces/namespace_name/services/[https:]service_name[:port_name]/proxy

Якщо ви не задали імʼя для вашого порту, вам не потрібно вказувати port_name в URL. Ви також можете використовувати номер порту замість port_name для іменованих та неіменованих портів.

Стандартно апі-сервер проксює до вашого Serviceʼу за допомогою HTTP. Щоб використовувати HTTPS, додайте префікс до імені Serviceʼу https:: http://<kubernetes_master_address>/api/v1/namespaces/<namespace_name>/services/<service_name>/proxy.

Підтримувані формати для сегмента <service_name> URL-адреси:

  • <service_name> — проксює до стандартного порту або до неіменованого порту за допомогою http
  • <service_name>:<port_name> — проксює до вказаного імені порту або номера порту за допомогою http
  • https:<service_name>: — проксює до стандартного порту або до неіменованого порту за допомогою https (зверніть увагу на кінцеву двокрапку)
  • https:<service_name>:<port_name> — проксює до вказаного імені порту або номера порту за допомогою https
Приклади
  • Для доступу до точки доступу сервісу Elasticsearch _search?q=user:kimchy, використовуйте:

    http://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_search?q=user:kimchy
    
  • Для доступу до інформації про стан кластера Elasticsearch _cluster/health?pretty=true, використовуйте:

    https://192.0.2.1/api/v1/namespaces/kube-system/services/elasticsearch-logging/proxy/_cluster/health?pretty=true
    

    Інформація про стан подібна до цієї:

    {
      "cluster_name" : "kubernetes_logging",
      "status" : "yellow",
      "timed_out" : false,
      "number_of_nodes" : 1,
      "number_of_data_nodes" : 1,
      "active_primary_shards" : 5,
      "active_shards" : 5,
      "relocating_shards" : 0,
      "initializing_shards" : 0,
      "unassigned_shards" : 5
    }
    
  • Для доступу до https інформації про стан кластера Elasticsearch _cluster/health?pretty=true, використовуйте:

    https://192.0.2.1/api/v1/namespaces/kube-system/services/https:elasticsearch-logging:/proxy/_cluster/health?pretty=true
    

Використання вебоглядачів для доступу до Serviceʼів, що працюють у кластері

Ви можете вставити URL-адресу проксі api-сервера у адресний рядок оглядача. Однак:

  • Вебоглядачі зазвичай не можуть передавати токени, тому вам може доведеться використовувати базову автентифікацію (пароль). Api-сервер можна налаштувати для роботи з базовою автентифікацією, але ваш кластер може бути не налаштований для цього.
  • Деякі вебзастосунки можуть не працювати, особливо ті, що використовують клієнтський javascript для створення URL-адрес, не знаючи про префікс шляху проксі.

11 - Розширення Kubernetes

Розуміння розширених способів адаптації кластера Kubernetes до потреб вашого робочого середовища.

11.1 - Використання власних ресурсів

11.1.1 - Розширення API Kubernetes за допомогою CustomResourceDefinitions

Ця сторінка показує, як встановити власний ресурс у API Kubernetes, створивши CustomResourceDefinition.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.16. Для перевірки версії введіть kubectl version. Якщо ви використовуєте старішу версію Kubernetes, яка все ще підтримується, використовуйте документацію для цієї версії, щоб отримати поради, які є відповідними для вашого кластера.

Створення CustomResourceDefinition

При створенні нового CustomResourceDefinition (CRD) сервер API Kubernetes створює новий RESTful ресурсний шлях для кожної версії, яку ви вказуєте. Власний ресурс, створений з обʼєкта CRD, може бути або просторово обмеженим за іменем, або обмеженим на рівні кластера, як вказано в полі spec.scope CRD. Як і з наявними вбудованими обʼєктами, видалення простору імен видаляє всі власні обʼєкти в цьому просторі імен. CustomResourceDefinition самі за собою не мають простору імен і доступні для всіх просторів імен.

Наприклад, якщо ви збережете наступне визначення CustomResourceDefinition у resourcedefinition.yaml:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # назва повинна відповідати полям специфікації нижче, і мати формат: <plural>.<group>
  name: crontabs.stable.example.com
spec:
  # назва групи, яка буде використана для REST API: /apis/<group>/<version>
  group: stable.example.com
  # список версій, підтримуваних цим визначенням власних ресурсів
  versions:
    - name: v1
      # Кожну версію можна ввімкнути/вимкнути за допомогою прапорця Served.
      served: true
      # Одна і лише одна версія повинна бути позначена як версія зберігання.
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  # або просторово обмежений за іменем, або на рівні кластера
  scope: Namespaced
  names:
    # назва множини, яка буде використана в URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # назва однини, яка буде використана як псевдонім у CLI та для показу
    singular: crontab
    # вид - зазвичай це тип у форматі CamelCased однини. Ваші маніфести ресурсів використовують це.
    kind: CronTab
    # короткі назви дозволяють мати збіг для скорочених рядків з вашим ресурсом у CLI
    shortNames:
    - ct

та створите його:

kubectl apply -f resourcedefinition.yaml

Тоді буде створено новий просторово обмежений RESTful API шлях за адресою:

/apis/stable.example.com/v1/namespaces/*/crontabs/...

Цю URL-адресу шляху можна буде використовувати для створення та управління власними обʼєктами. kind цих обʼєктів буде CronTab зі специфікації обʼєкта CustomResourceDefinition, який ви створили вище.

Може знадобитися кілька секунд, щоб створити точку доступу. Ви можете спостерігати, що умова Established вашого CustomResourceDefinition стає true або спостерігати інформацію про відкриття сервера API для вашого ресурсу, щоб він зʼявився.

Створення власних обʼєктів

Після створення обʼєкта CustomResourceDefinition ви можете створювати власні обʼєкти. Власні обʼєкти можуть містити власні поля. Ці поля можуть містити довільний JSON. У наступному прикладі поля cronSpec та image встановлені у власний обʼєкт типу CronTab. Тип CronTab походить зі специфікації обʼєкта CustomResourceDefinition, який ви створили вище.

Якщо ви збережете наступний YAML у my-crontab.yaml:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image

і створите його:

kubectl apply -f my-crontab.yaml

Потім ви можете управляти вашими обʼєктами CronTab за допомогою kubectl. Наприклад:

kubectl get crontab

Повинен вивести список, подібний до такого:

NAME                 AGE
my-new-cron-object   6s

Назви ресурсів нечутливі до регістру при використанні kubectl, і ви можете використовувати як однину, так і множину, визначені в CRD, а також будь-які короткі назви.

Ви також можете переглянути дані YAML:

kubectl get ct -o yaml

Ви повинні побачити, що він містить власні поля cronSpec та image з YAML, який ви використовували для його створення:

apiVersion: v1
items:
- apiVersion: stable.example.com/v1
  kind: CronTab
  metadata:
    annotations:
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"stable.example.com/v1","kind":"CronTab","metadata":{"annotations":{},"name":"my-new-cron-object","namespace":"default"},"spec":{"cronSpec":"* * * * */5","image":"my-awesome-cron-image"}}        
    creationTimestamp: "2021-06-20T07:35:27Z"
    generation: 1
    name: my-new-cron-object
    namespace: default
    resourceVersion: "1326"
    uid: 9aab1d66-628e-41bb-a422-57b8b3b1f5a9
  spec:
    cronSpec: '* * * * */5'
    image: my-awesome-cron-image
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

Видалення CustomResourceDefinition

Коли ви видаляєте CustomResourceDefinition, сервер деінсталює RESTful API шлях та видаляє всі власні обʼєкти, збережені в ньому.

kubectl delete -f resourcedefinition.yaml
kubectl get crontabs
Error from server (NotFound): Unable to list {"stable.example.com" "v1" "crontabs"}: the server could not
find the requested resource (get crontabs.stable.example.com)

Якщо ви пізніше створите те саме CustomResourceDefinition, воно буде порожнім з початку.

Визначення структурної схеми

CustomResources зберігають структуровані дані у власних полях (разом з вбудованими полями apiVersion, kind та metadata, які сервер API перевіряє неявно). З валідацією OpenAPI v3.0 можна вказати схему, яка перевіряється під час створення та оновлення. Подивіться нижче для деталей та обмежень такої схеми.

З apiextensions.k8s.io/v1 визначення структурної схеми є обовʼязковим для визначення власних ресурсів. У бета-версії CustomResourceDefinition структурна схема була необовʼязковою.

Структурна схема — це схема валідації OpenAPI v3.0, яка:

  1. вказує непорожній тип (за допомогою type в OpenAPI) для кореня, для кожного вказаного поля вузла обʼєкта (за допомогою properties або additionalProperties в OpenAPI) та для кожного елемента вузла масиву (за допомогою items в OpenAPI), за винятком:
    • вузла з x-kubernetes-int-or-string: true
    • вузла з x-kubernetes-preserve-unknown-fields: true
  2. для кожного поля в обʼєкті та кожного елемента в масиві, які вказані всередині будь-якого з allOf, anyOf, oneOf або not, схема також вказує поле/елемент поза цими логічними виразами (порівняйте приклад 1 та 2).
  3. не встановлює description, type, default, additionalProperties, nullable всередині allOf, anyOf, oneOf або not, за винятком двох шаблонів для x-kubernetes-int-or-string: true (див. нижче).
  4. якщо вказано metadata, то дозволяються обмеження тільки на metadata.name та metadata.generateName.

Неструктурний приклад 1:

allOf:
- properties:
    foo:
      ...

суперечить правилу 2. Наступне було б правильним:

properties:
  foo:
    ...
allOf:
- properties:
    foo:
      ...

Неструктурний приклад 2:

allOf:
- items:
    properties:
      foo:
        ...

суперечить правилу 2. Наступне було б правильним:

items:
  properties:
    foo:
      ...
allOf:
- items:
    properties:
      foo:
        ...

Неструктурний приклад 3:

properties:
  foo:
    pattern: "abc"
  metadata:
    type: object
    properties:
      name:
        type: string
        pattern: "^a"
      finalizers:
        type: array
        items:
          type: string
          pattern: "my-finalizer"
anyOf:
- properties:
    bar:
      type: integer
      minimum: 42
  required: ["bar"]
  description: "foo bar object"

не є структурною схемою через наступні порушення:

  • тип у корені відсутній (правило 1).
  • тип foo відсутній (правило 1).
  • bar всередині anyOf не вказаний зовні (правило 2).
  • тип bar в anyOf (правило 3).
  • опис встановлено в anyOf (правило 3).
  • metadata.finalizers можуть бути не обмежені (правило 4).

Натомість наступна відповідна схема є структурною:

type: object
description: "foo bar object"
properties:
  foo:
    type: string
    pattern: "abc"
  bar:
    type: integer
  metadata:
    type: object
    properties:
      name:
        type: string
        pattern: "^a"
anyOf:
- properties:
    bar:
      minimum: 42
  required: ["bar"]

Порушення правил структурної схеми повідомляються в умові NonStructural у CustomResourceDefinition.

Обрізка полів

CustomResourceDefinitions зберігають перевірені дані ресурсів у сховищі постійного зберігання кластера, etcd.Так само як і з вбудованими ресурсами Kubernetes, такими як ConfigMap, якщо ви вказуєте поле, яке сервер API не впізнає, невідоме поле обрізається (видаляється) перед зберіганням.

CRD, перетворені з apiextensions.k8s.io/v1beta1 на apiextensions.k8s.io/v1, можуть бути позбавлені структурних схем, і spec.preserveUnknownFields може бути встановлено в true.

Для застарілих обʼєктів власного визначення ресурсів, створених як apiextensions.k8s.io/v1beta1 з spec.preserveUnknownFields встановленим в true, також вірно наступне:

  • Обрізка не ввімкнена.
  • Ви можете зберігати довільні дані.

Для сумісності з apiextensions.k8s.io/v1 оновіть визначення своїх власних ресурсів:

  1. Використовуйте структурну схему OpenAPI.
  2. Встановіть spec.preserveUnknownFields в false.

Якщо ви збережете наступний YAML у my-crontab.yaml:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image
  someRandomField: 42

і створите його:

kubectl create --validate=false -f my-crontab.yaml -o yaml

Ваш вивід буде подібним до:

apiVersion: stable.example.com/v1
kind: CronTab
metadata:
  creationTimestamp: 2017-05-31T12:56:35Z
  generation: 1
  name: my-new-cron-object
  namespace: default
  resourceVersion: "285"
  uid: 9423255b-4600-11e7-af6a-28d2447dc82b
spec:
  cronSpec: '* * * * */5'
  image: my-awesome-cron-image

Зверніть увагу, що поле someRandomField було обрізано.

У цьому прикладі вимкнено перевірку на клієнтському боці, щоб показати поведінку сервера API, додавши параметр командного рядка --validate=false. Оскільки схеми валідації OpenAPI також публікуються для клієнтів, kubectl також перевіряє невідомі поля та відхиляє ці обʼєкти задовго до їх надсилання на сервер API.

Контроль обрізки полів

Типово усі невизначені поля власного ресурсу, у всіх версіях, обрізаються. Однак можна відмовитися від цього для певних піддерев полів, додавши x-kubernetes-preserve-unknown-fields: true у структурну схему валідації OpenAPI v3.

Наприклад:

type: object
properties:
  json:
    x-kubernetes-preserve-unknown-fields: true

Поле json може зберігати будь-яке значення JSON, без обрізки.

Ви також можете частково вказати допустимий JSON; наприклад:

type: object
properties:
  json:
    x-kubernetes-preserve-unknown-fields: true
    type: object
    description: this is arbitrary JSON

З цим дозволяються тільки значення типу object.

Обрізка знову ввімкнена для кожного вказаного властивості (або additionalProperties):

type: object
properties:
  json:
    x-kubernetes-preserve-unknown-fields: true
    type: object
    properties:
      spec:
        type: object
        properties:
          foo:
            type: string
          bar:
            type: string

З цим значення:

json:
  spec:
    foo: abc
    bar: def
    something: x
  status:
    something: x

обрізається до:

json:
  spec:
    foo: abc
    bar: def
  status:
    something: x

Це означає, що поле something у вказаному обʼєкті spec обрізається, але все поза цим обʼєктом — ні.

IntOrString

Вузли в схемі з x-kubernetes-int-or-string: true виключаються з правила 1, таким чином наступна схема є структурною:

type: object
properties:
  foo:
    x-kubernetes-int-or-string: true

Також ці вузли частково виключаються з правила 3 у тому сенсі, що дозволяються наступні два шаблони (саме ці, без варіацій в порядку або додаткових полів):

x-kubernetes-int-or-string: true
anyOf:
  - type: integer
  - type: string
...

та

x-kubernetes-int-or-string: true
allOf:
  - anyOf:
      - type: integer
      - type: string
  - ... # нуль або більше
...

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

У розділі Публікація схеми валідації, x-kubernetes-int-or-string: true розгортається до одного з двох показаних вище шаблонів.

RawExtension

RawExtensions (як у runtime.RawExtension) містять повні обʼєкти Kubernetes, тобто з полями apiVersion і kind.

Можна задати ці вбудовані обʼєкти (як повністю без обмежень, так і частково задані), встановивши x-kubernetes-embedded-resource: true. Наприклад:

type: object
properties:
  foo:
    x-kubernetes-embedded-resource: true
    x-kubernetes-preserve-unknown-fields: true

Тут поле foo містить повний обʼєкт, наприклад:

foo:
  apiVersion: v1
  kind: Pod
  spec:
    ...

Оскільки поруч вказано x-kubernetes-preserve-unknown-fields: true, нічого не обрізається. Використання x-kubernetes-preserve-unknown-fields: true є опціональним.

З x-kubernetes-embedded-resource: true поля apiVersion, kind і metadata неявно задаються та перевіряються на валідність.

Обслуговування декількох версій CRD

Дивіться версіювання визначення власних ресурсів для отримання додаткової інформації про обслуговування декількох версій вашого CustomResourceDefinition і міграцію ваших обʼєктів з однієї версії на іншу.

Розширені теми

Завершувачі

Завершувачі дозволяють контролерам реалізовувати асинхронні гаки перед видаленням. Власні обʼєкти підтримують завершувачі, аналогічні вбудованим обʼєктам.

Ви можете додати завершувач до власного обʼєкта ось так:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  finalizers:
  - stable.example.com/finalizer

Ідентифікатори власних завершувачів складаються з імені домену, косої риски та назви завершувача. Будь-який контролер може додати завершувач до списку завершувачів будь-якого обʼєкта.

Перший запит на видалення обʼєкта з завершувачами встановлює значення для поля metadata.deletionTimestamp, але не видаляє його. Після встановлення цього значення можна лише видаляти записи у списку finalizers. Поки залишаються будь-які завершувачі, неможливо примусово видалити обʼєкт.

Коли поле metadata.deletionTimestamp встановлене, контролери, які стежать за обʼєктом, виконують будь-які завершувачі, які вони обробляють, і видаляють завершувач зі списку після завершення. Відповідальність за видалення свого завершувача зі списку лежить на кожному контролері.

Значення поля metadata.deletionGracePeriodSeconds контролює інтервал між оновленнями опитування.

Після того, як список завершувачів стане порожнім, тобто всі завершувачі будуть виконані, ресурс буде видалено Kubernetes.

Валідація

Власні ресурси перевіряються за допомогою схем OpenAPI v3, за допомогою x-kubernetes-validations, коли функція Правил валідації ввімкнена, і ви можете додати додаткову валідацію за допомогою вебхуків допуску.

Крім того, до схеми застосовуються такі обмеження:

  • Ці поля не можна встановлювати:

    • definitions,
    • dependencies,
    • deprecated,
    • discriminator,
    • id,
    • patternProperties,
    • readOnly,
    • writeOnly,
    • xml,
    • $ref.
  • Поле uniqueItems не можна встановлювати в true.

  • Поле additionalProperties не можна встановлювати в false.

  • Поле additionalProperties є взаємозаперечним із properties.

Розширення x-kubernetes-validations можна використовувати для перевірки власних ресурсів за допомогою виразів загальної мови виразів (CEL), коли функція правил валідації увімкнена, а схема CustomResourceDefinition є структурною схемою.

Зверніться до розділу структурних схем для інших обмежень та функцій CustomResourceDefinition.

Схема визначається у CustomResourceDefinition. У наведеному нижче прикладі CustomResourceDefinition застосовує такі перевірки до власного обʼєкта:

  • spec.cronSpec повинен бути рядком і відповідати формі, описаній регулярним виразом.
  • spec.replicas повинен бути цілим числом і мати мінімальне значення 1 та максимальне значення 10.

Збережіть CustomResourceDefinition у файл resourcedefinition.yaml:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        # openAPIV3Schema - це схема для перевірки власних обʼєктів.
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                  pattern: '^(\d+|\*)(/\д+)?(\s+(\d+|\*)(/\д+)?){4}$'
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

і створіть його:

kubectl apply -f resourcedefinition.yaml

Запит на створення власного обʼєкта типу CronTab буде відхилено, якщо поля містять недійсні значення. У наведеному нижче прикладі власний обʼєкт містить поля з недійсними значеннями:

  • spec.cronSpec не відповідає регулярному виразу.
  • spec.replicas більше 10.

Якщо ви збережете наступний YAML у файл my-crontab.yaml:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * *"
  image: my-awesome-cron-image
  replicas: 15

і спробуєте створити його:

kubectl apply -f my-crontab.yaml

то отримаєте помилку:

The CronTab "my-new-cron-object" is invalid: []: Invalid value: map[string]interface {}{"apiVersion":"stable.example.com/v1", "kind":"CronTab", "metadata":map[string]interface {}{"name":"my-new-cron-object", "namespace":"default", "deletionTimestamp":interface {}(nil), "deletionGracePeriodSeconds":(*int64)(nil), "creationTimestamp":"2017-09-05T05:20:07Z", "uid":"e14d79e7-91f9-11e7-a598-f0761cb232d1", "clusterName":""}, "spec":map[string]interface {}{"cronSpec":"* * * *", "image":"my-awesome-cron-image", "replicas":15}}:
validation failure list:
spec.cronSpec in body should match '^(\d+|\*)(/\д+)?(\с+(\д+|\*)(/\д+)?){4}$'
spec.replicas in body should be less than or equal to 10

Якщо поля містять дійсні значення, запит на створення обʼєкта буде прийнято.

Збережіть наступний YAML у файл my-crontab.yaml:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image
  replicas: 5

І створіть його:

kubectl apply -f my-crontab.yaml
crontab "my-new-cron-object" created

Проковзування валідації

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.30 [beta]

Якщо ви використовуєте версію Kubernetes старше v1.30, вам потрібно явно ввімкнути функціональну можливість CRDValidationRatcheting, щоб використовувати цю поведінку, яка потім застосовується до всіх CustomResourceDefinitions у вашому кластері.

За умови увімкнення функціональної можливості, Kubernetes реалізує проковзування валідації для CustomResourceDefinitions. API сервер готовий прийняти оновлення ресурсів, які є недійсними після оновлення, за умови, що кожна частина ресурсу, яка не пройшла валідацію, не була змінена операцією оновлення. Іншими словами, будь-яка недійсна частина ресурсу, яка залишається недійсною, вже повинна була бути неправильною. Ви не можете використовувати цей механізм для оновлення дійсного ресурсу, щоб він став недійсним.

Ця функція дозволяє авторам CRD впевнено додавати нові перевірки до схеми OpenAPIV3 за певних умов. Користувачі можуть безпечно оновлюватися до нової схеми без зміни версії обʼєкта або порушення робочих процесів.

Хоча більшість перевірок, розміщених у схемі OpenAPIV3 CRD, підтримують обмеження, є кілька винятків. Наступні перевірки схеми OpenAPIV3 не підтримуються обмеженнями у реалізації в Kubernetes 1.31 і якщо порушені, продовжуватимуть видавати помилку як зазвичай:

  • Квантори

    • allOf
    • oneOf
    • anyOf
    • not
    • будь-які перевірки в нащадках одного з цих полів
  • x-kubernetes-validations Для Kubernetes 1.28, правила валідації CRD ігноруються обмеженнями. Починаючи з Alpha 2 у Kubernetes 1.29, x-kubernetes-validations обмежуються лише, якщо вони не посилаються на oldSelf.

    Перехідні правила ніколи не обмежуються: лише помилки, які виникають через правила, які не використовують oldSelf, автоматично обмежуються, якщо їх значення не змінені.

    Щоб написати власну логіку обмеження для виразів CEL, перегляньте optionalOldSelf.

  • x-kubernetes-list-type Помилки, що виникають через зміну типу списку у підсхемі, не будуть обмежені. Наприклад, додавання set до списку з дублікатів завжди призведе до помилки.

  • x-kubernetes-map-keys Помилки, що виникають через зміну ключів карти у схемі списку, не будуть обмежені.

  • required Помилки, що виникають через зміну списку обовʼязкових полів, не будуть обмежені.

  • properties Додавання/видалення/модифікація імен властивостей не обмежуються, але зміни до перевірок у схемах та підсхемах властивостей можуть бути обмежені, якщо імʼя властивості залишається незмінним.

  • additionalProperties Видалення раніше вказаної валідації additionalProperties не буде обмежено.

  • metadata Помилки, що виникають через вбудовану валідацію обʼєкта metadata у Kubernetes, не будуть обмежені (наприклад, імʼя обʼєкта або символи у значенні мітки). Якщо ви вказуєте свої власні додаткові правила для метаданих власного ресурсу, ця додаткова валідація буде обмежена.

Правила валідації

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.29 [stable]

Правила валідації використовують Common Expression Language (CEL) для валідації значень власних ресурсів. Правила валідації включаються в схеми CustomResourceDefinition за допомогою розширення x-kubernetes-validations.

Правило обмежується місцем знаходження розширення x-kubernetes-validations у схемі. Змінна self у виразі CEL привʼязана до значення, що перевіряється.

Всі правила валідації обмежені поточним обʼєктом: ніякі міжобʼєктні або stateful правила валідації не підтримуються.

Наприклад:

  ...
  openAPIV3Schema:
    type: object
    properties:
      spec:
        type: object
        x-kubernetes-validations:
          - rule: "self.minReplicas <= self.replicas"
            message: "replicas should be greater than or equal to minReplicas."
          - rule: "self.replicas <= self.maxReplicas"
            message: "replicas should be smaller than or equal to maxReplicas."
        properties:
          ...
          minReplicas:
            type: integer
          replicas:
            type: integer
          maxReplicas:
            type: integer
        required:
          - minReplicas
          - replicas
          - maxReplicas

відхилить запит на створення цього власного ресурсу:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  minReplicas: 0
  replicas: 20
  maxReplicas: 10

з відповіддю:

The CronTab "my-new-cron-object" is invalid:
* spec: Invalid value: map[string]interface {}{"maxReplicas":10, "minReplicas":0, "replicas":20}: replicas should be smaller than or equal to maxReplicas.

x-kubernetes-validations може містити декілька правил. rule під x-kubernetes-validations представляє вираз, який буде оцінюватися CEL. message представляє повідомлення, що показується при невдачі валідації. Якщо повідомлення не встановлено, відповідь буде такою:

The CronTab "my-new-cron-object" is invalid:
* spec: Invalid value: map[string]interface {}{"maxReplicas":10, "minReplicas":0, "replicas":20}: failed rule: self.replicas <= self.maxReplicas

Правила валідації компілюються при створенні/оновленні CRD. Запит на створення/оновлення CRD буде відхилено, якщо компіляція правил валідації зазнає невдачі. Процес компіляції включає перевірку типів.

Помилки компіляції:

  • no_matching_overload: ця функція не має перевантаження для типів аргументів.

    Наприклад, правило self == true для поля типу integer призведе до помилки:

    Invalid value: apiextensions.ValidationRule{Rule:"self == true", Message:""}: compilation failed: ERROR: \<input>:1:6: found no matching overload for '_==_' applied to '(int, bool)'
    
  • no_such_field: не містить бажаного поля.

    Наприклад, правило self.nonExistingField > 0 для неіснуючого поля поверне наступну помилку:

    Invalid value: apiextensions.ValidationRule{Rule:"self.nonExistingField > 0", Message:""}: compilation failed: ERROR: \<input>:1:5: undefined field 'nonExistingField'
    
  • invalid argument: недійсний аргумент для макросів.

    Наприклад, правило has(self) поверне помилку:

    Invalid value: apiextensions.ValidationRule{Rule:"has(self)", Message:""}: compilation failed: ERROR: <input>:1:4: invalid argument to has() macro
    

Приклади правил валідації:

ПравилоПризначення
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicasПеревірка, що три поля, які визначають репліки, впорядковані належним чином
'Available' in self.stateCountsПеревірка наявності запису з ключем 'Available' у map
(size(self.list1) == 0) != (size(self.list2) == 0)Перевірка, що один з двох списків не порожній, але не обидва
!('MY_KEY' in self.map1) || self['MY_KEY'].matches('^[a-zA-Z]*$')Перевірка значення map для конкретного ключа, якщо він у мапі
self.envars.filter(e, e.name == 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$')Перевірка поля 'value' у списку map, де ключове поле 'name' дорівнює 'MY_ENV'
has(self.expired) && self.created + self.ttl < self.expiredПеревірка, що дата 'expired' після дати 'create' плюс тривалість 'ttl'
self.health.startsWith('ok')Перевірка, що рядкове поле 'health' починається з префіксу 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10)Перевірка, що властивість 'foo' елемента списку map з ключем 'x' менше 10
type(self) == string ? self == '100%' : self == 1000Перевірка поля int-or-string для обох випадків int і string
self.metadata.name.startsWith(self.prefix)Перевірка, що імʼя обʼєкта має префікс іншого поля значення
self.set1.all(e, !(e in self.set2))Перевірка, що два списки множин не перетинаються
size(self.names) == size(self.details) && self.names.all(n, n in self.details)Перевірка, що мапа 'details' має ключі з елементів списку множин 'names'
size(self.clusters.filter(c, c.name == self.primary)) == 1Перевірка, що властивість 'primary' має лише одну появу у списку мап 'clusters'

Посилання: Supported evaluation on CEL

  • Якщо правило обмежене коренем ресурсу, воно може вибирати поля з будь-яких полів, оголошених у схемі OpenAPIv3 CRD, а також apiVersion, kind, metadata.name та metadata.generateName. Це включає вибір полів як у spec, так і в status в одному виразі:

      ...
      openAPIV3Schema:
        type: object
        x-kubernetes-validations:
          - rule: "self.status.availableReplicas >= self.spec.minReplicas"
        properties:
            spec:
              type: object
              properties:
                minReplicas:
                  type: integer
                ...
            status:
              type: object
              properties:
                availableReplicas:
                  type: integer
    
  • Якщо Rule обмежене обʼєктом з властивостями, доступні властивості обʼєкта можна вибирати за допомогою self.field, а наявність поля можна перевірити за допомогою has(self.field). Поля зі значенням null трактуються як відсутні поля у виразах CEL.

      ...
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            x-kubernetes-validations:
              - rule: "has(self.foo)"
            properties:
              ...
              foo:
                type: integer
    
  • Якщо Rule обмежене обʼєктом з додатковими властивостями (тобто map), значення map доступні через self[mapKey], наявність map можна перевірити за допомогою mapKey in self, а всі записи з map доступні за допомогою макросів і функцій CEL, таких як self.all(...).

      ...
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            x-kubernetes-validations:
              - rule: "self['xyz'].foo > 0"
            additionalProperties:
              ...
              type: object
              properties:
                foo:
                  type: integer
    
  • Якщо правило обмежене масивом, елементи масиву доступні через self[i] та також за допомогою макросів і функцій.

      ...
      openAPIV3Schema:
        type: object
        properties:
          ...
          foo:
            type: array
            x-kubernetes-validations:
              - rule: "size(self) == 1"
            items:
              type: string
    
  • Якщо правило обмежене скаляром, self привʼязується до значення скаляра.

      ...
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              ...
              foo:
                type: integer
                x-kubernetes-validations:
                - rule: "self > 0"
    

Приклади:

тип поля, для якого обмежено правилоПриклад правила
кореневий обʼєктself.status.actual <= self.spec.maxDesired
map обʼєктівself.components['Widget'].priority < 10
список цілих чиселself.values.all(value, value >= 0 && value < 100)
рядокself.startsWith('kube')

apiVersion, kind, metadata.name та metadata.generateName завжди доступні з кореня обʼєкта та з будь-яких обʼєктів з анотацією x-kubernetes-embedded-resource. Інші властивості метаданих недоступні.

Невідомі дані, збережені у власниї ресурсах за допомогою x-kubernetes-preserve-unknown-fields, не доступні у виразах CEL. Це включає:

  • Невідомі значення полів, які зберігаються у схемах обʼєктів з x-kubernetes-preserve-unknown-fields.

  • Властивості обʼєктів, де схема властивостей має "unknown type". "Unknown type" визначається рекурсивно як:

    • Схема без типу і з встановленим x-kubernetes-preserve-unknown-fields
    • Масив, де схема елементів має "unknown type"
    • Обʼєкт, де схема additionalProperties має "unknown type"

Доступні лише назви властивостей форми [a-zA-Z_.-/][a-zA-Z0-9_.-/]*. Доступні назви властивостей екрануються за наступними правилами при доступі у виразі:

послідовність екрануванняеквівалент назви властивості
__underscores____
__dot__.
__dash__-
__slash__/
__{keyword}__CEL RESERVED keyword

Примітка: зарезервоване ключове слово CEL повинно точно збігатися з назвою властивості, щоб бути екранованим (наприклад, int у слові sprint не буде екрановано).

Приклади екранування:

назва властивостіправило з екранованою назвою властивості
namespaceself.__namespace__ > 0
x-propself.x__dash__prop > 0
redact__dself.redact__underscores__d > 0
stringself.startsWith('kube')

Рівність масивів з x-kubernetes-list-type типу set або map ігнорує порядок елементів, тобто [1, 2] == [2, 1]. Конкатенація масивів з x-kubernetes-list-type використовує семантику типу списку:

  • set: X + Y виконує обʼєднання, де зберігаються позиції елементів у X, а непересічні елементи у Y додаються, зберігаючи їх частковий порядок.

  • map: X + Y виконує злиття, де зберігаються позиції всіх ключів у X, але значення перезаписуються значеннями у Y, коли ключі X та Y перетинаються. Елементи у Y з непересічними ключами додаються, зберігаючи їх частковий порядок.

Ось відповідність типів між OpenAPIv3 та CEL:

Тип OpenAPIv3Тип CEL
'object' з Propertiesobject / "message type"
'object' з AdditionalPropertiesmap
'object' з x-kubernetes-embedded-typeobject / "message type", 'apiVersion', 'kind', 'metadata.name' та 'metadata.generateName' неявно включені у схему
'object' з x-kubernetes-preserve-unknown-fieldsobject / "message type", невідомі поля НЕ доступні у виразі CEL
x-kubernetes-int-or-stringдинамічний обʼєкт, який може бути або int, або string, type(value) можна використовувати для перевірки типу
'arraylist
'array' з x-kubernetes-list-type=maplist з порівнянням на основі map та гарантіями унікальності ключів
'array' з x-kubernetes-list-type=setlist з порівнянням на основі множини та гарантіями унікальності елементів
'boolean'boolean
'number' (всі формати)double
'integer' (всі формати)int (64)
'null'null_type
'string'string
'string' з format=byte (base64 encoded)bytes
'string' з format=datetimestamp (google.protobuf.Timestamp)
'string' з format=datetimetimestamp (google.protobuf.Timestamp)
'string' з format=durationduration (google.protobuf.Duration)

посилання: CEL types, OpenAPI types, Структурні схемм Kubernetes.

Поле messageExpression

Поле messageExpression, аналогічно до поля message, визначає рядок, яким буде повідомлено про негативний результат правила валідації. Однак messageExpression дозволяє використовувати вираз CEL для побудови повідомлення, що дозволяє вставляти більш описову інформацію в повідомлення про невдачу валідації. messageExpression має оцінюватися як рядок і може використовувати ті ж змінні, що і поле rule. Наприклад:

x-kubernetes-validations:
- rule: "self.x <= self.maxLimit"
  messageExpression: '"x перевищує максимальний ліміт " + string(self.maxLimit)'

Майте на увазі, що конкатенація рядків CEL (оператор +) автоматично не призводить до перетворення в рядок. Якщо у вас є скаляр, який не є рядком, використовуйте функцію string(<значення>) для перетворення скаляра в рядок, як показано в прикладі вище.

messageExpression повинен оцінюватися як рядок, і це перевіряється при створенні CRD. Зауважте, що можна встановити як message, так і messageExpression для одного правила, і якщо обидва присутні, буде використовуватися messageExpression. Однак, якщо messageExpression оцінюється як помилка, буде використовуватися рядок, визначений у message, і помилка messageExpression буде зафіксована. Це повернення до попереднього стану також відбудеться, якщо вираз CEL, визначений у messageExpression, генерує порожній рядок або рядок, що містить розриви рядків.

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

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

Встановлення messageExpression є необовʼязковим.

Поле message

Якщо ви хочете встановити статичне повідомлення, ви можете передати message замість messageExpression. Значення message використовується як непрозорий рядок помилки, якщо перевірка не пройшла успішно.

Встановлення message є необовʼязковим.

Поле reason

Ви можете додати машинно-читаєму причину негативного результату перевірки в межах validation, щоб повертати її, коли запит не відповідає цьому правилу перевірки.

Наприклад:

x-kubernetes-validations:
- rule: "self.x <= self.maxLimit"
  reason: "FieldValueInvalid"

Код стану HTTP, повернутий абоненту, буде відповідати причині першої невдачі перевірки. Наразі підтримуються такі причини: "FieldValueInvalid", "FieldValueForbidden", "FieldValueRequired", "FieldValueDuplicate". Якщо причини не встановлені або невідомі, типово використовується "FieldValueInvalid".

Встановлення reason є необовʼязковим.

Поле fieldPath

Ви можете вказати шлях поля, який повертається, коли перевірка завершується негативним результатом.

Наприклад:

x-kubernetes-validations:
- rule: "self.foo.test.x <= self.maxLimit"
  fieldPath: ".foo.test.x"

У вищенаведеному прикладі перевіряється значення поля x, яке повинно бути менше значення maxLimit. Якщо не вказано fieldPath, коли результат перевірки негативний , fieldPath буде типово відповідати місцю розташування self. З вказаним fieldPath повернена помилка буде мати fieldPath, який належним чином посилатиметься на місце поля x.

Значення fieldPath повинно бути відносним шляхом JSON, що обмежений місцем цього розширення x-kubernetes-validations у схемі. Крім того, воно повинно посилатися на існуюче поле в межах схеми. Наприклад, коли перевірка перевіряє, чи є певний атрибут foo у map testMap, ви можете встановити fieldPath на ".testMap.foo" або .testMap['foo']'. Якщо для перевірки потрібно перевірити унікальні атрибути у двох списках, fieldPath можна встановити для будь-якого зі списків. Наприклад, його можна встановити на .testList1 або .testList2. Наразі підтримується дочірня операція для посилання на існуюче поле. Для отримання додаткової інформації див. Підтримка JSONPath у Kubernetes. Поле fieldPath не підтримує індексування масивів числовими значеннями.

Встановлення fieldPath є необовʼязковим.

Поле optionalOldSelf

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.30 [beta]

Якщо у вашому кластері не включено проковзування перевірки CRD, API визначення власного ресурсу не містить цього поля, і спроба встановити його може призвести до помилки.

Поле optionalOldSelf є булевим полем, яке змінює поведінку Правил переходу, описаних нижче. Зазвичай правило переходу не оцінюється, якщо oldSelf не може бути визначено: під час створення обʼєкта або коли нове значення вводиться під час оновлення.

Якщо optionalOldSelf встановлено в true, тоді правила переходу завжди будуть оцінюватися, і тип oldSelf буде змінено на тип CEL Optional.

optionalOldSelf корисно в тих випадках, коли автори схеми хочуть мати більше інструментів керування ніж надається за стандартно на основі рівності, щоб ввести нові, зазвичай строгіші обмеження на нові значення, зазвичай більш жорсткі для нових значень, але все ще дозволяючи те, що старі значення можуть бути пропущені з використанням старих правил.

Example Usage:

CELОпис
self.foo == "foo" || (oldSelf.hasValue() && oldSelf.value().foo != "foo")Проковзування правила. Якщо значення має значення «foo», воно повинно залишатися foo. Але якщо воно існувало до того, як було введено обмеження «foo», воно може використовувати будь-яке значення
[oldSelf.orValue(""), self].all(x, ["OldCase1", "OldCase2"].exists(case, x == case)) || ["NewCase1", "NewCase2"].exists(case, self == case) || ["NewCase"].has(self)"Перевірка для вилучених випадків перерахування, якщо oldSelf використовував їх"
oldSelf.optMap(o, o.size()).orValue(0) < 4 || self.size() >= 4Перевірка нещодавно збільшеного мінімального розміру map або списку з проковзуванням

Функції перевірки

Доступні функції включають:

Правила переходу

Правило, яке містить вираз з посиланням на ідентифікатор oldSelf, неявно вважається правилом переходу. Правила переходу дозволяють авторам схеми запобігати певним переходам між двома в іншому випадку допустимими станами. Наприклад:

type: string
enum: ["low", "medium", "high"]
x-kubernetes-validations:
- rule: "!(self == 'high' && oldSelf == 'low') && !(self == 'low' && oldSelf == 'high')"
  message: не можна переходити безпосередньо між 'low' та 'high'

На відміну від інших правил, правила переходу застосовуються тільки до операцій, що відповідають наступним критеріям:

  • Операція оновлює існуючий обʼєкт. Правила переходу ніколи не застосовуються до операцій створення.

  • Існують як старе, так і нове значення. Залишається можливим перевірити, чи було додано або вилучено значення, розмістивши правило переходу на батьківському вузлі. Правила переходу ніколи не застосовуються до створення власних ресурсів. Якщо вони розміщені в необовʼязковому полі, правило переходу не буде застосовуватися до операцій оновлення, які встановлюють або прибирають поле.

  • Шлях до вузла схеми, який перевіряється правилом переходу, повинен розподілятися на вузол, який може порівнюватися між старим обʼєктом і новим обʼєктом. Наприклад, елементи списку та їх нащадки (spec.foo[10].bar) не обовʼязково будуть кореліювати між існуючим обʼєктом та пізнішим оновленням того ж обʼєкта.

Помилки будуть генеруватися при записі CRD, якщо вузол схеми містить правило переходу, яке ніколи не може бути застосоване, наприклад "oldSelf не може бути використано на некорельованій частині схеми у межах path".

Правила переходу дозволені тільки для корелятивних частин схеми. Частина схеми є корелятивною, якщо всі батьківські схеми масиву мають тип x-kubernetes-list-type=map; будь-які батьківські схеми масиву типу set чи atomic роблять неможливим однозначне корелювання між self та oldSelf.

Нижче наведено кілька прикладів правил переходу:

Приклади правил переходу
ВикористанняПравило
Незмінністьself.foo == oldSelf.foo
Запобігання модифікації/видаленню після присвоєнняoldSelf != 'bar' || self == 'bar' або !has(oldSelf.field) || has(self.field)
Тільки додавання до множиниself.all(element, element in oldSelf)
Якщо попереднє значення було X, нове значення може бути лише A або B, не Y або ZoldSelf != 'X' || self in ['A', 'B']
Монотонні (незменшувані) лічільникиself >= oldSelf

Використання ресурсів функціями перевірки

Коли ви створюєте або оновлюєте CustomResourceDefinition, що використовує правила перевірки, API сервер перевіряє можливий вплив виконання цих правил перевірки. Якщо правило оцінюється як надмірно витратне у виконанні, API сервер відхиляє операцію створення або оновлення та повертає повідомлення про помилку. Подібна система використовується під час виконання, яка спостерігає за діями інтерпретатора. Якщо інтерпретатор виконує занадто багато інструкцій, виконання правила буде припинено, і виникне помилка. Кожен CustomResourceDefinition також має певний ліміт ресурсів для завершення виконання всіх своїх правил перевірки. Якщо сумарна оцінка всіх правил перевищує цей ліміт під час створення, то також виникне помилка перевірки.

Ймовірність виникнення проблем з ресурсним бюджетом перевірки низька, якщо ви лише зазначаєте правила, які завжди займають однакову кількість часу незалежно від розміру вхідних даних. Наприклад, правило, яке стверджує, що self.foo == 1, само по собі не має ризику відхилення через ресурсний бюджет перевірки. Але якщо foo є рядком і ви визначаєте правило перевірки self.foo.contains("someString"), то це правило займає довше часу на виконання в залежності від довжини foo. Інший приклад: якщо foo є масивом, і ви вказали правило перевірки self.foo.all(x, x > 5). Система оцінки завжди припускає найгірший сценарій, якщо не вказано ліміт на довжину foo, і це буде стосуватися будь-якого обʼєкта, який можна ітерувати (списки, мапи тощо).

Через це, вважається найкращою практикою встановлювати ліміт через maxItems, maxProperties і maxLength для будь-якого обʼєкта, який обробляється в правилі перевірки, щоб уникнути помилок перевірки під час оцінки витрат. Наприклад, якщо є наступна схема з одним правилом:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      items:
        type: string
      x-kubernetes-validations:
        - rule: "self.all(x, x.contains('a string'))"

то API сервер відхилить це правило через бюджет перевірки з помилкою:

spec.validation.openAPIV3Schema.properties[spec].properties[foo].x-kubernetes-validations[0].rule: Forbidden:
CEL rule exceeded budget by more than 100x (try simplifying the rule, or adding maxItems, maxProperties, and
maxLength where arrays, maps, and strings are used)

Відхилення відбувається тому, що self.all передбачає виклик contains() для кожного рядка у foo, що в свою чергу перевіряє, чи містить даний рядок 'a string'. Без обмежень, це дуже витратне правило.

Якщо ви не вказуєте жодного ліміту перевірки, оцінена вартість цього правила перевищить ліміт вартості для одного правила. Але якщо додати обмеження в потрібні місця, правило буде дозволено:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      maxItems: 25
      items:
        type: string
        maxLength: 10
      x-kubernetes-validations:
        - rule: "self.all(x, x.contains('a string'))"

Система оцінки витрат враховує кількість разів, коли правило буде виконане, крім оціненої вартості самого правила. Наприклад, наступне правило матиме таку ж оцінену вартість, як і попередній приклад (незважаючи на те, що правило тепер визначено для окремих елементів масиву):

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      maxItems: 25
      items:
        type: string
        x-kubernetes-validations:
          - rule: "self.contains('a string'))"
        maxLength: 10

Якщо у списку всередині іншого списку є правило перевірки, яке використовує self.all, це значно дорожче, ніж правило для не вкладеного списку. Правило, яке було б дозволено для не вкладеного списку, може потребувати нижчих обмежень для обох вкладених списків, щоб бути дозволеним. Наприклад, навіть без встановлення обмежень, наступне правило дозволено:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      items:
        type: integer
    x-kubernetes-validations:
      - rule: "self.all(x, x == 5)"

Але те саме правило для наступної схеми (з доданим вкладеним масивом) викликає помилку перевірки:

openAPIV3Schema:
  type: object
  properties:
    foo:
      type: array
      items:
        type: array
        items:
          type: integer
        x-kubernetes-validations:
          - rule: "self.all(x, x == 5)"

Це тому, що кожен елемент foo сам є масивом, і кожен підмасив викликає self.all. Уникайте вкладених списків і map, якщо це можливо, де використовуються правила перевірки.

Встановлення станадартних значень

Встановлення стандартних значень дозволяє вказати такі значення у схемі перевірки OpenAPI v3:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        # openAPIV3Schema це схема для перевірки власних обʼєктів.
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\д+)?){4}$'
                  default: "5 0 * * *"
                image:
                  type: string
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                  default: 1
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

Таким чином, і cronSpec, і replicas будуть мати стандартні значення:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  image: my-awesome-cron-image

приводить до

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "5 0 * * *"
  image: my-awesome-cron-image
  replicas: 1

Встановлення стандартних значень відбувається на обʼєкті

  • в запиті до API сервера з використанням стандартних значень версії запиту,
  • під час читання з etcd з використанням стандартних значень версії зберігання,
  • після втулків для мутаційного допуску з невипадковими змінами з використанням стандартних значень версії веб-хука допуску.

Стандартне значення, застосовані під час читання даних з etcd, автоматично не записуються назад у etcd. Потрібен запит на оновлення через API для збереження цих стандартних значень у etcd.

Стандартні значення мають бути обрізані (за винятком стандартних значень полів metadata) та відповідати схемі перевірки.

Стандартні значення полів metadata вузлів x-kubernetes-embedded-resources: true (або частин типових значень з metadata) не будуть обрізані під час створення CustomResourceDefinition, але будуть обрізані під час оброки запитів.

Стандартні значення та nullable

Значення null для полів, які або не вказують прапорець nullable, або задають його значення як false, будуть видалені до того, як буде застосовано стандартне значення. Якщо стандартне значення присутнє, воно буде застосоване. Коли nullable дорівнює true, значення null будуть збережені і не будуть змінені на стандартні значення.

Наприклад, розглянемо наступну схему OpenAPI:

type: object
properties:
  spec:
    type: object
    properties:
      foo:
        type: string
        nullable: false
        default: "default"
      bar:
        type: string
        nullable: true
      baz:
        type: string

створення обʼєкта зі значеннями null для foo, bar і baz:

spec:
  foo: null
  bar: null
  baz: null

призведе до

spec:
  foo: "default"
  bar: null

де foo буде обрізане та встановлене стандартне значення, оскільки поле є ненульовим, bar залишиться зі значенням null через nullable: true, а baz буде видалено, оскільки поле є ненульовим і не має стандартного значення.

Публікація схеми валідації в OpenAPI

Схеми валідації OpenAPI v3 CustomResourceDefinition, які є структурованими та дозволяють обрізку, публікуються як OpenAPI v3 та OpenAPI v2 з сервера API Kubernetes. Рекомендується використовувати документ OpenAPI v3, оскільки він є представленням схеми валідації OpenAPI v3 для CustomResourceDefinition без втрат, тоді як OpenAPI v2 представляє перетворення з втратами.

Kubectl використовує опубліковану схему для виконання клієнтської валідації (kubectl create та kubectl apply), пояснення схеми (kubectl explain) для власних ресурсів. Опублікована схема також може бути використана для інших цілей, таких як генерація клієнтів або документації.

Сумісність з OpenAPI V2

Для сумісності з OpenAPI V2, схема валідації OpenAPI v3 виконує перетворення з втратамиу схему OpenAPI v2. Схема зʼявляється в полях definitions та paths у специфікації OpenAPI v2.

Під час перетворення застосовуються наступні зміни для збереження зворотної сумісності з kubectl версії 1.13. Ці зміни запобігають занадто строгій роботі kubectl та відхиленню дійсних схем OpenAPI, які він не розуміє. Перетворення не змінює схему валідації, визначену в CRD, і тому не впливає на валідацію на сервері API.

  1. Наступні поля видаляються, оскільки вони не підтримуються OpenAPI v2:

    • Поля allOf, anyOf, oneOf та not видаляються
  2. Якщо встановлено nullable: true, ми видаляємо type, nullable, items та properties, оскільки OpenAPI v2 не може відобразити nullable. Це необхідно для того, щоб kubectl не відхиляв правильні обʼєкти.

Додаткові колонки виводу

Інструмент kubectl покладається на форматування виводу на стороні сервера. Сервер API вашого кластера вирішує, які колонки показуються командою kubectl get. Ви можете налаштувати ці колонки для CustomResourceDefinition. Наступний приклад додає колонки Spec, Replicas та Age.

Збережіть CustomResourceDefinition у файл resourcedefinition.yaml:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              cronSpec:
                type: string
              image:
                type: string
              replicas:
                type: integer
    additionalPrinterColumns:
    - name: Spec
      type: string
      description: The cron spec defining the interval a CronJob is run
      jsonPath: .spec.cronSpec
    - name: Replicas
      type: integer
      description: The number of jobs launched by the CronJob
      jsonPath: .spec.replicas
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp

Створіть CustomResourceDefinition:

kubectl apply -f resourcedefinition.yaml

Створіть екземпляр, використовуючи my-crontab.yaml з попереднього розділу.

Викличте вивід на стороні сервера:

kubectl get crontab my-new-cron-object

Зверніть увагу на колонки NAME, SPEC, REPLICAS та AGE у виводі:

NAME                 SPEC        REPLICAS   AGE
my-new-cron-object   * * * * *   1          7s

Пріоритет

Кожна колонка включає поле priority. Наразі пріоритет розрізняє стовпці, що показуються у стандартному вигляді або в розгорнутому (за допомогою прапорця -o wide).

  • Колонки з пріоритетом 0 показані у стандартному вигляді.
  • Колонки з пріоритетом більшим за 0 показані тільки у широкому (wide) вигляді.

Тип

Поле type колонки може бути одним з наступних (порівняйте OpenAPI v3 data types):

  • integer — цілі числа без плаваючої точки
  • number — числа з плаваючою точкою
  • string — строки
  • boolean — true або false
  • date — виводиться диференційовано як час, що минув від цієї мітки.

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

Формат

Поле format колонки може бути одним з наступних:

  • int32
  • int64
  • float
  • double
  • byte
  • date
  • date-time
  • password

Поле format колонки контролює стиль, який використовується при виводі значення за допомогою kubectl.

Селектори полів

Селектори полів дозволяють клієнтам вибирати власні ресурси на основі значення одного або декількох полів ресурсу.

Усі власні ресурси підтримують вибір полів metadata.name та metadata.namespace.

Поля, оголошені в CustomResourceDefinition, також можуть бути використані з селектором полів, коли вони включені в поле spec.versions[*].selectableFields CustomResourceDefinition.

Поля селекторів власних ресурсів

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [beta]

Для Kubernetes 1.31 можливість визначати селектори полів для власних ресурсів доступна стандартно (стандартно увімкнена з Kubernetes v1.31); ви можете вимкнути цю функцію для вашого кластера, відключивши функціональну можливість CustomResourceFieldSelectors. Поле spec.versions[*].selectableFields у CustomResourceDefinition може бути використане для оголошення, які інші поля у власному ресурсі можуть бути використані у селекторах полів з функціональною можливісюь CustomResourceFieldSelectors (Ця функція є стандартно увімкненою з Kubernetes v1.31). Ось приклад, який додає поля .spec.color та .spec.size як доступні для вибору.

Збережіть CustomResourceDefinition у файл shirt-resource-definition.yaml:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: shirts.stable.example.com
spec:
  group: stable.example.com
  scope: Namespaced
  names:
    plural: shirts
    singular: shirt
    kind: Shirt
  versions:
  - name: v1
    served: true
    storage: true
    schema:
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              color:
                type: string
              size:
                type: string
    selectableFields:
    - jsonPath: .spec.color
    - jsonPath: .spec.size
    additionalPrinterColumns:
    - jsonPath: .spec.color
      name: Color
      type: string
    - jsonPath: .spec.size
      name: Size
      type: string

Створіть CustomResourceDefinition:

kubectl apply -f shirt-resource-definition.yaml

Визначте деякі сорочки (Shirts), редагуючи файл shirt-resources.yaml; наприклад:

---
apiVersion: stable.example.com/v1
kind: Shirt
metadata:
  name: example1
spec:
  color: blue
  size: S
---
apiVersion: stable.example.com/v1
kind: Shirt
metadata:
  name: example2
spec:
  color: blue
  size: M
---
apiVersion: stable.example.com/v1
kind: Shirt
metadata:
  name: example3
spec:
  color: green
  size: M

Створіть власні ресурси:

kubectl apply -f shirt-resources.yaml

Отримайте всі ресурси:

kubectl get shirts.stable.example.com

Вивід буде:

NAME       COLOR  SIZE
example1   blue   S
example2   blue   M
example3   green  M

Отримайте сорочки синього кольору (отримайте сорочки у яких color має значення blue):

kubectl get shirts.stable.example.com --field-selector spec.color=blue

Повинен зʼявитися такий вивід:

NAME       COLOR  SIZE
example1   blue   S
example2   blue   M

Отримайте лише ресурси з кольором green та розміром M:

kubectl get shirts.stable.example.com --field-selector spec.color=green,spec.size=M

Повинен зʼявитися такий вивід:

NAME       COLOR  SIZE
example2   blue   M

Субресурси

Власні ресурси підтримують субресурси /status та /scale.

Субресурси status та scale можна опціонально увімкнути, визначивши їх у CustomResourceDefinition.

Субресурс Status

Коли субресурс статусу увімкнено, субресурс /status для власного ресурсу буде доступним.

  • Стани status та spec представлені відповідно за допомогою JSONPath .status та .spec всередині власного ресурсу.

  • Запити PUT до субресурсу /status приймають обʼєкт власного ресурсу і ігнорують зміни до будь-чого, крім стану status.

  • Запити PUT до субресурсу /status лише перевіряють стан status власного ресурсу.

  • Запити PUT/POST/PATCH до власного ресурсу ігнорують зміни до стану status.

  • Значення .metadata.generation збільшується для всіх змін, за винятком змін у .metadata або .status.

  • У корені схеми валідації OpenAPI CRD дозволяються тільки такі конструкції:

    • description
    • example
    • exclusiveMaximum
    • exclusiveMinimum
    • externalDocs
    • format
    • items
    • maximum
    • maxItems
    • maxLength
    • minimum
    • minItems
    • minLength
    • multipleOf
    • pattern
    • properties
    • required
    • title
    • type
    • uniqueItems

Субресурс Scale

Коли субресурс масштабу увімкнено, субресурс /scale для власного ресурсу буде доступним. Обʼєкт autoscaling/v1.Scale надсилається як навантаження для /scale.

Для увімкнення субресурсу масштабу наступні поля визначаються в CustomResourceDefinition.

  • specReplicasPath визначає JSONPath всередині власного ресурсу, що відповідає scale.spec.replicas.

    • Це обовʼязкове значення.
    • Дозволяються тільки JSONPath під .spec і з позначенням через крапку.
    • Якщо у власному ресурсі немає значення під specReplicasPath, субресурс /scale поверне помилку при GET запиті.
  • statusReplicasPath визначає JSONPath всередині власного ресурсу, що відповідає scale.status.replicas.

    • Це обовʼязкове значення.
    • Дозволяються тільки JSONPath під .status і з позначенням через крапку.
    • Якщо у власному ресурсі немає значення під statusReplicasPath, значення репліки статусу у субресурсі /scale за замовчуванням дорівнюватиме 0.
  • labelSelectorPath визначає JSONPath всередині власного ресурсу, що відповідає Scale.Status.Selector.

    • Це необовʼязкове значення.
    • Воно повинно бути встановлено для роботи з HPA та VPA.
    • Дозволяються тільки JSONPath під .status або .spec і з позначенням через крапку.
    • Якщо у власному ресурсі немає значення під labelSelectorPath, значення селектора статусу у субресурсі /scale за замовчуванням буде порожнім рядком.
    • Поле, на яке вказує цей JSONPath, повинно бути рядковим полем (не комплексним селектором), що містить серіалізований селектор міток у вигляді рядка.

У наступному прикладі увімкнено обидва субресурси: статусу та масштабу.

Збережіть CustomResourceDefinition у файл resourcedefinition.yaml:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
            status:
              type: object
              properties:
                replicas:
                  type: integer
                labelSelector:
                  type: string
      # subresources описує субресурси для власних ресурсів.
      subresources:
        # status увімкнення субресурсу статусу.
        status: {}
        # scale увімкнення субресурсу масштабу.
        scale:
          # specReplicasPath визначає JSONPath всередині власного ресурсу, що відповідає Scale.Spec.Replicas.
          specReplicasPath: .spec.replicas
          # statusReplicasPath визначає JSONPath всередині власного ресурсу, що відповідає Scale.Status.Replicas.
          statusReplicasPath: .status.replicas
          # labelSelectorPath визначає JSONPath всередині власного ресурсу, що відповідає Scale.Status.Selector.
          labelSelectorPath: .status.labelSelector
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct

І створіть його:

kubectl apply -f resourcedefinition.yaml

Після створення обʼєкта CustomResourceDefinition, ви можете створювати власні обʼєкти.

Якщо ви збережете наступний YAML у файл my-crontab.yaml:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image
  replicas: 3

і створите його:

kubectl apply -f my-crontab.yaml

Тоді будуть створені нові RESTful API точки доступу на рівні простору імен:

/apis/stable.example.com/v1/namespaces/*/crontabs/status

та

/apis/stable.example.com/v1/namespaces/*/crontabs/scale

Власний ресурс можна масштабувати за допомогою команди kubectl scale. Наприклад, наступна команда встановлює .spec.replicas власного ресурсу, створеного вище, до 5:

kubectl scale --replicas=5 crontabs/my-new-cron-object
crontabs "my-new-cron-object" scaled

kubectl get crontabs my-new-cron-object -o jsonpath='{.spec.replicas}'
5

Ви можете використовувати PodDisruptionBudget, щоб захистити власні ресурси, які мають увімкнений субресурс масштабу.

Категорії

Категорії — це список групованих ресурсів, до яких належить власний ресурс (наприклад, all). Ви можете використовувати kubectl get <category-name>, щоб вивести список ресурсів, що належать до категорії.

Наступний приклад додає all у список категорій у CustomResourceDefinition та ілюструє, як вивести власний ресурс за допомогою kubectl get all.

Збережіть наступний CustomResourceDefinition у файл resourcedefinition.yaml:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct
    # categories - це список групованих ресурсів, до яких належить власний ресурс.
    categories:
    - all

і створіть його:

kubectl apply -f resourcedefinition.yaml

Після створення обʼєкта CustomResourceDefinition, ви можете створювати власні обʼєкти.

Збережіть наступний YAML у файл

my-crontab.yaml:

apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: my-new-cron-object
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image

і створіть його:

kubectl apply -f my-crontab.yaml

Ви можете вказати категорію при використанні kubectl get:

kubectl get all

і це включатиме власні ресурси типу CronTab:

NAME                          AGE
crontabs/my-new-cron-object   3s

Що далі

11.1.2 - Версії у CustomResourceDefinitions

Ця сторінка пояснює, як додати інформацію про версії до CustomResourceDefinitions, щоб вказати рівень стабільності ваших CustomResourceDefinitions або перейти на нову версію з конвертацією між представленнями API. Також описується, як оновити обʼєкт з однієї версії до іншої.

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

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

Ви вже повинні розуміти що таке власні ресурси.

Версія вашого Kubernetes сервера має бути не старішою ніж v1.16. Для перевірки версії введіть kubectl version.

Огляд

API CustomResourceDefinition надає робочий процес для впровадження та оновлення нових версій CustomResourceDefinition.

Коли створюється CustomResourceDefinition, перша версія встановлюється в списку spec.versions CustomResourceDefinition на відповідний рівень стабільності та номер версії. Наприклад, v1beta1 буде вказувати, що перша версія ще не стабільна. Всі обʼєкти власних ресурсів спочатку будуть зберігатися в цій версії.

Після створення CustomResourceDefinition, клієнти можуть почати використовувати API v1beta1.

Пізніше може знадобитися додати нову версію, наприклад v1.

Додавання нової версії:

  1. Виберіть стратегію конвертації. Оскільки обʼєкти власних ресурсів повинні мати можливість обслуговуватися в обох версіях, це означає, що вони іноді будуть обслуговуватися у версії, відмінній від тієї, в якій зберігаються. Для цього іноді потрібно конвертувати обʼєкти власних ресурсів між версією, в якій вони зберігаються, та версією, в якій вони обслуговуються. Якщо конвертація передбачає зміни схеми та вимагає власної логіки, слід використовувати конвертаційний webhook. Якщо змін схеми немає, можна використовувати стратегію конвертації None, і при обслуговуванні різних версій буде змінено лише поле apiVersion.
  2. Якщо використовуються конвертаційні webhook'и, створіть і розгорніть конвертаційний webhook. Див. Конвертація через webhook для отримання більш детальної інформації.
  3. Оновіть CustomResourceDefinition, включивши нову версію в список spec.versions з served:true. Також встановіть поле spec.conversion на вибрану стратегію конвертації. Якщо використовується конвертаційний webhook, налаштуйте поле spec.conversion.webhookClientConfig для виклику webhook.

Після додавання нової версії клієнти можуть поступово перейти на нову версію. Цілком безпечно для деяких клієнтів використовувати стару версію, тоді як інші використовують нову версію.

Міграція збережених обʼєктів до нової версії:

  1. Див. розділ оновлення наявних обʼєктів до нової збереженої версії.

Клієнти можуть безпечно використовувати як стару, так і нову версію до, під час і після оновлення обʼєктів до нової збереженої версії.

Видалення старої версії:

  1. Переконайтеся, що всі клієнти повністю перейшли на нову версію. Логи kube-apiserver можна переглянути для виявлення клієнтів, які все ще використовують стару версію.
  2. Встановіть served на false для старої версії в списку spec.versions. Якщо якісь клієнти все ще несподівано використовують стару версію, вони можуть почати повідомляти про помилки при спробі доступу до обʼєктів власних ресурсів у старій версії. У такому випадку поверніться до використання served:true на старій версії, мігруйте залишкових клієнтів на нову версію та повторіть цей крок.
  3. Переконайтеся, що крок оновлення поточних обʼєктів до нової збереженої версії завершено.
    1. Перевірте, що storage встановлено на true для нової версії в списку spec.versions в CustomResourceDefinition.
    2. Перевірте, що стара версія більше не згадується в status.storedVersions CustomResourceDefinition.
  4. Видаліть стару версію зі списку spec.versions CustomResourceDefinition.
  5. Припиніть підтримку конвертації для старої версії в конвертаційних webhook'ах.

Зазначення кількох версій

Поле versions в CustomResourceDefinition API може бути використане для підтримки кількох версій розроблених вами власних ресурсів. Версії можуть мати різні схеми, а конвертаційні webhook'и можуть конвертувати власні ресурси між версіями. Конвертації через webhook повинні дотримуватися конвенцій Kubernetes API в тих випадках, де це застосовується. Зокрема, дивіться документацію по змінах API для набору корисних порад та рекомендацій.

У цьому прикладі показано CustomResourceDefinition з двома версіями. У першому прикладі припускається, що всі версії мають однакову схему без конвертації між ними. У YAML-файлах наведено коментарі, які надають додатковий контекст.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # назва повинна відповідати полям spec нижче, і бути у формі: <plural>.<group>
  name: crontabs.example.com
spec:
  # імʼя групи, яке буде використовуватися для REST API: /apis/<group>/<version>
  group: example.com
  # список версій, підтримуваних цим CustomResourceDefinition
  versions:
  - name: v1beta1
    # Кожна версія може бути увімкнена/вимкнена прапорцем Served.
    served: true
    # Лише одна версія повинна бути позначена як версія зберігання.
    storage: true
    # Схема обовʼязкова
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  # Секція конвертації була введена в Kubernetes 1.13+ зі стандартним значенням
  # конвертації None (стратегії встановленя субполів None).
  conversion:
    # Конвертація None передбачає ту ж саму схему для всіх версій, і лише встановлює apiVersion
    # поле власних ресрів у відповідне значення
    strategy: None
  # або Namespaced або Cluster
  scope: Namespaced
  names:
    # plural, назва ресрусу в що використовується в URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular, назва ресурсу в що буде використовуватись як аліас в CLI та у виводі
    singular: crontab
    # kind, зазвичай тип в однині в CamelCase. Ваші маніфести будуть використовувати це.
    kind: CronTab
    # shortNames, дозволяють коротше звертатися до ресурсу в CLI
    shortNames:
    - ct

# Застаріло у версії v1.16 на користь apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # імʼя має збігатися з полями spec нижче і бути у формі: <plural>.<group>
  name: crontabs.example.com
spec:
  # імʼя групи для використання в REST API: /apis/<group>/<version>
  group: example.com
  # список версій, які підтримуються цим CustomResourceDefinition
  versions:
  - name: v1beta1
    # Кожна версія може бути увімкненна/вимкнена за допомогою прапорця Served.
    served: true
    # Лише одна версія повинна бути позначена як версія зберігання.
    storage: true
  - name: v1
    served: true
    storage: false
  validation:
    openAPIV3Schema:
      type: object
      properties:
        host:
          type: string
        port:
          type: string
  # Розділ конвертації введений у Kubernetes 1.13+ зі стандартним значенням конвертації
  # None (підполе strategy встановлено на None).
  conversion:
    # None конфертація передбачає однакову схему для всіх версій і лише встановлює поле apiVersion
    # власних ресурсів у правильне значення
    strategy: None
  # або Namespaced, або Cluster
  scope: Namespaced
  names:
    # plural, назва ресрусу в що використовується в URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular, назва ресурсу в що буде використовуватись як аліас в CLI та у виводі
    singular: crontab
    # kind, зазвичай тип в однині в PascalCased. Ваші маніфести будуть використовувати це.
    kind: CronTab
    # shortNames, дозволяють коротше звертатися до ресурсу в CLI
    shortNames:
    - ct

Ви можете зберегти CustomResourceDefinition у YAML-файлі, а потім використати kubectl apply, щоб створити його.

kubectl apply -f my-versioned-crontab.yaml

Після створення API-сервер починає обслуговувати кожну включену версію за HTTP REST-точкою доступу. У наведеному вище прикладі, версії API доступні за адресами /apis/example.com/v1beta1 та /apis/example.com/v1.

Пріоритет версії

Незалежно від порядку, в якому версії визначені в CustomResourceDefinition, версія з найвищим пріоритетом використовується kubectl як стандартна версія для доступу до обʼєктів. Пріоритет визначається шляхом аналізу поля name для визначення номера версії, стабільності (GA, Beta або Alpha) та послідовності в межах цього рівня стабільності.

Алгоритм, який використовується для сортування версій, розроблений для сортування версій таким же чином, як проєкт Kubernetes сортує версії Kubernetes. Версії починаються з v, за яким слідує число, опціональне позначення beta або alpha, та опціональна додаткова числова інформація про версію. В загальному вигляді рядок версії може виглядати як v2 або v2beta1. Версії сортуються за таким алгоритмом:

  • Записи, що відповідають шаблонам версій Kubernetes, сортуються перед тими, що не відповідають шаблонам.
  • Для записів, що відповідають шаблонам версій Kubernetes, числові частини рядка версії сортуються від більшого до меншого.
  • Якщо після першої числової частини йдуть рядки beta або alpha, вони сортуються в такому порядку після еквівалентного рядка без суфікса beta або alpha (який вважається GA версією).
  • Якщо після beta або alpha йде ще одне число, ці числа також сортуються від більшого до меншого.
  • Рядки, що не відповідають зазначеному формату, сортуються за алфавітом, і числові частини не мають спеціального порядку. Зауважте, що в наведеному нижче прикладі foo1 сортується вище за foo10. Це відрізняється від сортування числових частин записів, що відповідають шаблонам версій Kubernetes.

Це може бути зрозуміло, якщо подивитися на наступний відсортований список версій:

- v10
- v2
- v1
- v11beta2
- v10beta3
- v3beta1
- v12alpha1
- v11alpha2
- foo1
- foo10

Для прикладу у розділі Зазначення кількох версій, порядок сортування версій — v1, за яким слідує v1beta1. Це змушує команду kubectl використовувати v1 як стандартну версію, якщо у наданому обʼєкті не вказано версію.

Застарівання версій

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.19 [stable]

Починаючи з версії v1.19, CustomResourceDefinition може вказувати, що певна версія ресурсу, який він визначає, є застарілою. Коли API-запити до застарілої версії цього ресурсу здійснюються, у відповідь API повертається попереджувальне повідомлення в заголовку. Попереджувальне повідомлення для кожної застарілої версії ресурсу можна налаштувати за бажанням.

Налаштоване попереджувальне повідомлення повинно вказувати застарілу API-групу, версію та тип (kind), і, якщо це можливо, вказувати, яку API-групу, версію та тип слід використовувати замість цього.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  versions:
  - name: v1alpha1
    served: true
    storage: false
    # Це вказує, що версія v1alpha1 власного ресурсу є застарілою.
    # API-запити до цієї версії отримують попереджувальний заголовок у відповіді сервера.
    deprecated: true
    # Це перевизначає стандартне попереджувальне повідомлення, яке повертається клієнтам API, що здійснюють запити до v1alpha1.
    deprecationWarning: "example.com/v1alpha1 CronTab застарілий; див. http://example.com/v1alpha1-v1 для інструкцій щодо переходу на example.com/v1 CronTab"

    schema: ...
  - name: v1beta1
    served: true
    # Це вказує, що версія v1beta1 власного ресурсу є застарілою.
    # API-запити до цієї версії отримують попереджувальний заголовок у відповіді сервера.
    # Стандартне попереджувальне повідомлення повертається для цієї версії.
    deprecated: true
    schema: ...
  - name: v1
    served: true
    storage: true
    schema: ...

# Застаріло у v1.16 на користь apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  validation: ...
  versions:
  - name: v1alpha1
    served: true
    storage: false
    # Це вказує, що версія v1alpha1 власного ресурсу є застарілою.
    # API-запити до цієї версії отримують попереджувальний заголовок у відповіді сервера.
    deprecated: true
    # Це перевизначає стандартне попереджувальне повідомлення, яке повертається клієнтам API, що здійснюють запити до v1alpha1.
    deprecationWarning: "example.com/v1alpha1 CronTab застарілий; див. http://example.com/v1alpha1-v1 для інструкцій щодо переходу на example.com/v1 CronTab"
  - name: v1beta1
    served: true
    # Це вказує, що версія v1beta1 власного ресурсу є застарілою.
    # API-запити до цієї версії отримують попереджувальний заголовок у відповіді сервера.
    # Стандартне попереджувальне повідомлення повертається для цієї версії.
    deprecated: true
  - name: v1
    served: true
    storage: true

Видалення версії

Стара версія API не може бути видалена з маніфесту CustomResourceDefinition, доки наявні збережені дані не будуть мігровані до новішої версії API для всіх кластерів, які обслуговували стару версію власного ресурсу, та поки стара версія не буде видалена зі списку status.storedVersions у CustomResourceDefinition.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
  name: crontabs.example.com
spec:
  group: example.com
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
  scope: Namespaced
  versions:
  - name: v1beta1
    # Це вказує, що версія v1beta1 власного ресурсу більше не обслуговується.
    # API-запити до цієї версії отримують помилку "не знайдено" у відповіді сервера.
    served: false
    schema: ...
  - name: v1
    served: true
    # Нова версія, що обслуговується повинна бути встановлена як версія зберігання
    storage: true
    schema: ...

Конвертація за допомогою вебхука

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.16 [stable]

Наведений вище приклад має None-конвертацію між версіями, яка лише встановлює поле apiVersion під час конвертації та не змінює решту обʼєкта. API-сервер також підтримує конвертації за допомогою вебхука, які викликають зовнішній сервіс у випадку, коли потрібна конвертація. Наприклад, коли:

  • власний ресурс запитується у версії, яка відрізняється від збереженої версії.
  • створюється спостереження (Watch) в одній версії, але змінений обʼєкт зберігається в іншій версії.
  • запит на PUT для власного ресурсу здійснюється в іншій версії, ніж версія зберігання.

Щоб охопити всі ці випадки та оптимізувати конвертацію за допомогою API-сервера, запити на конвертацію можуть містити кілька обʼєктів з метою мінімізації зовнішніх викликів. Вебхук повинен виконувати ці конвертації незалежно.

Написання сервера для конвертації за допомогою вебхука

Будь ласка, зверніться до реалізації сервера вебхука для конвертації власних ресурсів, який проходить перевірку в e2e тесті Kubernetes. Вебхук обробляє запити ConversionReview, що надсилаються API-серверами, і надсилає назад результати конвертації, загорнуті в ConversionResponse. Зверніть увагу, що запит містить список власних ресурсів, які потрібно конвертувати незалежно, не змінюючи порядок обʼєктів. Приклад сервера організований таким чином, щоб його можна було повторно використовувати для інших конвертацій. Більшість загального коду знаходиться у файлі фреймворку, залишаючи лише одну функцію для реалізації різних конвертацій.

Допустимі зміни

Вебхук для конвертації не повинен змінювати нічого в полі metadata обʼєкта, окрім labels та annotations. Спроби змінити name, UID та namespace відхиляються і призводять до помилки запиту, що спричинив конвертацію. Усі інші зміни ігноруються.

Розгортання сервера вебхука для конвертації

Документація для розгортання вебхука для конвертації аналогічна документації для прикладу сервісу вебхука для допуску. Наступні секції передбачають, що сервер вебхука для конвертації розгорнутий як сервіс з іменем example-conversion-webhook-server у просторі імен default та обслуговує трафік за шляхом /crdconvert.

Налаштування CustomResourceDefinition для використання вебхуків конвертації

Приклад None конвертації можна розширити для використання вебхука конвертації, змінивши розділ conversion в розділі spec:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # імʼя повинно відповідати полям spec нижче, і мати форму: <plural>.<group>
  name: crontabs.example.com
spec:
  # назва групи, яку використовувати для REST API: /apis/<group>/<version>
  group: example.com
  # список версій, які підтримуються цим CustomResourceDefinition
  versions:
  - name: v1beta1
    # Кожну версію можна увімкнути/вимкнути за допомогою прапорця Served.
    served: true
    # Одна і тільки одна версія повинна бути позначена як версія для зберігання.
    storage: true
    # Кожна версія може визначити свою власну схему, коли немає визначеної схеми верхнього рівня.
    schema:
      openAPIV3Schema:
        type: object
        properties:
          hostPort:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  conversion:
    # стратегія Webhook інструктує сервер API викликати зовнішній вебхук для будь-якої конвертації між власними ресурсами.
    strategy: Webhook
    # вебхук необхідний, коли стратегія - `Webhook`, і він налаштовує точку доступу вебхука для виклику сервером API.
    webhook:
      # conversionReviewVersions вказує, які версії ConversionReview розуміються/надається  перевага вебхуком.
      # Перша версія у списку, яку розуміє сервер API, надсилається до вебхуку.
      # Вебхук повинен відповісти обʼєктом ConversionReview в тій самій версії, що й отримана.
      conversionReviewVersions: ["v1","v1beta1"]
      clientConfig:
        service:
          namespace: default
          name: example-conversion-webhook-server
          path: /crdconvert
        caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  # або Namespaced, або Cluster
  scope: Namespaced
  names:
    # plural, назва ресурсу в що використовується в URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular, назва ресурсу в що буде використовуватись як аліас в CLI та у виводі
    singular: crontab
    # kind, зазвичай тип в однині в CamelCased. Ваші маніфести будуть використовувати це.
    kind: CronTab
    # shortNames, дозволяють коротше звертатися до ресурсу в CLI
    shortNames:
    - ct

# Застаріло у v1.16 на користь apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  # імʼя повинно відповідати полям spec нижче, і мати форму: <plural>.<group>
  name: crontabs.example.com
spec:
  # назва групи, яку використовувати для REST API: /apis/<group>/<version>
  group: example.com
  # обрізає поля обʼєктів, які не вказані у схемах OpenAPI нижче.
  preserveUnknownFields: false
  # список версій, які підтримуються цим CustomResourceDefinition
  versions:
  - name: v1beta1
    # Кожну версію можна включити/вимкнути за допомогою прапорця Served.
    served: true
    # Одна і тільки одна версія повинна бути позначена як версія зберігання.
    storage: true
    # Кожна версія може визначити свою власну схему, коли немає визначеної схеми верхнього рівня.
    schema:
      openAPIV3Schema:
        type: object
        properties:
          hostPort:
            type: string
  - name: v1
    served: true
    storage: false
    schema:
      openAPIV3Schema:
        type: object
        properties:
          host:
            type: string
          port:
            type: string
  conversion:
    # стратегія Webhook інструктує сервер API викликати зовнішній вебхук для будь-якої конвертації між власними ресурсами.
    strategy: Webhook
    # webhookClientConfig необхідний, коли стратегія - `Webhook`, і він налаштовує точку доступу вебхука для виклику сервером API.
    webhookClientConfig:
      service:
        namespace: default
        name: example-conversion-webhook-server
        path: /crdconvert
      caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
  # або Namespaced, або Cluster
  scope: Namespaced
  names:
    # plural, назва ресурсу в що використовується в URL: /apis/<group>/<version>/<plural>
    plural: crontabs
    # singular, назва ресурсу в що буде використовуватись як аліас в CLI та у виводі
    singular: crontab
    # kind, зазвичай тип в однині в CamelCased. Ваші маніфести будуть використовувати це.
    kind: CronTab
    # shortNames, дозволяють коротше звертатися до ресурсу в CLI
    shortNames:
    - ct

Ви можете зберегти опис CustomResourceDefinition у файлі YAML, а потім використовувати kubectl apply, щоб застосувати його.

kubectl apply -f мій-версійний-crontab-з-конвертацією.yaml

Переконайтеся, що сервіс конвертації працює, перш ніж застосовувати нові зміни.

Звʼязок з вебхуком

Після того, як сервер API визначив, що запит потрібно надіслати до вебхуку конвертації, йому потрібно знати, як звʼязатися з вебхуком. Це вказується в розділі webhookClientConfig конфігурації вебхука.

Вебхуки конвертації можуть бути викликані або через URL, або через посилання на сервіс, і можуть додатково містити власний пакет CA для перевірки TLS-зʼєднання.

URL

url вказує знаходження вебхука, у стандартній формі URL (scheme://host:port/path).

host не повинен посилатися на сервіс, що працює в кластері; використовуйте посилання на сервіс, вказавши замість цього поле service. Хост може бути вирішений через зовнішній DNS в деяких apiserver-ах (тобто kube-apiserver не може виконувати внутрішній DNS, оскільки це було б порушенням рівня). host також може бути IP-адресою.

Зверніть увагу, що використання localhost або 127.0.0.1 як host є ризикованим, якщо ви не приділяєте велику увагу тому, щоб запустити цей вебхук на всіх хостах, на яких працює apiserver, який може потребувати звернень до цього вебхука. Такі установки, швидше за все, не будуть переносними або не можуть бути легко запущені в новому кластері.

Схема повинна бути "https"; URL повинен починатися з "https://".

Спроба використання автентифікації користувача або базової автентифікації (наприклад, "user:password@") заборонена. Фрагменти ("#...") та параметри запиту ("?...") також не дозволені.

Ось приклад вебхука конвертації, налаштованого на виклик URL (і очікується, що сертифікат TLS буде перевірений за допомогою коренів довіри системи, тому не вказується caBundle):

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        url: "https://my-webhook.example.com:9443/my-webhook-path"
...

# Застаріло у v1.16 на користь apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhookClientConfig:
      url: "https://my-webhook.example.com:9443/my-webhook-path"
...

Посилання на сервіс

Розділ service всередині webhookClientConfig є посиланням на сервіс для вебхука конвертації. Якщо вебхук працює всередині кластера, то ви повинні використовувати service замість url. Простір імен та імʼя сервісу є обовʼязковими. Порт є необовʼязковим і типово дорівнює 443. Шлях є необовʼязковим і типово дорівнює "/".

Ось приклад вебхука, який налаштований на виклик сервісу на порту "1234" з шляхом "/my-path", і для перевірки TLS-зʼєднання на ServerName my-service-name.my-service-namespace.svc з використанням власного пакету CA.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      clientConfig:
        service:
          namespace: my-service-namespace
          name: my-service-name
          path: /my-path
          port: 1234
        caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
...

# Застаріло у v1.16 на користь apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhookClientConfig:
      service:
        namespace: my-service-namespace
        name: my-service-name
        path: /my-path
        port: 1234
      caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle>...tLS0K"
...

Запити та відповіді вебхуків

Запит

Вебхуки надсилають POST-запит з Content-Type: application/json, з обʼєктом API ConversionReview з API-групи apiextensions.k8s.io, який серіалізується в JSON як тіло запиту.

Вебхуки можуть вказати, які версії обʼєктів ConversionReview вони приймають, за допомогою поля conversionReviewVersions у своїй CustomResourceDefinition:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    webhook:
      conversionReviewVersions: ["v1", "v1beta1"]
      ...

Поле conversionReviewVersions є обовʼязковим при створенні custom resource definitions apiextensions.k8s.io/v1. Вебхуки повинні підтримувати принаймні одну версію ConversionReview, яку розуміє поточний та попередній API сервер.

# Застаріло у v1.16 на користь apiextensions.k8s.io/v1
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
...
spec:
  ...
  conversion:
    strategy: Webhook
    conversionReviewVersions: ["v1", "v1beta1"]
    ...

Якщо conversionReviewVersions не вказано, стандартно при створенні custom resource definitions apiextensions.k8s.io/v1beta1 використовується v1beta1.

API сервери надсилають першу версію ConversionReview у списку conversionReviewVersions, яку вони підтримують. Якщо жодна з версій у списку не підтримується API сервером, створення custom resource definition не буде дозволено. Якщо API сервер зустрічає конфігурацію вебхука конвертації, яка була створена раніше і не підтримує жодної з версій ConversionReview, які сервер може надіслати, спроби виклику вебхука завершаться невдачею.

Цей приклад показує дані, що містяться в обʼєкті ConversionReview для запиту на конверсію обʼєктів CronTab до example.com/v1:

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "request": {
    # Випадковий uid, який унікально ідентифікує цей виклик конвертації
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    
    # API група та версія, до якої повинні бути конвертовані обʼєкти
    "desiredAPIVersion": "example.com/v1",
    
    # Список обʼєктів для конвертації.
    # Може містити один або більше обʼєктів, у одній або більше версіях.
    "objects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "hostPort": "localhost:1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "hostPort": "example.com:2345"
      }
    ]
  }
}

{
  # Застаріло у v1.16 на користь apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "request": {
    # Випадковий uid, який унікально ідентифікує цей виклик конвертації
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    
    # API група та версія, до якої повинні бути конвертовані обʼєкти
    "desiredAPIVersion": "example.com/v1",
    
    # Список обʼєктів для конвертації.
    # Може містити один або більше обʼєктів, у одній або більше версіях.
    "objects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-b5da-fd6083580d66"
        },
        "hostPort": "localhost:1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1beta1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-b553-d859cedde8a0"
        },
        "hostPort": "example.com:2345"
      }
    ]
  }
}

Відповідь

Вебхуки відповідають зі статусом HTTP 200, Content-Type: application/json та тілом, що містить обʼєкт ConversionReview (у тій самій версії, у якій вони були надіслані), з заповненим розділом response, серіалізованим у JSON.

Якщо конвертація успішна, вебхук має повернути розділ response, що містить наступні поля:

  • uid, скопійований з request.uid, надісланого до вебхука
  • result, встановлений на {"status":"Success"}
  • convertedObjects, що містить всі обʼєкти з request.objects, конвертовані до request.desiredAPIVersion

Приклад мінімально успішної відповіді від вебхука:

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "response": {
    # має збігатись з <request.uid>
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",
    "result": {
      "status": "Success"
    },
    # Обʼєкти мають відповідати порядку request.objects та мати apiVersion, встановлений у <request.desiredAPIVersion>.
    # поля kind, metadata.uid, metadata.name та metadata.namespace не повинні змінюватися вебхуком.
    # поля metadata.labels та metadata.annotations можуть бути змінені вебхуком.
    # всі інші зміни полів metadata, зроблені вебхуком, ігноруються.
    "convertedObjects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-б5da-fd6083580d66"
        },
        "host": "localhost",
        "port": "1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-b575-460d-б553-d859cedde8a0"
        },
        "host": "example.com",
        "port": "2345"
      }
    ]
  }
}

{
  # Застаріло у версії v1.16 на користь apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "response": {
    # має відповідати <request.uid>
    "uid": "705ab4f5-6393-11e8-б7cc-42010a800002",
    "result": {
      "status": "Failed"
    },
    # Обʼєкти мають відповідати порядку request.objects та мати apiVersion, встановлений у <request.desiredAPIVersion>.
    # поля kind, metadata.uid, metadata.name та metadata.namespace не повинні змінюватися вебхуком.
    # поля metadata.labels та metadata.annotations можуть бути змінені вебхуком.
    # всі інші зміни полів metadata, зроблені вебхуком, ігноруються.
    "convertedObjects": [
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-04T14:03:02Z",
          "name": "local-crontab",
          "namespace": "default",
          "resourceVersion": "143",
          "uid": "3415a7fc-162b-4300-б5da-fd6083580d66"
        },
        "host": "localhost",
        "port": "1234"
      },
      {
        "kind": "CronTab",
        "apiVersion": "example.com/v1",
        "metadata": {
          "creationTimestamp": "2019-09-03T13:02:01Z",
          "name": "remote-crontab",
          "resourceVersion": "12893",
          "uid": "359a83ec-б575-460d-б553-d859cedde8a0"
        },
        "host": "example.com",
        "port": "2345"
      }
    ]
  }
}

Якщо конвертація не вдалася, вебхук має повернути розділ response, що містить наступні поля:

  • uid, скопійований з request.uid, надісланого до вебхуку
  • result, встановлений на {"status":"Failed"}

Приклад відповіді від вебхуку, що вказує на невдачу запиту конвертації, з додатковим повідомленням:

{
  "apiVersion": "apiextensions.k8s.io/v1",
  "kind": "ConversionReview",
  "response": {
    "uid": "<значення з request.uid>",
    "result": {
      "status": "Failed",
      "message": "hostPort не вдалося розділити на окремі host та port"
    }
  }
}

{
  # Застаріло у версії v1.16 на користь apiextensions.k8s.io/v1
  "apiVersion": "apiextensions.k8s.io/v1beta1",
  "kind": "ConversionReview",
  "response": {
    "uid": "<значення з request.uid>",
    "result": {
      "status": "Failed",
      "message": "hostPort не вдалося розділити на окремі host та port"
    }
  }
}

Запис, читання та оновлення обʼєктів CustomResourceDefinition з версіями

Коли обʼєкт записується, він зберігається у версії, визначеній як версія зберігання на момент запису. Якщо версія зберігання змінюється, наявні обʼєкти ніколи не конвертуються автоматично. Проте новостворені або оновлені обʼєкти записуються у новій версії зберігання. Можливо, що обʼєкт було записано у версії, яка більше не обслуговується.

Коли ви зчитуєте обʼєкт, ви вказуєте версію як частину шляху. Ви можете запитати обʼєкт у будь-якій версії, яка наразі обслуговується. Якщо ви вказуєте версію, яка відрізняється від версії, у якій зберігається обʼєкт, Kubernetes повертає вам обʼєкт у запитуваній версії, але збережений обʼєкт не змінюється на диску.

Що відбувається з обʼєктом, який повертається під час обслуговування запиту на читання, залежить від того, що вказано у spec.conversion CRD:

  • Якщо вказано стандартне значення стратегії None, єдині зміни обʼєкта — це зміна рядка apiVersion і, можливо, обрізання невідомих полів (залежно від конфігурації). Зазначимо, що це навряд чи призведе до хороших результатів, якщо схеми відрізняються між версіями зберігання та запитуваною версією. Зокрема, не слід використовувати цю стратегію, якщо ті самі дані представлені у різних полях між версіями.
  • Якщо вказано конвретацію за допомогою вебхуку, тоді цей механізм контролює конверсію.

Якщо ви оновлюєте існуючий обʼєкт, він перезаписується у версії, яка наразі є версією зберігання. Це єдиний спосіб, за допомогою якого обʼєкти можуть змінюватися з однієї версії на іншу.

Щоб проілюструвати це, розглянемо наступну гіпотетичну послідовність подій:

  1. Версія зберігання — v1beta1. Ви створюєте обʼєкт. Він зберігається у версії v1beta1.
  2. Ви додаєте версію v1 до вашого CustomResourceDefinition і призначаєте її версією зберігання. Схеми для v1 та v1beta1 є ідентичними, що зазвичай має місце під час підвищення API до стабільного стану в екосистемі Kubernetes.
  3. Ви читаєте свій обʼєкт у версії v1beta1, потім читаєте обʼєкт знову у версії v1. Обидва отримані обʼєкти є ідентичними, за винятком поля apiVersion.
  4. Ви створюєте новий обʼєкт. Він зберігається у версії v1. Тепер у вас є два обʼєкти: один у версії v1beta1, інший у версії v1.
  5. Ви оновлюєте перший обʼєкт. Тепер він зберігається у версії v1, оскільки це поточна версія зберігання.

Попередні версії зберігання

API сервер записує кожну версію, яка коли-небудь була позначена як версія зберігання у полі статусу storedVersions. Обʼєкти можуть бути збережені у будь-якій версії, яка коли-небудь була визначена як версія зберігання. Ніякі обʼєкти не можуть існувати у зберіганні у версії, яка ніколи не була версією зберігання.

Оновлення існуючих обʼєктів до нової версії зберігання

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

Варіант 1: Використання Storage Version Migrator

  1. Запустіть Storage Version Migrator.
  2. Видаліть стару версію з поля status.storedVersions CustomResourceDefinition.

Варіант 2: Ручне оновлення наявних обʼєктів до нової версії зберігання

Наведено приклад процедури оновлення з v1beta1 до v1.

  1. Встановіть v1 як версію зберігання у файлі CustomResourceDefinition та застосуйте це за допомогою kubectl. Тепер storedVersions містить v1beta1, v1.
  2. Напишіть процедуру оновлення для отримання всіх наявних обʼєктів і запишіть їх з тим самим вмістом. Це змушує бекенд записувати обʼєкти у поточній версії зберігання, якою є v1.
  3. Видаліть v1beta1 з поля status.storedVersions CustomResourceDefinition.

11.2 - Налаштування шару агрегації

Налаштування шару агрегації дозволяє розширити apiserver Kubernetes додатковими API, які не є частиною основних API Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Потоки автентифікації

На відміну від Custom Resource Definitions (CRDs), API агрегації включає ще один сервер, ваш apiserver розширення, крім стандартного apiserver Kubernetes. Kubernetes apiserver повинен взаємодіяти з вашим apiserver розширення, а ваш apiserver розширення повинен взаємодіяти з Kubernetes apiserver. Щоб ця взаємодія була захищеною, Kubernetes apiserver використовує x509 сертифікати для автентифікації себе перед apiserver розширення.

У цьому розділі описано, як працюють потоки автентифікації та авторизації та як їх налаштувати.

Основний потік виглядає наступним чином:

  1. Kubernetes apiserver: автентифікація користувача, що запитує, та авторизація його прав на запитаний API шлях.
  2. Kubernetes apiserver: проксіювання запиту до apiserverʼа розширення.
  3. apiserver розширення: автентифікація запиту від Kubernetes apiserver.
  4. apiserver розширення: авторизація запиту від початкового користувача.
  5. apiserver розширення: виконання.

У решті цього розділу описуються ці кроки детально.

Потік можна побачити на наступній діаграмі.

aggregation auth flows

Джерело для вищезазначених потоків можна знайти у вихідному коді цього документа.

Автентифікація та авторизація Kubernetes Apiserver

Запит до API шляху, який обслуговується apiserver розширення, починається так само, як і всі API запити: з комунікації з Kubernetes apiserver. Цей шлях вже був зареєстрований з Kubernetes apiserver apiserver розширення.

Користувач взаємодіє з Kubernetes apiserver, запитуючи доступ до шляху. Kubernetes apiserver використовує стандартну автентифікацію та авторизацію, налаштовану в Kubernetes apiserver, для автентифікації користувача та авторизації доступу до конкретного шляху.

Для загального огляду автентифікації в кластері Kubernetes див. "Автентифікація в кластері". Для загального огляду авторизації доступу до ресурсів кластера Kubernetes див. "Огляд авторизації".

Все до цього моменту було стандартними запитами API Kubernetes, автентифікацією та авторизацією.

Kubernetes apiserver тепер готовий відправити запит до apiserverʼа розширення.

Проксіювання запиту Kubernetes Apiserver

Kubernetes apiserver тепер відправить або проксує запит до apiserverʼа розширення, який зареєстрований для обробки цього запиту. Для цього потрібно знати кілька речей:

  1. Як Kubernetes apiserver повинен автентифікуватися у apiserver розширення, інформуючи його, що запит, який надходить мережею, надходить від дійсного Kubernetes apiserver?
  2. Як Kubernetes apiserver повинен інформувати apiserver розширення про імʼя користувача та групу, з якими оригінальний запит був автентифікований?

Щоб забезпечити ці два аспекти, ви повинні налаштувати Kubernetes apiserver, використовуючи кілька прапорців.

Автентифікація клієнта Kubernetes Apiserver

Kubernetes apiserver підключається до apiserverʼа розширення через TLS, автентифікується за допомогою клієнтського сертифіката. Ви повинні надати наступне для Kubernetes apiserver при запуску, використовуючи вказані параметри:

  • файл приватного ключа через --proxy-client-key-file
  • підписаний файл клієнтського сертифіката через --proxy-client-cert-file
  • сертифікат CA, який підписав файл клієнтського сертифіката через --requestheader-client-ca-file
  • дійсні значення загального імені (CN) в підписаному клієнтському сертифікаті через --requestheader-allowed-names

Kubernetes apiserver використовуватиме файли, вказані --proxy-client-*-file, щоб автентифікуватися у apiserver розширення. Щоб запит був вважався дійсним відповідно до стандартів apiserverʼа розширення, мають виконуватися наступні умови:

  1. Підключення має бути виконане за допомогою клієнтського сертифіката, який підписаний CA, сертифікат якого знаходиться в --requestheader-client-ca-file.
  2. Підключення має бути виконане за допомогою клієнтського сертифіката, CN якого знаходиться в одному з тих, що перелічені в --requestheader-allowed-names.

Після запуску з цими параметрами Kubernetes apiserver:

  1. Використовуватиме їх для автентифікації у apiserver розширення.
  2. Створить ConfigMap у просторі імен kube-system з назвою extension-apiserver-authentication, в який він помістить сертифікат CA та дозволені CN. Ці дані можна отримати apiserverʼа розширенняs для перевірки запитів.

Зверніть увагу, що той самий клієнтський сертифікат використовується Kubernetes apiserver для автентифікації для усіх apiserverʼів розширень. Він не створює окремий клієнтський сертифікат для кожного apiserverʼа розширення, а лише один, щоб автентифікуватися як Kubernetes apiserver. Цей самий сертифікат використовується для всіх запитів apiserverʼа розширення.

Імʼя користувача та група оригінального запиту

Коли Kubernetes apiserver проксіює запит до apiserverʼа розширення, він повідомляє apiserver розширення імʼя користувача та групу, з якими успішно був автентифікований початковий запит. Він надає це у http заголовках свого проксі-запиту. Ви повинні повідомити Kubernetes apiserver назви заголовків, які слід використовувати.

  • заголовок, в якому зберігати імʼя користувача через --requestheader-username-headers
  • заголовок, в якому зберігати групу через --requestheader-group-headers
  • префікс для всіх додаткових заголовків через --requestheader-extra-headers-prefix

Ці назви заголовків також поміщаються в ConfigMap extension-apiserver-authentication, так що їх можна отримати та використати apiserverʼа розширенняs.

Apiserver розширення автентифікує запит

apiserver розширення, отримавши проксі-запит від Kubernetes apiserver, повинен перевірити, що запит дійсно надійшов від дійсного автентифікуючого проксі, роль якого виконує Kubernetes apiserver. apiserver розширення перевіряє його за допомогою:

  1. Отримання наступного з ConfigMap в kube-system, як описано вище:
    • Сертифікат CA клієнта
    • Список дозволених імен (CN)
    • Назви заголовків для імені користувача, групи та додаткової інформації
  2. Перевірте, що TLS-зʼєднання було автентифіковано за допомогою сертифіката клієнта, який:
    • Був підписаний CA, чий сертифікат відповідає отриманому сертифікату CA.
    • Має CN у списку дозволених CN, якщо список не порожній, в іншому випадку дозволяються всі CN.
    • Витягу імені користувача та групи з відповідних заголовків.

Якщо вище зазначене пройшло, тоді запит є дійсним проксійним запитом від законного проксі автентифікації, у цьому випадку — apiserver Kubernetes.

Зверніть увагу, що відповідальність за надання вищезазначеного лежить на реалізації apiserverʼа розширення. Більшість роблять це стандартно, використовуючи пакет k8s.io/apiserver/. Інші можуть надати опції для зміни цього за допомогою параметрів командного рядка.

Для того, щоб мати дозвіл на отримання конфігураційного файлу, apiserver розширення потребує відповідної ролі. Існує стандартна роль з назвою extension-apiserver-authentication-reader в просторі імен kube-system, яка може бути призначена.

Apiserver розширення авторизує запит

Тепер apiserver розширення може перевірити, що користувач/група, отримані з заголовків, мають дозвіл на виконання даного запиту. Він робить це, надсилаючи стандартний запит SubjectAccessReview до apiserver Kubernetes.

Для того, щоб apiserver розширення мав право на надсилання запиту SubjectAccessReview до apiserver Kubernetes, йому потрібні відповідні дозволи. Kubernetes включає стандартну ClusterRole з назвою system:auth-delegator, яка має необхідні дозволи. Її можна надати обліковому запису служби apiserverʼа розширенняа.

Виконання Apiserver розширення

Якщо перевірка SubjectAccessReview пройде успішно, apiserver розширення виконує запит.

Увімкнення прапорців Apiserver Kubernetes

Увімкніть агрегаційний шар за допомогою наступних прапорців kube-apiserver. Вони можуть вже бути налаштовані вашим постачальником.

--requestheader-client-ca-file=<шлях до сертифікату CA агрегатора>
--requestheader-allowed-names=front-proxy-client
--requestheader-extra-headers-prefix=X-Remote-Extra-
--requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User
--proxy-client-cert-file=<шлях до сертифікату проксі-клієнта агрегатора>
--proxy-client-key-file=<шлях до ключа проксі-клієнта агрегатора>

Повторне використання та конфлікти сертифікатів CA

У Kubernetes apiserver є два параметри CA клієнта:

  • --client-ca-file
  • --requestheader-client-ca-file

Кожен з цих параметрів працює незалежно і може конфліктувати один з одним, якщо їх не використовувати належним чином.

  • --client-ca-file: Коли запит надходить на Kubernetes apiserver, якщо цей параметр увімкнено, Kubernetes apiserver перевіряє сертифікат запиту. Якщо він підписаний одним з сертифікатів CA в файлі, на який вказує --client-ca-file, то запит вважається законним, а користувач — значенням загального імені CN=, а група — організацією O=. Див. документацію з автентифікації TLS.
  • --requestheader-client-ca-file: Коли запит надходить на Kubernetes apiserver, якщо цей параметр увімкнено, Kubernetes apiserver перевіряє сертифікат запиту. Якщо він підписаний одним із сертифікатів CA у файлі, на який вказує --requestheader-client-ca-file, то запит вважається потенційно законним. Потім Kubernetes apiserver перевіряє, чи є загальне імʼя CN= одним з імен у списку, наданому параметром --requestheader-allowed-names. Якщо імʼя дозволене, запит схвалюється; якщо ні, запит відхиляється.

Якщо обидва параметри --client-ca-file та --requestheader-client-ca-file надані, то спочатку запит перевіряє CA --requestheader-client-ca-file, а потім --client-ca-file. Зазвичай для кожного з цих параметрів використовуються різні сертифікати CA, або кореневі CA, або проміжні CA; звичайні клієнтські запити відповідають --client-ca-file, тоді як агрегаційні запити відповідають --requestheader-client-ca-file. Однак, якщо обидва використовують той самий CA, то звичайні клієнтські запити, які зазвичай пройшли б через --client-ca-file, не пройдуть, оскільки CA буде відповідати CA в --requestheader-client-ca-file, але загальне імʼя CN= не буде відповідати одному з припустимих загальних імен в --requestheader-allowed-names. Це може призвести до того, що kublete та інші компоненти панелі управління, так само як і інші клієнти, не зможуть автентифікуватись на Kubernetes apiserver.

З цієї причини використовуйте різні сертифікати CA для опції --client-ca-file, щоб авторизувати компоненти панелі управління та кінцевих користувачів, і опції --requestheader-client-ca-file, щоб авторизувати запити apiserverʼа агрегації.

Якщо ви не запускаєте kube-proxy на хості, на якому працює API-сервер, вам потрібно впевнитися, що система ввімкнена з наступним прапорцем kube-apiserver:

--enable-aggregator-routing=true

Реєстрація обʼєктів APIService

Ви можете динамічно налаштувати, які клієнтські запити будуть проксійовані до apiserverʼа розширення. Нижче наведено приклад реєстрації:


apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  name: <імʼя обʼєкта реєстрації>
spec:
  group: <назва групи API, яку обслуговує цей apiserver розширення>
  version: <версія API, яку обслуговує цей apiserver розширення>
  groupPriorityMinimum: <пріоритет цього APIService для цієї групи, див. документацію API>
  versionPriority: <пріоритет упорядкування цієї версії у межах групи, див. документацію API>
  service:
    namespace: <простір імен сервісу apiserverʼа розширення>
    name: <імʼя сервісу apiserverʼа розширення>
  caBundle: <pem-кодований ca-сертифікат, який підписує сертифікат сервера, який використовується вебзапитом>

Імʼя обʼєкта APIService повинно бути дійсним імʼям сегмента шляху.

Звертання до apiserverʼа розширення

Після того, як Kubernetes apiserver вирішив, що запит потрібно надіслати до apiserverʼа розширення, він повинен знати, як з ним звʼязатися.

Блок service — це посилання на сервіс для apiserverʼа розширення. Простір імен та імʼя сервісу обовʼязкові. Порт є необовʼязковим і типово дорівнює 443.

Ось приклад apiserverʼа розширення, який налаштований для виклику на порті "1234" та перевірки зʼєднання TLS проти ServerName my-service-name.my-service-namespace.svc, використовуючи власний пакет CA.

apiVersion: apiregistration.k8s.io/v1
kind: APIService
...
spec:
  ...
  service:
    namespace: my-service-namespace
    name: my-service-name
    port: 1234
  caBundle: "Ci0tLS0tQk...<base64-кодований PEM пакет>...tLS0K"
...

Що далі

11.3 - Налаштування API сервера розширення

Налаштування API сервера розширення для роботи з шаром агрегації дозволяє розширювати apiserver Kubernetes додатковими API, які не є частиною основних API Kubernetes.

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

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

Для перевірки версії введіть kubectl version.

Налаштування API сервера розширення для роботи з шаром агрегації

Наступні кроки описують налаштування apiserverʼа розширення на високому рівні. Ці кроки застосовуються незалежно від того, чи використовуєте ви YAML конфігурації, чи API. Робиться спроба конкретно визначити будь-які відмінності між цими двома підходами. Для конкретного прикладу їх реалізації за допомогою YAML конфігурацій, ви можете ознайомитися з sample-apiserver у репозиторії Kubernetes.

Альтернативно, ви можете використовувати наявне стороннє рішення, таке як apiserver-builder, яке має згенерувати кістяк та автоматизувати всі наступні кроки для вас.

  1. Переконайтеся, що APIService API увімкнено (перевірте --runtime-config). Воно має бути типово увімкнене, якщо воно не було свідомо вимкнено у вашому кластері.
  2. Можливо, вам потрібно створити правило RBAC, яке дозволяє додавати обʼєкти APIService, або попросити адміністратора вашого кластера створити його. (Оскільки розширення API впливають на весь кластер, не рекомендується проводити тестування/розробку/налагодження розширення API у робочому кластері.)
  3. Створіть простір імен Kubernetes, у якому ви хочете запустити свій api-сервер розширення.
  4. Отримайте сертифікат CA, який буде використовуватися для підпису сертифіката сервера, що використовує api-сервер розширення для HTTPS.
  5. Створіть серверний сертифікат/ключ для api-сервера, який буде використовуватися для HTTPS. Цей сертифікат повинен бути підписаний вище згаданим CA. Він також повинен мати CN у вигляді імені Kube DNS. Це імʼя формується на основі сервісу Kubernetes і має вигляд <service name>.<service name namespace>.svc.
  6. Створіть Secret Kubernetes з серверним сертифікатом/ключем у вашому просторі імен.
  7. Створіть Deployment Kubernetes для api-сервера розширення і переконайтеся, що ви завантажуєте Secret як том. Він повинен містити посилання на робочий образ вашого api-сервера розширення. Deployment також повинен бути у вашому просторі імен.
  8. Переконайтеся, що ваш api-сервер розширення завантажує ці сертифікати з того тому і що вони використовуються в процесі рукостискання (handshake) HTTPS.
  9. Створіть службовий обліковий запис (service account) Kubernetes у вашому просторі імен.
  10. Створіть кластерну роль Kubernetes для операцій, які ви хочете дозволити над вашими ресурсами.
  11. Створіть привʼязку кластерної ролі до службового облікового запису (service account) у вашому просторі імен до створеної кластерної ролі.
  12. Створіть привʼязку кластерної ролі до службового облікового запису (service account) у вашому просторі імен до кластерної ролі system:auth-delegator для делегування рішень щодо автентифікації на основному API серверу Kubernetes.
  13. Створіть привʼязку службового облікового запису (service account) у вашому просторі імен до ролі extension-apiserver-authentication-reader. Це дозволяє вашому api-серверу розширення отримувати доступ до ConfigMap extension-apiserver-authentication.
  14. Створіть apiservice Kubernetes. CA сертифікат повинен бути закодований в base64, не містити позбавлений нових рядків і використаний як spec.caBundle в apiservice. Він не повинен бути привʼязаний до простору імен. Якщо ви використовуєте kube-aggregator API, передайте лише PEM-кодований CA bundle, оскільки кодування в base64 виконується за вас.
  15. Використовуйте kubectl для отримання вашого ресурсу. Під час запуску kubectl повинен повернути "No resources found.". Це повідомлення вказує на те, що все спрацювало, але наразі у вас немає створених обʼєктів цього типу ресурсу.

Що далі

11.4 - Налаштування кількох планувальників

Kubernetes постачається зі стандартним планувальником. Якщо стандартний планувальник не підходить для ваших потреб, ви можете реалізувати власний. До того, ви можете одночасно запускати кілька планувальників поряд зі стандартним планувальником і вказувати Kubernetes, який планувальник використовувати для кожного з ваших Podʼів. Тепер навчимося запускати кілька планувальників в Kubernetes на прикладі.

Детальний опис того, як реалізувати планувальник, виходить за рамки цього документа. Будь ласка, зверніться до реалізації kube-scheduler в pkg/scheduler в теці вихідних кодів Kubernetes для канонічного прикладу.

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

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

Для перевірки версії введіть kubectl version.

Упакування планувальника

Упакуйте ваш планувальник у контейнерний образ. Для цілей цього прикладу ви можете використовувати стандартний планувальник (kube-scheduler) як ваш другий планувальник. Клонуйте вихідний код Kubernetes з GitHub і зберіть планувальник з сирців.

git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes
make

Створіть контейнерний образ, що містить виконавчий файл kube-scheduler. Ось Dockerfile для створення образу:

FROM busybox
ADD ./_output/local/bin/linux/amd64/kube-scheduler /usr/local/bin/kube-scheduler

Збережіть файл як Dockerfile, зберіть образ і завантажте його до реєстру. У цьому прикладі образ завантажується до Google Container Registry (GCR). Для отримання додаткової інформації, будь ласка, прочитайте документацію GCR. Як альтернативу ви також можете використовувати docker hub. Для отримання додаткової інформації зверніться до документації docker hub.

docker build -t gcr.io/my-gcp-project/my-kube-scheduler:1.0 .     # Назва образу та репозиторію
gcloud docker -- push gcr.io/my-gcp-project/my-kube-scheduler:1.0 # тут є лише прикладом

Визначення розгортання Kubernetes для планувальника

Тепер, коли ви маєте ваш планувальник у контейнерному образі, створіть конфігурацію Podʼа для нього і запустіть його у вашому кластері Kubernetes. Але замість того, щоб створювати Pod безпосередньо в кластері, ви можете використовувати Deployment для цього прикладу. Deployment керує Replica Set, який, своєю чергою, керує Podʼами, забезпечуючи стійкість планувальника до збоїв. Ось конфігурація розгортання. Збережіть її як my-scheduler.yaml:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-scheduler
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-kube-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-volume-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:volume-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-scheduler-extension-apiserver-authentication-reader
  namespace: kube-system
roleRef:
  kind: Role
  name: extension-apiserver-authentication-reader
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-scheduler-config
  namespace: kube-system
data:
  my-scheduler-config.yaml: |
    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    profiles:
      - schedulerName: my-scheduler
    leaderElection:
      leaderElect: false    
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    component: scheduler
    tier: control-plane
  name: my-scheduler
  namespace: kube-system
spec:
  selector:
    matchLabels:
      component: scheduler
      tier: control-plane
  replicas: 1
  template:
    metadata:
      labels:
        component: scheduler
        tier: control-plane
        version: second
    spec:
      serviceAccountName: my-scheduler
      containers:
      - command:
        - /usr/local/bin/kube-scheduler
        - --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml
        image: gcr.io/my-gcp-project/my-kube-scheduler:1.0
        livenessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
          initialDelaySeconds: 15
        name: kube-second-scheduler
        readinessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
        resources:
          requests:
            cpu: '0.1'
        securityContext:
          privileged: false
        volumeMounts:
          - name: config-volume
            mountPath: /etc/kubernetes/my-scheduler
      hostNetwork: false
      hostPID: false
      volumes:
        - name: config-volume
          configMap:
            name: my-scheduler-config

У наведеному вище маніфесті ви використовуєте KubeSchedulerConfiguration для налаштування поведінки вашої реалізації планувальника. Ця конфігурація була передана kube-scheduler під час ініціалізації за допомогою параметра --config. Конфігураційний файл зберігається у ConfigMap my-scheduler-config. Pod Deployment my-scheduler монтує ConfigMap my-scheduler-config як том.

У вищезгаданій конфігурації планувальника ваша реалізація планувальника представлена за допомогою KubeSchedulerProfile.

Також зверніть увагу, що ви створюєте окремий службовий обліковий запис my-scheduler і привʼязуєте до нього кластерну роль system:kube-scheduler, щоб він міг отримати ті ж привілеї, що й kube-scheduler.

Будь ласка, зверніться до документації kube-scheduler для детального опису інших параметрів командного рядка та довідкової інформації щодо конфігурації планувальника для детального опису інших налаштовуваних конфігурацій kube-scheduler.

Запуск другого планувальника в кластері

Щоб запустити ваш планувальник у кластері Kubernetes, створіть Deployment вказаний у конфігурації вище у кластері Kubernetes:

kubectl create -f my-scheduler.yaml

Переконайтеся, що Pod планувальника працює:

kubectl get pods --namespace=kube-system
NAME                                           READY     STATUS    RESTARTS   AGE
....
my-scheduler-lnf4s-4744f                       1/1       Running   0          2m
...

У цьому списку ви повинні побачити Pod "Running" my-scheduler, на додачу до стандартного планувальника kube-scheduler.

Увімкнення обрання лідера

Щоб запустити кілька планувальників з увімкненим обранням лідера, ви повинні зробити наступне:

Оновіть наступні поля для KubeSchedulerConfiguration у ConfigMap my-scheduler-config у вашому YAML файлі:

  • leaderElection.leaderElect до true
  • leaderElection.resourceNamespace до <lock-object-namespace>
  • leaderElection.resourceName до <lock-object-name>

Якщо у вашому кластері увімкнено RBAC, ви повинні оновити кластерну роль system:kube-scheduler. Додайте імʼя вашого планувальника до імен ресурсів у правилі, яке застосовується до ресурсів endpoints та leases, як у наступному прикладі:

kubectl edit clusterrole system:kube-scheduler
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-scheduler
rules:
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs:
      - create
  - apiGroups:
      - coordination.k8s.io
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - leases
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - endpoints
    verbs:
      - delete
      - get
      - patch
      - update

Вказання планувальників для Podʼів

Тепер, коли ваш другий планувальник працює, створіть кілька Podʼів і вкажіть, щоб вони планувалися або стандартним планувальником, або тим, який ви розгорнули. Щоб запланувати Pod за допомогою конкретного планувальника, вкажіть імʼя планувальника у специфікації Podʼа. Розгляньмо три приклади.

  • Специфікація Podʼа без вказаного імені планувальника

    apiVersion: v1
    kind: Pod
    metadata:
      name: no-annotation
      labels:
        name: multischeduler-example
    spec:
      containers:
      - name: pod-with-no-annotation-container
        image: registry.k8s.io/pause:2.0

    Коли імʼя планувальника не вказано, Pod автоматично планується за допомогою стандартного планувальника.

    Збережіть цей файл як pod1.yaml і надішліть його до кластера Kubernetes.

    kubectl create -f pod1.yaml
    
  • Специфікація Podʼа з default-scheduler

    apiVersion: v1
    kind: Pod
    metadata:
      name: annotation-default-scheduler
      labels:
        name: multischeduler-example
    spec:
      schedulerName: default-scheduler
      containers:
      - name: pod-with-default-annotation-container
        image: registry.k8s.io/pause:2.0
    

    Планувальник вказується шляхом надання його імені як значення для spec.schedulerName. У цьому випадку ми надаємо імʼя стандартного планувальника, яке є default-scheduler.

    Збережіть цей файл як pod2.yaml і надішліть його до кластера Kubernetes.

    kubectl create -f pod2.yaml
    
  • Специфікація Podʼа з my-scheduler

    apiVersion: v1
    kind: Pod
    metadata:
      name: annotation-second-scheduler
      labels:
        name: multischeduler-example
    spec:
      schedulerName: my-scheduler
      containers:
      - name: pod-with-second-annotation-container
        image: registry.k8s.io/pause:2.0
    

    У цьому випадку ми вказуємо, що цей Pod має бути запланований за допомогою планувальника, який ми розгорнули — my-scheduler. Зверніть увагу, що значення spec.schedulerName повинно відповідати імені, яке було надано планувальнику в полі schedulerName профілю KubeSchedulerProfile.

    Збережіть цей файл як pod3.yaml і надішліть його до кластера Kubernetes.

    kubectl create -f pod3.yaml
    

    Переконайтеся, що всі три Podʼи працюють.

    kubectl get pods
    

Перевірка, що Podʼи були заплановані за допомогою бажаних планувальників

Для спрощення роботи з цими прикладами ми не перевірили, що Podʼи дійсно були заплановані за допомогою бажаних планувальників. Ми можемо перевірити це, змінивши порядок подання конфігурацій Podʼів і розгортання вище. Якщо ми передамо всі конфігурації Podʼів до кластера Kubernetes перед передачею конфігурації розгортання планувальника, ми побачимо, що Pod annotation-second-scheduler залишається в стані "Pending" назавжди, тоді як інші два Podʼи заплановані. Після надсилання конфігурації розгортання планувальника і запуску нашого нового планувальника, Pod annotation-second-scheduler також запланується.

Як альтернативу, ви можете переглянути записи "Scheduled" у лозі подій, щоб перевірити, що Podʼи були заплановані бажаними планувальниками.

kubectl get events

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

11.5 - Використання HTTP-проксі для доступу до Kubernetes API

Ця сторінка показує, як використовувати HTTP-проксі для доступу до Kubernetes API.

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

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

Для перевірки версії введіть kubectl version.

Якщо у вас ще немає застосунку, що працює у вашому кластері, запустіть застосунок Hello world, скориставшись:

kubectl create deployment hello-app --image=gcr.io/google-samples/hello-app:2.0 --port=8080

Використання kubectl для запуску проксі-сервера

Ця команда запускає проксі до сервера Kubernetes API:

kubectl proxy --port=8080

Дослідження Kubernetes API

Коли проксі-сервер працює, ви можете досліджувати API за допомогою curl, wget або оглядача.

Отримання версій API:

curl http://localhost:8080/api/

Вихідні дані повинні виглядати приблизно так:

{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.0.2.15:8443"
    }
  ]
}

Отримання списку Podʼів:

curl http://localhost:8080/api/v1/namespaces/default/pods

Вихідні дані повинні виглядати приблизно так:

{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "33074"
  },
  "items": [
    {
      "metadata": {
        "name": "kubernetes-bootcamp-2321272333-ix8pt",
        "generateName": "kubernetes-bootcamp-2321272333-",
        "namespace": "default",
        "uid": "ba21457c-6b1d-11e6-85f7-1ef9f1dab92b",
        "resourceVersion": "33003",
        "creationTimestamp": "2016-08-25T23:43:30Z",
        "labels": {
          "pod-template-hash": "2321272333",
          "run": "kubernetes-bootcamp"
        },
        ...

Що далі

Дізнайтеся більше про kubectl proxy.

11.6 - Використання SOCKS5-проксі для доступу до Kubernetes API

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.24 [stable]

Ця сторінка показує, як використовувати SOCKS5-проксі для доступу до API віддаленого кластера Kubernetes. Це корисно, коли кластер, до якого ви хочете отримати доступ, не відкриває свій API безпосередньо в інтернет.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.24. Для перевірки версії введіть kubectl version.

Вам потрібне програмне забезпечення для SSH-клієнта (інструмент ssh) і сервіс SSH, що працює на віддаленому сервері. Ви повинні мати можливість увійти до сервісу SSH на віддаленому сервері.

Контекст завдання

На схемі 1 представлено, що ви збираєтесь досягти в цьому завданні.

  • У вас є компʼютер-клієнт, який називається локальним у подальших кроках, з якого ви будете створювати запити для спілкування з Kubernetes API.
  • Сервер Kubernetes/API розміщений на віддаленому сервері.
  • Ви будете використовувати програмне забезпечення SSH-клієнта і сервера для створення безпечного SOCKS5-тунелю між локальним і віддаленим сервером. HTTPS-трафік між клієнтом і Kubernetes API буде проходити через SOCKS5-тунель, який сам тунелюється через SSH.

graph LR; subgraph local[Локальний клієнтський компʼютер] client([клієнт])-. локальний
трафік .-> local_ssh[Локальний SSH
SOCKS5 проксі]; end local_ssh[SSH
SOCKS5
проксі]-- SSH-тунель -->sshd subgraph remote[Віддалений сервер] sshd[SSH
сервер]-- локальний трафік -->service1; end client([клієнт])-. проксійований HTTPS-трафік
проходить через проксі .->service1[Kubernetes API]; classDef plain fill:#ddd,stroke:#fff,stroke-width:4px,color:#000; classDef k8s fill:#326ce5,stroke:#fff,stroke-width:4px,color:#fff; classDef cluster fill:#fff,stroke:#bbb,stroke-width:2px,color:#326ce5; class ingress,service1,service2,pod1,pod2,pod3,pod4 k8s; class client plain; class cluster cluster;
Схема 1. Компоненти уроку про SOCKS5

Використання ssh для створення SOCKS5-проксі

Наступна команда запускає SOCKS5-проксі між вашим клієнтським компʼютером і віддаленим SOCKS-сервером:

# SSH-тунель продовжує працювати у фоновому режимі після виконання цієї команди
ssh -D 1080 -q -N username@kubernetes-remote-server.example

SOCKS5-проксі дозволяє вам підключатися до сервера API вашого кластера на основі наступної конфігурації:

  • -D 1080: відкриває SOCKS-проксі на локальному порту :1080.
  • -q: тихий режим. Приглушує більшість попереджень і діагностичних повідомлень.
  • -N: не виконувати віддалені команди. Корисно для простого пересилання портів.
  • username@kubernetes-remote-server.example: віддалений SSH-сервер, за яким працює кластер Kubernetes (наприклад, bastion host).

Конфігурація клієнта

Щоб отримати доступ до сервера Kubernetes API через проксі, вам потрібно вказати kubectl надсилати запити через створений раніше SOCKS проксі. Зробіть це або налаштуванням відповідної змінної середовища, або через атрибут proxy-url у файлі kubeconfig. Використання змінної середовища:

export HTTPS_PROXY=socks5://localhost:1080

Щоб завжди використовувати це налаштування в конкретному контексті kubectl, вкажіть атрибут proxy-url у відповідному записі cluster у файлі ~/.kube/config. Наприклад:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LRMEMMW2 # скорочено для читабельності
    server: https://<API_SERVER_IP_ADDRESS>:6443  # сервер "Kubernetes API", тобто IP-адреса kubernetes-remote-server.example
    proxy-url: socks5://localhost:1080   # "SSH SOCKS5 проксі" на діаграмі вище
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
  user:
    client-certificate-data: LS0tLS1CR== # скорочено для читабельності
    client-key-data: LS0tLS1CRUdJT=      # скорочено для читабельності

Після створення тунелю через команду ssh, зазначену вище, і визначення змінної середовища або атрибуту proxy-url, ви можете взаємодіяти з вашим кластером через цей проксі. Наприклад:

kubectl get pods
NAMESPACE     NAME                                     READY   STATUS      RESTARTS   AGE
kube-system   coredns-85cb69466-klwq8                  1/1     Running     0          5m46s

Очищення

Зупиніть процес пересилання портів ssh, натиснувши CTRL+C у терміналі, де він працює.

Введіть unset https_proxy у терміналі, щоб припинити пересилання HTTP-трафіку через проксі.

Додаткові матеріали

11.7 - Налаштування служби Konnectivity

Служба Konnectivity надає проксі на рівні TCP для комунікації між панеллю управління та кластером.

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

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

Налаштування служби Konnectivity

Для виконання наступних кроків потрібна конфігурація egress, наприклад:

apiVersion: apiserver.k8s.io/v1beta1
kind: EgressSelectorConfiguration
egressSelections:
# Оскільки ми хочемо контролювати вихідний трафік з кластеру, ми використовуємо
# "cluster" як назву. Інші підтримувані значення: "etcd" та "controlplane".
- name: cluster
  connection:
    # Це керує протоколом між API-сервером та сервером Konnectivity.
    # Підтримувані значення: "GRPC" та "HTTPConnect". Між двома режимами
    # немає відмінностей для кінцевого користувача. Вам потрібно встановити
    # сервер Konnectivity в тому ж режимі.
    proxyProtocol: GRPC
    transport:
      # Це керує транспортом, який використовує API-сервер для спілкування з
      # сервером Konnectivity. Якщо сервер Konnectivity розташований на тому ж
      # компʼютері, рекомендується використовувати UDS. Вам потрібно налаштувати
      # сервер Konnectivity на прослуховування того самого UDS-сокету.
      # Інший підтримуваний транспорт - "tcp". Вам потрібно налаштувати TLS-конфігурацію
      # для захисту TCP-транспорту.
      uds:
        udsName: /etc/kubernetes/konnectivity-server/konnectivity-server.socket

Вам потрібно налаштувати API-сервер для використання служби Konnectivity та направлення мережевого трафіку на вузли кластера:

  1. Переконайтеся, що функція Проєкція токенів службових облікових записів увімкенна у вашому кластері. Вона є типово увімкненою з версії Kubernetes v1.20.

  2. Створіть файл конфігурації egress, наприклад admin/konnectivity/egress-selector-configuration.yaml.

  3. Встановіть прапорець --egress-selector-config-file API-сервера на шлях до файлу конфігурації виходу API-сервера.

  4. Якщо ви використовуєте з'ʼєднання UDS, додайте конфігурацію томів до kube-apiserver:

    spec:
      containers:
        volumeMounts:
        - name: konnectivity-uds
          mountPath: /etc/kubernetes/konnectivity-server
          readOnly: false
      volumes:
      - name: konnectivity-uds
        hostPath:
          path: /etc/kubernetes/konnectivity-server
          type: DirectoryOrCreate
    

Згенеруйте або отримайте сертифікат та kubeconfig для konnectivity-server. Наприклад, ви можете використовувати інструмент командного рядка OpenSSL для створення сертифіката X.509, використовуючи сертифікат CA кластера /etc/kubernetes/pki/ca.crt з хосту панелі управління.

openssl req -subj "/CN=system:konnectivity-server" -new -newkey rsa:2048 -nodes -out konnectivity.csr -keyout konnectivity.key
openssl x509 -req -in konnectivity.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out konnectivity.crt -days 375 -sha256
SERVER=$(kubectl config view -o jsonpath='{.clusters..server}')
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-credentials system:konnectivity-server --client-certificate konnectivity.crt --client-key konnectivity.key --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-cluster kubernetes --server "$SERVER" --certificate-authority /etc/kubernetes/pki/ca.crt --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-context system:konnectivity-server@kubernetes --cluster kubernetes --user system:konnectivity-server
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config use-context system:konnectivity-server@kubernetes
rm -f konnectivity.crt konnectivity.key konnectivity.csr

Потім вам потрібно розгорнути сервер Konnectivity та агентів. kubernetes-sigs/apiserver-network-proxy є посиланням на референсну реалізацію.

Розгорніть сервер Konnectivity на ваших вузлах панелі управління. Наданий маніфест konnectivity-server.yaml передбачає, що компоненти Kubernetes розгорнуті як статичний Pod у вашому кластері. Якщо ні, ви можете розгорнути сервер Konnectivity як DaemonSet.

apiVersion: v1
kind: Pod
metadata:
  name: konnectivity-server
  namespace: kube-system
spec:
  priorityClassName: system-cluster-critical
  hostNetwork: true
  containers:
  - name: konnectivity-server-container
    image: registry.k8s.io/kas-network-proxy/proxy-server:v0.0.37
    command: ["/proxy-server"]
    args: [
            "--logtostderr=true",
            # Це повинно збігатися зі значенням, встановленим у egressSelectorConfiguration.
            "--uds-name=/etc/kubernetes/konnectivity-server/konnectivity-server.socket",
            "--delete-existing-uds-file",
            # Наступні два рядки передбачають, що сервер Konnectivity
            # розгорнуто на тому ж компʼютері, що й apiserver, і сертифікати та
            # ключ API-сервера знаходяться за вказаною шляхом.
            "--cluster-cert=/etc/kubernetes/pki/apiserver.crt",
            "--cluster-key=/etc/kubernetes/pki/apiserver.key",
            # Це повинно збігатися зі значенням, встановленим у egressSelectorConfiguration.
            "--mode=grpc",
            "--server-port=0",
            "--agent-port=8132",
            "--admin-port=8133",
            "--health-port=8134",
            "--agent-namespace=kube-system",
            "--agent-service-account=konnectivity-agent",
            "--kubeconfig=/etc/kubernetes/konnectivity-server.conf",
            "--authentication-audience=system:konnectivity-server"
            ]
    livenessProbe:
      httpGet:
        scheme: HTTP
        host: 127.0.0.1
        port: 8134
        path: /healthz
      initialDelaySeconds: 30
      timeoutSeconds: 60
    ports:
    - name: agentport
      containerPort: 8132
      hostPort: 8132
    - name: adminport
      containerPort: 8133
      hostPort: 8133
    - name: healthport
      containerPort: 8134
      hostPort: 8134
    volumeMounts:
    - name: k8s-certs
      mountPath: /etc/kubernetes/pki
      readOnly: true
    - name: kubeconfig
      mountPath: /etc/kubernetes/konnectivity-server.conf
      readOnly: true
    - name: konnectivity-uds
      mountPath: /etc/kubernetes/konnectivity-server
      readOnly: false
  volumes:
  - name: k8s-certs
    hostPath:
      path: /etc/kubernetes/pki
  - name: kubeconfig
    hostPath:
      path: /etc/kubernetes/konnectivity-server.conf
      type: FileOrCreate
  - name: konnectivity-uds
    hostPath:
      path: /etc/kubernetes/konnectivity-server
      type: DirectoryOrCreate

Потім розгорніть агентів Konnectivity у вашому кластері:

apiVersion: apps/v1
# Instead of this, you can deploy agents as Deployments. It is not necessary to have an agent on each node.
kind: DaemonSet
metadata:
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app: konnectivity-agent
  namespace: kube-system
  name: konnectivity-agent
spec:
  selector:
    matchLabels:
      k8s-app: konnectivity-agent
  template:
    metadata:
      labels:
        k8s-app: konnectivity-agent
    spec:
      priorityClassName: system-cluster-critical
      tolerations:
        - key: "CriticalAddonsOnly"
          operator: "Exists"
      containers:
        - image: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent:v0.0.37
          name: konnectivity-agent
          command: ["/proxy-agent"]
          args: [
                  "--logtostderr=true",
                  "--ca-cert=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
                  # Оскільки сервер konnectivity працює з hostNetwork=true,
                  # це є IP-адреса машини-майстра.
                  "--proxy-server-host=35.225.206.7",
                  "--proxy-server-port=8132",
                  "--admin-server-port=8133",
                  "--health-server-port=8134",
                  "--service-account-token-path=/var/run/secrets/tokens/konnectivity-agent-token"
                  ]
          volumeMounts:
            - mountPath: /var/run/secrets/tokens
              name: konnectivity-agent-token
          livenessProbe:
            httpGet:
              port: 8134
              path: /healthz
            initialDelaySeconds: 15
            timeoutSeconds: 15
      serviceAccountName: konnectivity-agent
      volumes:
        - name: konnectivity-agent-token
          projected:
            sources:
              - serviceAccountToken:
                  path: konnectivity-agent-token
                  audience: system:konnectivity-server

Нарешті, якщо RBAC включено у вашому кластері, створіть відповідні правила RBAC:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:konnectivity-server
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: system:konnectivity-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: konnectivity-agent
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile

12 - TLS

Дізнайтесь, як захистити трафік у вашому кластері за допомогою TLS (Transport Layer Security).

12.1 - Налаштування ротації сертифікатів для Kubelet

Ця сторінка показує, як увімкнути та налаштувати ротацію сертифікатів для kubelet.

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.19 [stable]

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

  • Потрібна версія Kubernetes 1.8.0 або пізніша

Огляд

Kubelet використовує сертифікати для автентифікації в Kubernetes API. Типово ці сертифікати створюються з терміном дії один рік, щоб їх не потрібно було оновлювати занадто часто.

Kubernetes містить ротацію сертифікатів kubelet, яка автоматично генерує новий ключ і запитує новий сертифікат від Kubernetes API, коли поточний сертифікат наближається до закінчення терміну дії. Коли новий сертифікат стає доступним, він буде використовуватись для автентифікації зʼєднань з Kubernetes API.

Увімкнення ротації клієнтських сертифікатів

Процес kubelet приймає аргумент --rotate-certificates, який контролює, чи буде kubelet автоматично запитувати новий сертифікат, коли наближається термін дії поточного сертифіката.

Процес kube-controller-manager приймає аргумент --cluster-signing-duration (--experimental-cluster-signing-duration до версії 1.19), який контролює, на який термін будуть випускатись сертифікати.

Розуміння налаштувань ротації сертифікатів

Коли kubelet запускається, якщо він налаштований для початкового завантаження (використовуючи прапорець --bootstrap-kubeconfig), він використовуватиме свій початковий сертифікат для підключення до Kubernetes API та надсилатиме запит на підписання сертифіката. Ви можете переглянути статус запитів на підписання сертифікатів за допомогою:

kubectl get csr

Спочатку запит на підписання сертифіката від kubelet на вузлі матиме статус Pending. Якщо запит на підписання сертифіката відповідає певним критеріям, він буде автоматично схвалений менеджером контролерів, після чого він матиме статус Approved. Далі, менеджер контролерів підпише сертифікат, виданий на термін, вказаний параметром --cluster-signing-duration, і підписаний сертифікат буде прикріплений до запиту на підписання сертифіката.

Kubelet отримає підписаний сертифікат від Kubernetes API та запише його на диск у місці, вказаному параметром --cert-dir. Потім kubelet використовуватиме новий сертифікат для приєднання до Kubernetes API.

Коли наближається закінчення терміну дії підписаного сертифіката, kubelet автоматично надішле новий запит на підписання сертифіката, використовуючи Kubernetes API. Це може статись у будь-який момент між 30% та 10% залишкового часу дії сертифіката. Знову ж таки, менеджер контролерів автоматично схвалить запит на сертифікат і прикріпить підписаний сертифікат до запиту на підписання сертифіката. Kubelet отримає новий підписаний сертифікат від Kubernetes API та запише його на диск. Потім він оновить зʼєднання з Kubernetes API, щоб приєднатись за допомогою нового сертифіката.

12.2 - Ручна ротація сертифікатів центру сертифікації (CA)

Ця сторінка показує, як робити вручну ротацію сертифікатів центру сертифікації (CA).

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

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

  • Для отримання додаткової інформації про автентифікацію в Kubernetes, див. Автентифікація.
  • Для отримання додаткової інформації про найкращі практики для сертифікатів CA, див. Один кореневий CA.

Ручна ротація сертифікатів CA

  1. Розподіліть нові сертифікати CA та приватні ключі (наприклад: ca.crt, ca.key, front-proxy-ca.crt і front-proxy-ca.key) на всі вузли вашої панелі управління у теці сертифікатів Kubernetes.

  2. Оновіть прапорець --root-ca-file для kube-controller-manager, щоб включити як старий, так і новий CA, після чого перезавантажте kube-controller-manager.

    Будь-який ServiceAccount, створений після цього, отримає Secretʼи, що містять як старий, так і новий CA.

  3. Зачекайте, поки менеджер контролерів оновить ca.crt у Secretʼах облікових записів сервісів, щоб включити як старі, так і нові сертифікати CA.

    Якщо будь-які Podʼи були запущені до того, як новий CA буде використаний серверами API, нові Podʼи отримають це оновлення та будуть довіряти як старим, так і новим CA.

  4. Перезавантажте всі Podʼи, що використовують внутрішні конфігурації (наприклад: kube-proxy, CoreDNS тощо), щоб вони могли використовувати оновлені дані сертифікату центру сертифікації із Secretʼів, що повʼязані з ServiceAccounts.

    • Переконайтеся, що CoreDNS, kube-proxy та інші Podʼи, що використовують внутрішні конфігурації, працюють належним чином.
  5. Додайте як старий, так і новий CA до файлу, зазначеного у прапорці --client-ca-file та --kubelet-certificate-authority в конфігурації kube-apiserver.

  6. Додайте як старий, так і новий CA до файлу, зазначеного у прапорці --client-ca-file в конфігурації kube-scheduler.

  7. Оновіть сертифікати для облікових записів користувачів, замінивши вміст client-certificate-data та client-key-data.

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

    Крім того, оновіть розділ certificate-authority-data у kubeconfig файлах, відповідно до закодованих у Base64 даних старого та нового центру сертифікації.

  8. Оновіть прапорець --root-ca-file для Cloud Controller Manager, щоб включити як старий, так і новий CA, після чого перезавантажте cloud-controller-manager.

  9. Виконайте наступні кроки поступово.

    1. Перезавантажте будь-які інші агреговані сервери API або обробники вебхуків, щоб довіряти новим сертифікатам CA.

    2. Перезавантажте kubelet, оновивши файл, зазначений у clientCAFile в конфігурації kubelet, та certificate-authority-data у kubelet.conf, щоб використовувати як старий, так і новий CA на всіх вузлах.

      Якщо ваш kubelet не використовує ротацію клієнтських сертифікатів, оновіть client-certificate-data та client-key-data у kubelet.conf на всіх вузлах разом із файлом клієнтського сертифіката kubelet, зазвичай розташованим у /var/lib/kubelet/pki.

    3. Перезавантажте сервери API із сертифікатами (apiserver.crt, apiserver-kubelet-client.crt та front-proxy-client.crt), підписаними новим CA. Ви можете використовувати наявні приватні ключі або нові приватні ключі. Якщо ви змінили приватні ключі, то також оновіть їх у теці сертифікатів Kubernetes.

      Оскільки Podʼи у вашому кластері довіряють як старим, так і новим CA, буде короткочасне відключення, після чого клієнти Kubernetes у Podʼах переприєднаються до нового сервера API. Новий сервер API використовує сертифікат, підписаний новим CA.

      • Перезавантажте kube-scheduler, щоб використовувати та довіряти новим CA.
      • Переконайтеся, що логи компонентів панелі управління не містять помилок TLS.
    4. Анотуйте будь-які DaemonSets та Deployments, щоб викликати заміну Podʼів у більш безпечний спосіб.

      for namespace in $(kubectl get namespace -o jsonpath='{.items[*].metadata.name}'); do
          for name in $(kubectl get deployments -n $namespace -o jsonpath='{.items[*].metadata.name}'); do
              kubectl patch deployment -n ${namespace} ${name} -p '{"spec":{"template":{"metadata":{"annotations":{"ca-rotation": "1"}}}}}';
          done
          for name in $(kubectl get daemonset -n $namespace -o jsonpath='{.items[*].metadata.name}'); do
              kubectl patch daemonset -n ${namespace} ${name} -p '{"spec":{"template":{"metadata":{"annotations":{"ca-rotation": "1"}}}}}';
          done
      done
      

      Залежно від того, як ви використовуєте StatefulSets, вам також може знадобитися виконати подібну поступову заміну.

  10. Якщо ваш кластер використовує токени для початкового завантаження для приєднання вузлів, оновіть ConfigMap cluster-info в просторі імен kube-public з новим CA.

    base64_encoded_ca="$(base64 -w0 /etc/kubernetes/pki/ca.crt)"
    
    kubectl get cm/cluster-info --namespace kube-public -o yaml | \
        /bin/sed "s/\(certificate-authority-data:\).*/\1 ${base64_encoded_ca}/" | \
        kubectl apply -f -
    
  11. Перевірте функціональність кластера.

    1. Перевірте логи компонентів панелі управління, а також kubelet та kube-proxy. Переконайтеся, що ці компоненти не повідомляють про помилки TLS; див. перегляд журналів для отримання додаткових деталей.

    2. Перевірте логи будь-яких агрегованих серверів API та Podʼів, що використовують внутрішню конфігурацію.

  12. Після успішної перевірки функціональності кластера:

    1. Оновіть всі токени службових облікових записів, щоб включити лише новий сертифікат CA.

      • Всі Podʼи, що використовують внутрішній kubeconfig, згодом потрібно буде перезапустити, щоб отримати новий Secret, щоб жоден Pod не залежав від старого CA кластера.
    2. Перезавантажте компоненти панелі управління, видаливши старий CA з kubeconfig файлів та файлів, зазначених у прапорцях --client-ca-file та --root-ca-file.

    3. На кожному вузлі перезавантажте kubelet, видаливши старий CA з файлу, зазначеного у прапорі clientCAFile, та з файлу kubeconfig для kubelet. Ви повинні виконати це як поступове оновлення.

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

12.3 - Управління TLS сертифікатами в кластері

Kubernetes надає API certificates.k8s.io, який дозволяє вам отримувати TLS сертифікати, підписані Центром Сертифікації (CA), який ви контролюєте. Ці CA та сертифікати можуть використовуватися вашими робочими навантаженнями для встановлення довіри.

API certificates.k8s.io використовує протокол, подібний до чернетки ACME.

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

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

Вам потрібен інструмент cfssl. Ви можете завантажити cfssl з https://github.com/cloudflare/cfssl/releases.

Деякі кроки на цій сторінці використовують інструмент jq. Якщо у вас його немає, ви можете встановити його через джерела програмного забезпечення вашої операційної системи або завантажити з https://jqlang.github.io/jq/.

Довіра до TLS в кластері

Довіра до власного CA з боку застосунків, що працюють як Podʼи, зазвичай вимагає додаткової конфігурації. Вам потрібно додати пакет сертифікатів CA до списку сертифікатів CA, яким довіряє TLS клієнт або сервер. Наприклад, ви можете зробити це за допомогою налаштування TLS в Golang, проаналізувавши ланцюжок сертифікатів і додавши проаналізовані сертифікати до поля RootCAs в структурі tls.Config.

Запит сертифіката

Наступний розділ демонструє, як створити TLS сертифікат для Kubernetes сервісу, до якого звертаються через DNS.

Створення запиту на підписання сертифіката

Згенеруйте приватний ключ і запит на підписання сертифіката (або CSR), виконавши наступну команду:

cat <<EOF | cfssl genkey - | cfssljson -bare server
{
  "hosts": [
    "my-svc.my-namespace.svc.cluster.local",
    "my-pod.my-namespace.pod.cluster.local",
    "192.0.2.24",
    "10.0.34.2"
  ],
  "CN": "my-pod.my-namespace.pod.cluster.local",
  "key": {
    "algo": "ecdsa",
    "size": 256
  }
}
EOF

Де 192.0.2.24 — це кластерний IP Serviceʼу, my-svc.my-namespace.svc.cluster.local — це DNS-імʼя Serviceʼу, 10.0.34.2 —це IP Podʼа, а my-pod.my-namespace.pod.cluster.local — це DNS-імʼя Podʼа. Ви повинні побачити вивід подібний до:

2022/02/01 11:45:32 [INFO] generate received request
2022/02/01 11:45:32 [INFO] received CSR
2022/02/01 11:45:32 [INFO] generating key: ecdsa-256
2022/02/01 11:45:32 [INFO] encoded CSR

Ця команда генерує два файли; server.csr, що містить PEM закодований PKCS#10 запит на сертифікат, і server-key.pem, що містить PEM закодований ключ до сертифіката, який ще потрібно створити.

Створення обʼєкта CertificateSigningRequest для надсилання до Kubernetes API

Згенеруйте маніфест CSR (у форматі YAML) і надішліть його на сервер API. Ви можете зробити це, виконавши наступну команду:

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: my-svc.my-namespace
spec:
  request: $(cat server.csr | base64 | tr -d '\n')
  signerName: example.com/serving
  usages:
  - digital signature
  - key encipherment
  - server auth
EOF

Зверніть увагу, що файл server.csr, створений на кроці 1, кодується base64 та зберігається в полі .spec.request. Ви також запитуєте сертифікат з використанням ключів "digital signature", "key encipherment" і "server auth", підписаний підписувачем example.com/serving. Повинен бути запитаний конкретний signerName. Перегляньте документацію для підтримуваних імен підписувачів для отримання додаткової інформації.

CSR тепер має бути видимий з API у стані Pending. Ви можете побачити це, виконавши:

kubectl describe csr my-svc.my-namespace
Name:                   my-svc.my-namespace
Labels:                 <none>
Annotations:            <none>
CreationTimestamp:      Tue, 01 Feb 2022 11:49:15 -0500
Requesting User:        yourname@example.com
Signer:                 example.com/serving
Status:                 Pending
Subject:
        Common Name:    my-pod.my-namespace.pod.cluster.local
        Serial Number:
Subject Alternative Names:
        DNS Names:      my-pod.my-namespace.pod.cluster.local
                        my-svc.my-namespace.svc.cluster.local
        IP Addresses:   192.0.2.24
                        10.0.34.2
Events: <none>

Отримання схвалення CertificateSigningRequest

Схвалення запиту на підписання сертифіката виконується або автоматичною процедурою схвалення, або одноразово адміністратором кластера. Якщо у вас є дозвіл на схвалення запиту на сертифікат, ви можете зробити це вручну за допомогою kubectl; наприклад:

kubectl certificate approve my-svc.my-namespace
certificatesigningrequest.certificates.k8s.io/my-svc.my-namespace approved

Ви тепер повинні побачити наступне:

kubectl get csr
NAME                  AGE   SIGNERNAME            REQUEST

OR              REQUESTEDDURATION   CONDITION
my-svc.my-namespace   10m   example.com/serving   yourname@example.com   <none>              Approved

Це означає, що запит на сертифікат був схвалений і чекає на підписання запрошеним підписувачем.

Підписання CertificateSigningRequest

Далі ви зіграєте роль підписувача сертифікатів, видасте сертифікат і завантажите його в API.

Підписувач зазвичай спостерігає за API CertificateSigningRequest для обʼєктів з його signerName, перевіряє, що вони були схвалені, підписує сертифікати для цих запитів, і оновлює статус обʼєкта API з виданим сертифікатом.

Створення Центру Сертифікації

Вам потрібен центр сертифікації, щоб забезпечити цифровий підпис нового сертифікату.

Спочатку створіть підписний сертифікат, виконавши наступне:

cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
{
  "CN": "My Example Signer",
  "key": {
    "algo": "rsa",
    "size": 2048
  }
}
EOF

Ви повинні побачити вивід подібний до:

2022/02/01 11:50:39 [INFO] generating a new CA key and certificate from CSR
2022/02/01 11:50:39 [INFO] generate received request
2022/02/01 11:50:39 [INFO] received CSR
2022/02/01 11:50:39 [INFO] generating key: rsa-2048
2022/02/01 11:50:39 [INFO] encoded CSR
2022/02/01 11:50:39 [INFO] signed certificate with serial number 263983151013686720899716354349605500797834580472

Це створює файл ключа центру сертифікації (ca-key.pem) і сертифікат (ca.pem).

Видача сертифіката

{
    "signing": {
        "default": {
            "usages": [
                "digital signature",
                "key encipherment",
                "server auth"
            ],
            "expiry": "876000h",
            "ca_constraint": {
                "is_ca": false
            }
        }
    }
}

Використовуйте конфігурацію для підпису server-signing-config.json та файл ключа центру сертифікації та сертифікат для підпису запиту на сертифікат:

kubectl get csr my-svc.my-namespace -o jsonpath='{.spec.request}' | \
  base64 --decode | \
  cfssl sign -ca ca.pem -ca-key ca-key.pem -config server-signing-config.json - | \
  cfssljson -bare ca-signed-server

Ви повинні побачити вивід подібний до:

2022/02/01 11:52:26 [INFO] signed certificate with serial number 576048928624926584381415936700914530534472870337

Це створює файл підписаного серверного сертифіката, ca-signed-server.pem.

Завантаження підписаного сертифіката

Нарешті, вкажіть підписаний сертифікат у статусі обʼєкта API:

kubectl get csr my-svc.my-namespace -o json | \
  jq '.status.certificate = "'$(base64 ca-signed-server.pem | tr -d '\n')'"' | \
  kubectl replace --raw /apis/certificates.k8s.io/v1/certificatesigningrequests/my-svc.my-namespace/status -f -

Після схвалення CSR і завантаження підписаного сертифіката, виконайте:

kubectl get csr

Вивід буде подібним до:

NAME                  AGE   SIGNERNAME            REQUESTOR              REQUESTEDDURATION   CONDITION
my-svc.my-namespace   20m   example.com/serving   yourname@example.com   <none>              Approved,Issued

Завантаження сертифіката та його використання

Тепер, як запитувач, ви можете завантажити виданий сертифікат і зберегти його у файл server.crt, виконавши наступне:

kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
    | base64 --decode > server.crt

Тепер ви можете заповнити server.crt і server-key.pem у Секрет, який ви можете пізніше монтувати в Pod (наприклад, для використання з вебсервером, який обслуговує HTTPS).

kubectl create secret tls server --cert server.crt --key server-key.pem
secret/server created

Нарешті, ви можете заповнити ca.pem у ConfigMap і використовувати його як кореневий довірчий сертифікат для перевірки серверного сертифіката:

kubectl create configmap example-serving-ca --from-file ca.crt=ca.pem
configmap/example-serving-ca created

Схвалення CertificateSigningRequests

Адміністратор Kubernetes (з відповідними дозволами) може вручну схвалювати (або відхиляти) CertificateSigningRequests за допомогою команд kubectl certificate approve та kubectl certificate deny. Однак, якщо ви плануєте активно використовувати цей API, варто розглянути можливість написання автоматизованого контролера сертифікатів.

Чи це буде машина або людина, яка використовує kubectl, роль схвалювача полягає у перевірці, що CSR відповідає двом вимогам:

  1. Субʼєкт CSR контролює приватний ключ, який використовується для підписання CSR. Це зменшує ризик того, що є третя сторона, яка видає себе за авторизованого субʼєкта. У вищезазначеному прикладі цей крок передбачає перевірку того, що Pod контролює приватний ключ, який використовується для створення CSR.
  2. Субʼєкт CSR має право діяти в запитуваному контексті. Це зменшує ризик появи небажаного субʼєкта, який приєднується до кластера. У вищезазначеному прикладі цей крок передбачає перевірку того, що Pod має дозвіл на участь у запитуваному сервісі.

Якщо і тільки якщо ці дві вимоги виконані, схвалювач повинен схвалити CSR інакше він повинен відхилити CSR.

Для отримання додаткової інформації про схвалення сертифікатів та контроль доступу, прочитайте сторінку довідника Запити на підписання сертифікатів.

Налаштування вашого кластера для виконання накладання підписів

Ця сторінка припускає, що підписувач налаштований для обслуговування API сертифікатів. Менеджер контролерів Kubernetes надає стандартну реалізацію підписувача. Щоб увімкнути його, передайте параметри --cluster-signing-cert-file та --cluster-signing-key-file до менеджера контролерів з шляхами до пари ключів вашого центру сертифікації.

13 - Керування фоновими процесами кластера

Виконуйте загальні завдання для керування DaemonSet, такі як виконання поступового оновлення.

13.1 - Виконання поетапного оновлення DaemonSet

Ця сторінка показує, як виконати поетапне оновлення (rolling update) DaemonSet.

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

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

Стратегія оновлення DaemonSet

DaemonSet має два типи стратегій оновлення:

  • OnDelete: Зі стратегією оновлення OnDelete, після оновлення шаблону DaemonSet нові Podʼи DaemonSet будуть створюватися лише після вручну видалених старих Podʼів DaemonSet. Це та ж поведінка, що й у версії Kubernetes 1.5 або раніше.
  • RollingUpdate: Це стандартна стратегія оновлення. Зі стратегією оновлення RollingUpdate, після оновлення шаблону DaemonSet старі Podʼи DaemonSet будуть видалені, і нові Podʼи DaemonSet будуть створені автоматично, у контрольованому режимі. Під час усього процесу оновлення на кожному вузлі працюватиме максимум один Pod DaemonSet.

Виконання поетапного оновлення

Щоб увімкнути функцію поетапного оновлення DaemonSet, необхідно встановити .spec.updateStrategy.type на RollingUpdate.

Ви можете також встановити значення .spec.updateStrategy.rollingUpdate.maxUnavailable (типово 1), .spec.minReadySeconds (типово 0) та .spec.updateStrategy.rollingUpdate.maxSurge (типово 0).

Створення DaemonSet зі стратегією оновлення RollingUpdate

Цей YAML файл задає DaemonSet зі стратегією оновлення RollingUpdate:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # ці tolerations дозволяють запускати DaemonSet на вузлах панелі управління
      # видаліть їх, якщо ваші вузли панелі управління не повинні запускати Podʼи
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Після перевірки стратегії оновлення в маніфесті DaemonSet, створіть DaemonSet:

kubectl create -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml

Або скористайтесь командою kubectl apply, щоб створити той самий DaemonSet, якщо ви плануєте оновлювати DaemonSet за допомогою kubectl apply.

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml

Перевірка стратегії оновлення RollingUpdate у DaemonSet

Перевірте стратегію оновлення вашого DaemonSet і переконайтесь, що вона встановлена на RollingUpdate:

kubectl get ds/fluentd-elasticsearch -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}' -n kube-system

Якщо ви ще не створили DaemonSet у системі, перевірте ваш маніфест DaemonSet за допомогою наступної команди:

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset.yaml --dry-run=client -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'

Вивід обох команд повинен бути таким:

RollingUpdate

Якщо вивід не RollingUpdate, поверніться назад і змініть обʼєкт DaemonSet або його маніфест відповідно.

Оновлення шаблону DaemonSet

Будь-які оновлення до .spec.template RollingUpdate DaemonSet викличуть поетапне оновлення. Оновімо DaemonSet, застосувавши новий YAML файл. Це можна зробити за допомогою кількох різних команд kubectl.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      # ці tolerations дозволяють запускати DaemonSet на вузлах панелі управління
      # видаліть їх, якщо ваші вузли панелі управління не повинні запускати Podʼи
      - key: node-role.kubernetes.io/control-plane
        operator: Exists
        effect: NoSchedule
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Декларативні команди

Якщо ви оновлюєте DaemonSets за допомогою конфігураційних файлів, використовуйте kubectl apply:

kubectl apply -f https://k8s.io/examples/controllers/fluentd-daemonset-update.yaml

Імперативні команди

Якщо ви оновлюєте DaemonSets за допомогою імперативних команд, використовуйте kubectl edit :

kubectl edit ds/fluentd-elasticsearch -n kube-system
Оновлення лише образу контейнера

Якщо вам потрібно оновити лише образ контейнера у шаблоні DaemonSet, тобто .spec.template.spec.containers[*].image, використовуйте kubectl set image:

kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=quay.io/fluentd_elasticsearch/fluentd:v2.6.0 -n kube-system

Спостереження за станом поетапного оновлення

Нарешті, спостерігайте за станом останнього поетапного оновлення DaemonSet:

kubectl rollout status ds/fluentd-elasticsearch -n kube-system

Коли оновлення завершиться, вивід буде подібний до цього:

daemonset "fluentd-elasticsearch" successfully rolled out

Усунення несправностей

Поетапне оновлення DaemonSet застрягло

Іноді поетапне оновлення DaemonSet може застрягнути. Ось деякі можливі причини:

Деякі вузли вичерпали ресурси

Оновлення застрягло, оскільки нові Podʼи DaemonSet не можуть бути заплановані на принаймні один вузол. Це можливо, коли вузол вичерпує ресурси.

Коли це трапляється, знайдіть вузли, на яких не заплановані Podʼи DaemonSet, порівнявши вихід kubectl get nodes з виходом:

kubectl get pods -l name=fluentd-elasticsearch -o wide -n kube-system

Після того, як ви знайдете ці вузли, видаліть деякі не-DaemonSet Podʼи з вузла, щоб звільнити місце для нових Podʼіів DaemonSet.

Неправильне оновлення

Якщо недавнє оновлення шаблону DaemonSet є неправильним, наприклад, контейнер зациклюється або образ контейнера не існує (часто через помилку у назві), поетапне оновлення DaemonSet не просуватиметься.

Щоб виправити це, оновіть шаблон DaemonSet ще раз. Нове оновлення не буде блокуватися попередніми несправними оновленнями.

Невідповідність годинників

Якщо у DaemonSet задано значення .spec.minReadySeconds, невідповідність годинників між мастером та вузлами зробить DaemonSet нездатним визначити правильний прогрес оновлення.

Очищення

Видаліть DaemonSet з простору імен:

kubectl delete ds fluentd-elasticsearch -n kube-system

Що далі

13.2 - Виконання відкату DaemonSet

Ця сторінка показує, як виконати відкат на DaemonSet.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж 1.7. Для перевірки версії введіть kubectl version.

Ви повинні вже знати, як виконати поетапне оновлення на DaemonSet.

Виконання відкату на DaemonSet

Крок 1: Знайдіть ревізію DaemonSet, до якої ви хочете повернутися

Цей крок можна пропустити, якщо ви хочете повернутися до останньої ревізії.

Перегляньте всі ревізії DaemonSet:

kubectl rollout history daemonset <daemonset-name>

Це поверне список ревізій DaemonSet:

daemonsets "<daemonset-name>"
REVISION        CHANGE-CAUSE
1               ...
2               ...
...
  • Причина зміни копіюється з анотації DaemonSet kubernetes.io/change-cause до його ревізій під час створення. Ви можете вказати --record=true в kubectl, щоб записати команду, виконану в анотації причини зміни.

Щоб переглянути деталі конкретної ревізії:

kubectl rollout history daemonset <daemonset-name> --revision=1

Це поверне деталі цієї ревізії:

daemonsets "<daemonset-name>" with revision #1
Pod Template:
Labels:       foo=bar
Containers:
app:
 Image:        ...
 Port:         ...
 Environment:  ...
 Mounts:       ...
Volumes:      ...

Крок 2: Поверніться до конкретної ревізії DaemonSet

# Вкажіть номер ревізії, отриманий на кроці 1, у --to-revision
kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>

Якщо команда успішна, вона поверне:

daemonset "<daemonset-name>" rolled back

Крок 3: Спостерігайте за процесом відкату DaemonSet

kubectl rollout undo daemonset вказує серверу почати відкочування DaemonSet. Реальний відкат виконується асинхронно всередині панелі управління кластера.

Щоб спостерігати за процесом відкату:

kubectl rollout status ds/<daemonset-name>

Коли відкочування завершиться, вивід буде подібним до цього:

daemonset "<daemonset-name>" successfully rolled out

Розуміння ревізій DaemonSet

У попередньому кроці kubectl rollout history ви отримали список ревізій DaemonSet. Кожна ревізія зберігається в ресурсі під назвою ControllerRevision.

Щоб побачити, що зберігається в кожній ревізії, знайдіть сирцеві ресурси ревізії DaemonSet:

kubectl get controllerrevision -l <daemonset-selector-key>=<daemonset-selector-value>

Це поверне список ControllerRevisions:

NAME                               CONTROLLER                     REVISION   AGE
<daemonset-name>-<revision-hash>   DaemonSet/<daemonset-name>     1          1h
<daemonset-name>-<revision-hash>   DaemonSet/<daemonset-name>     2          1h

Кожен ControllerRevision зберігає анотації та шаблон ревізії DaemonSet.

kubectl rollout undo бере конкретний ControllerRevision і замінює шаблон DaemonSet на шаблон, збережений у ControllerRevision. kubectl rollout undo еквівалентний оновленню шаблону DaemonSet до попередньої ревізії за допомогою інших команд, таких як kubectl edit або kubectl apply.

Усунення несправностей

13.3 - Запуск Podʼів лише на деяких вузлах

Ця сторінка демонструє, як можна запускати Podʼи лише на деяких вузлах як частину DaemonSet

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

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

Запуск Pod лише на деяких вузлах

Уявімо, що ви хочете запустити DaemonSet, але вам потрібно запускати ці Pod демонів лише на вузлах, які мають локальне SSD-сховище. Наприклад, Pod може надавати кеш-сервіс для вузла, і кеш корисний тільки тоді, коли доступне локальне сховище з низькою затримкою.

Крок 1: Додайте мітки до ваших вузлів

Додайте мітку ssd=true до вузлів, які мають SSD.

kubectl label nodes example-node-1 example-node-2 ssd=true

Крок 2: Створіть маніфест

Створімо DaemonSet, який буде запускати Podʼи демонів тільки на вузлах з міткою SSD.

Використайте nodeSelector, щоб забезпечити, що DaemonSet буде запускати Pod лише на вузлах з міткою ssd, значення якої дорівнює "true".

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ssd-driver
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: ssd-driver-pod
  template:
    metadata:
      labels:
        app: ssd-driver-pod
    spec:
      nodeSelector:
        ssd: "true"
      containers:
        - name: example-container
          image: example-image

Крок 3: Створіть DaemonSet

Створіть DaemonSet з маніфесту, використовуючи kubectl create або kubectl apply.

Додамо мітку ще одному вузлу ssd=true.

kubectl label nodes example-node-3 ssd=true

Додавання мітки вузлу автоматично запускає панель управління (конкретно, контролер DaemonSet), щоб запустити новий Pod для демона на цьому вузлі.

kubectl get pods -o wide

Вивід буде схожим на:

NAME                              READY     STATUS    RESTARTS   AGE    IP      NODE
<daemonset-name><some-hash-01>    1/1       Running   0          13s    .....   example-node-1
<daemonset-name><some-hash-02>    1/1       Running   0          13s    .....   example-node-2
<daemonset-name><some-hash-03>    1/1       Running   0          5s     .....   example-node-3

14 - Мережа

Дізнайтесь, як налаштувати мережу для вашого кластера Kubernetes.

14.1 - Додавання записів до файлу /etc/hosts у Pod за допомогою HostAliases

Додавання записів до файлу /etc/hosts у Pod надає можливість перевизначення розподілу імен на рівні Pod, коли DNS та інші параметри не застосовуються. Ви можете додавати ці власні записи за допомогою поля HostAliases у PodSpec.

Не рекомендується вносити зміни без використання HostAliases, оскільки файл керується kubelet і може бути перезаписаний під час створення/перезапуску Pod.

Типовий вміст файлу hosts

Запустіть Pod з Nginx, який має призначену IP-адресу Podʼа:

kubectl run nginx --image nginx
pod/nginx created

Перевірте IP-адресу Podʼа:

kubectl get pods --output=wide
NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

Вміст файлу hosts буде виглядати так:

kubectl exec nginx -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.4	nginx

Типово файл hosts містить тільки шаблони IPv4 та IPv6, такі як localhost та власне імʼя хосту.

Додавання додаткових записів за допомогою hostAliases

Крім стандартних шаблонів, ви можете додати додаткові записи до файлу hosts. Наприклад: щоб перевести foo.local, bar.local в 127.0.0.1 та foo.remote, bar.remote в 10.1.2.3, ви можете налаштувати HostAliases для Pod в підполі .spec.hostAliases:

apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  restartPolicy: Never
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox:1.28
    command:
    - cat
    args:
    - "/etc/hosts"

Ви можете запустити Pod з такою конфігурацією, виконавши:

kubectl apply -f https://k8s.io/examples/service/networking/hostaliases-pod.yaml
pod/hostaliases-pod created

Перевірте деталі Pod, щоб побачити його IPv4-адресу та статус:

kubectl get pod --output=wide
NAME                           READY     STATUS      RESTARTS   AGE       IP              NODE
hostaliases-pod                0/1       Completed   0          6s        10.200.0.5      worker0

Вміст файлу hosts виглядає так:

kubectl logs hostaliases-pod
# Kubernetes-managed hosts file.
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.5	hostaliases-pod

# Entries added by HostAliases.
127.0.0.1	foo.local	bar.local
10.1.2.3	foo.remote	bar.remote

з додатковими записами, вказаними внизу.

Чому kubelet керує файлом hosts?

kubelet керує файлом hosts для кожного контейнера Podʼа, щоб запобігти модифікації файлу контейнерним середовищем після того, як контейнери вже були запущені. Історично Kubernetes завжди використовував Docker Engine як своє контейнерне середовище, і Docker Engine модифікував файл /etc/hosts після запуску кожного контейнера.

Поточна версія Kubernetes може використовувати різні контейнерні середовища; проте, kubelet керує файлом hosts у кожному контейнері, щоб результат був таким, як очікувалося, незалежно від використаного контейнерного середовища.

14.2 - Розширення діапазонів IP Service

СТАН ФУНКЦІОНАЛУ: Kubernetes v1.31 [beta]

У цьому документі описано, як розширити наявний діапазон IP-адрес, призначених Serviceʼу в кластері.

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

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

Версія вашого Kubernetes сервера має бути не старішою ніж v1.29. Для перевірки версії введіть kubectl version.

API

Кластери Kubernetes з kube-apiservers, у яких увімкнено функціональну можливість MultiCIDRServiceAllocator та API networking.k8s.io/v1alpha1, створюватимуть новий обʼєкт ServiceCIDR, який має відоме імʼя kubernetes, та використовуватимуть діапазон IP-адрес, заснований на значенні аргументу командного рядка --service-cluster-ip-range для kube-apiserver.

kubectl get servicecidr
NAME         CIDRS          AGE
kubernetes   10.96.0.0/28   17d

Відомий сервіс kubernetes, який використовується для відкриття точки доступу kube-apiserver для Podʼів, обчислює першу IP-адресу зі стандартного діапазону ServiceCIDR та використовує цю IP-адресу як свою кластерну IP-адресу.

kubectl get service kubernetes
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   17d

Стандартний Service у цьому випадку використовує ClusterIP 10.96.0.1, що має відповідний обʼєкт IPAddress.

kubectl get ipaddress 10.96.0.1
NAME        PARENTREF
10.96.0.1   services/default/kubernetes

ServiceCIDRs захищені за допомогою завершувачів, щоб уникнути залишання Service ClusterIPs сиріт; завершувач видаляється лише в разі, якщо існує інша підмережа, яка містить наявні IP-адреси або немає IP-адрес, що належать до підмережі.

Розширення кількості доступних IP для Service

Існують випадки, коли користувачам потрібно збільшити кількість доступних адрес для Serviceʼів; раніше, збільшення діапазону Service було руйнівною операцією, яка також може призвести до втрати даних. З цією новою функцією користувачам потрібно лише додати новий ServiceCIDR, щоб збільшити кількість доступних адрес.

Додавання нового ServiceCIDR

У кластері з діапазоном 10.96.0.0/28 для Serviceʼів доступно лише 2^(32-28) - 2 = 14 IP-адрес. Service kubernetes.default завжди створюється; для цього прикладу у вас залишається лише 13 можливих Serviceʼів.

for i in $(seq 1 13); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.11
10.96.0.5
10.96.0.12
10.96.0.13
10.96.0.14
10.96.0.2
10.96.0.3
10.96.0.4
10.96.0.6
10.96.0.7
10.96.0.8
10.96.0.9
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full

Ви можете збільшити кількість IP-адрес, доступних для Serviceʼів, створивши новий ServiceCIDR, який розширює або додає нові діапазони IP-адрес.

cat <EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1alpha1
kind: ServiceCIDR
metadata:
  name: newcidr1
spec:
  cidrs:
  - 10.96.0.0/24
EOF
servicecidr.networking.k8s.io/newcidr1 created

це дозволить вам створювати нові Service з ClusterIP, які будуть вибрані з цього нового діапазону.

for i in $(seq 13 16); do kubectl create service clusterip "test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.48
10.96.0.200
10.96.0.121
10.96.0.144

Видалення ServiceCIDR

Ви не можете видалити ServiceCIDR, якщо існують IP-адреси, які залежать від ServiceCIDR.

kubectl delete servicecidr newcidr1
servicecidr.networking.k8s.io "newcidr1" deleted

Kubernetes використовує завершувач на ServiceCIDR для відстеження цього залежного відношення.

kubectl get servicecidr newcidr1 -o yaml
apiVersion: networking.k8s.io/v1alpha1
kind: ServiceCIDR
metadata:
  creationTimestamp: "2023-10-12T15:11:07Z"
  deletionGracePeriodSeconds: 0
  deletionTimestamp: "2023-10-12T15:12:45Z"
  finalizers:
  - networking.k8s.io/service-cidr-finalizer
  name: newcidr1
  resourceVersion: "1133"
  uid: 5ffd8afe-c78f-4e60-ae76-cec448a8af40
spec:
  cidrs:
  - 10.96.0.0/24
status:
  conditions:
  - lastTransitionTime: "2023-10-12T15:12:45Z"
    message: There are still IPAddresses referencing the ServiceCIDR, please remove
      them or create a new ServiceCIDR
    reason: OrphanIPAddress
    status: "False"
    type: Ready

Видаляючи Serviceʼи, що містять IP-адреси, які блокують видалення ServiceCIDR

for i in $(seq 13 16); do kubectl delete service "test-$i" ; done
service "test-13" deleted
service "test-14" deleted
service "test-15" deleted
service "test-16" deleted

панель управління помічає видалення. Панель управління потім видаляє свій завершувач, так що ServiceCIDR, який був у черзі на видалення, фактично буде видалено.

kubectl get servicecidr newcidr1
Error from server (NotFound): servicecidrs.networking.k8s.io "newcidr1" not found

14.3 - Перевірка наявності підтримки подвійного стеку IPv4/IPv6

Цей документ розповідає, як перевірити підтримку dual-stack IPv4/IPv6 в увімкнених кластерах Kubernetes.

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

  • Підтримка постачальника для мереж з підтримкою подвійного стека (постачальник хмарних послуг або інший постачальник повинен забезпечити вузлам Kubernetes мережеві інтерфейси з маршрутними IPv4/IPv6)
  • Втулок мережі, який підтримує dual-stack мережу.
  • Увімкнений подвійний стек кластер
Версія вашого Kubernetes сервера має бути не старішою ніж v1.23. Для перевірки версії введіть kubectl version.

Перевірка адресації

Перевірка адресування вузлів

Кожен вузол з подвійним стеком має мати виділені один блок IPv4 та один блок IPv6. Перевірте, що діапазони адрес IPv4/IPv6 для Pod налаштовані за допомогою наступної команди. Замініть імʼя вузла з прикладу на наявний вузол з подвійним стеком у вашому кластері. У цьому прикладі імʼя вузла — k8s-linuxpool1-34450317-0:

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .spec.podCIDRs}}{{printf "%s\n" .}}{{end}}'
10.244.1.0/24
2001:db8::/64

Має бути виділено один блок IPv4 та один блок IPv6.

Перевірте, що на вузлі виявлено інтерфейс IPv4 та IPv6. Замініть імʼя вузла на дійсний вузол з кластера. У цьому прикладі імʼя вузла - k8s-linuxpool1-34450317-0:

kubectl get nodes k8s-linuxpool1-34450317-0 -o go-template --template='{{range .status.addresses}}{{printf "%s: %s\n" .type .address}}{{end}}'
Hostname: k8s-linuxpool1-34450317-0
InternalIP: 10.0.0.5
InternalIP: 2001:db8:10::5

Перевірка адресації Pod

Перевірте, що у Pod є призначена адреса IPv4 та IPv6. Замініть імʼя Pod на наявний Pod у вашому кластері. У цьому прикладі імʼя Pod - pod01:

kubectl get pods pod01 -o go-template --template='{{range .status.podIPs}}{{printf "%s\n" .ip}}{{end}}'
10.244.1.4
2001:db8::4

Ви також можете перевірити IP-адреси Pod за допомогою Downward API через поле .status.podIPs. Наступний уривок показує, як ви можете використовувати IP-адреси Pod через змінну середовища з назвою MY_POD_IPS всередині контейнера.

        env:
        - name: MY_POD_IPS
          valueFrom:
            fieldRef:
              fieldPath: status.podIPs

Наступна команда виводить значення змінної середовища MY_POD_IPS всередині контейнера. Значення — це кома, що розділяє список, який відповідає IPv4 та IPv6 адресам Pod.

kubectl exec -it pod01 -- set | grep MY_POD_IPS
MY_POD_IPS=10.244.1.4,2001:db8::4

IP-адреси Pod також будуть записані в /etc/hosts всередині контейнера. Наступна команда виконує cat на /etc/hosts в Podʼі з подвійним стеком. З виводу ви можете перевірити як IPv4, так і IPv6 IP-адресу для Pod.

kubectl exec -it pod01 -- cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1    localhost
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet
fe00::0    ip6-mcastprefix
fe00::1    ip6-allnodes
fe00::2    ip6-allrouters
10.244.1.4    pod01
2001:db8::4    pod01

Перевірка Serviceʼів

Створіть наступний Service, який не визначає явно .spec.ipFamilyPolicy. Kubernetes призначить кластерний IP для Service з першого налаштованого service-cluster-ip-range і встановить .spec.ipFamilyPolicy на SingleStack.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

Використовуйте kubectl, щоб переглянути YAML для Service.

kubectl get svc my-service -o yaml

У Service .spec.ipFamilyPolicy встановлено на SingleStack, а .spec.clusterIP встановлено на IPv4-адрес з першого налаштованого діапазону, встановленого за допомогою прапорця --service-cluster-ip-range на kube-controller-manager.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: default
spec:
  clusterIP: 10.0.217.164
  clusterIPs:
  - 10.0.217.164
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9376
  selector:
    app.kubernetes.io/name: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

Створіть наступний Service, який явно визначає IPv6 як перший елемент масиву в .spec.ipFamilies. Kubernetes призначить кластерний IP для Service з діапазону IPv6, налаштованого в service-cluster-ip-range, і встановить .spec.ipFamilyPolicy на SingleStack.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilies:
  - IPv6
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

Використовуйте kubectl, щоб переглянути YAML для Service.

kubectl get svc my-service -o yaml

У Service .spec.ipFamilyPolicy встановлено на SingleStack, а .spec.clusterIP встановлено на IPv6-адрес з діапазону IPv6, налаштованого за допомогою прапорця --service-cluster-ip-range у kube-controller-manager.

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/name: MyApp
  name: my-service
spec:
  clusterIP: 2001:db8:fd00::5118
  clusterIPs:
  - 2001:db8:fd00::5118
  ipFamilies:
  - IPv6
  ipFamilyPolicy: SingleStack
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app.kubernetes.io/name: MyApp
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

Створіть наступний Service, який явно визначає PreferDualStack в .spec.ipFamilyPolicy. Kubernetes призначить як IPv4, так і IPv6 адреси кластера (оскільки в цьому кластері ввімкнено подвійний стек) і вибере .spec.ClusterIP зі списку .spec.ClusterIPs на основі сімʼї адрес, що вказана в першому елементі масиву .spec.ipFamilies.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

Перевірте, що Service отримує кластерні IP з діапазонів IPv4 та IPv6, використовуючи kubectl describe. Потім ви можете перевірити доступ до Service за допомогою IP та портів.

kubectl describe svc -l app.kubernetes.io/name=MyApp
Name:              my-service
Namespace:         default
Labels:            app.kubernetes.io/name=MyApp
Annotations:       <none>
Selector:          app.kubernetes.io/name=MyApp
Type:              ClusterIP
IP Family Policy:  PreferDualStack
IP Families:       IPv4,IPv6
IP:                10.0.216.242
IPs:               10.0.216.242,2001:db8:fd00::af55
Port:              <unset>  80/TCP
TargetPort:        9376/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

Створення Service з подвійним стеком для балансування навантаження

Якщо постачальник хмарних послуг підтримує надання зовнішніх балансувальників навантаження з підтримкою IPv6, створіть наступний Service з PreferDualStack в .spec.ipFamilyPolicy, IPv6 як перший елемент масиву .spec.ipFamilies, а також встановіть поле type на LoadBalancer.

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app.kubernetes.io/name: MyApp
spec:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
  - IPv6
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80

Перевірка Service:

kubectl get svc -l app.kubernetes.io/name=MyApp

Перевірте, що Service отримує CLUSTER-IP адресу з блоку адрес IPv6 разом із EXTERNAL-IP. Потім ви можете перевірити доступ до Service за допомогою IP та портів.

NAME         TYPE           CLUSTER-IP            EXTERNAL-IP        PORT(S)        AGE
my-service   LoadBalancer   2001:db8:fd00::7ebc   2603:1030:805::5   80:30790/TCP   35s

15 - Розширення kubectl за допомогою втулків

Дізнайтеся, як розширити kubectl за допомогою втулків.

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

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

Вам потрібно мати встановлений працюючий бінарний файл kubectl.

Встановлення втулків для kubectl

Втулок — це автономний виконуваний файл, назва якого починається з kubectl-. Для встановлення втулка перемістіть його виконуваний файл до будь-якої теки, що знаходиться в вашому PATH.

Ви також можете знайти та встановити втулки для kubectl, наявні у відкритому доступі, за допомогою Krew. Krew — це менеджер втулків, підтримуваний спільнотою Kubernetes SIG CLI.

Пошук втулків

kubectl надає команду kubectl plugin list, яка оглядає ваш PATH для відповідних виконуваних файлів втулків. Виконання цієї команди призводить до огляду всіх файлів у вашому PATH. Будь-які файли, які є виконуваними та починаються з kubectl-, зʼявляться в порядку їх розташування в вашому PATH у виводі цієї команди. Для будь-яких файлів, що починаються з kubectl- та не є виконуваними, буде додано попередження. Також буде додане попередження для будь-яких вірних файлів втулків, назви яких перекриваються.

Ви можете використовувати Krew, щоб шукати та встановлювати втулки для kubectl з індексу втулків, який підтримується спільнотою.

Обмеження

Зараз неможливо створити втулки, які перезаписують чинні команди kubectl. Наприклад, створення втулка kubectl-version призведе до того, що цей втулок ніколи не буде виконаний, оскільки наявна команда kubectl version завжди матиме пріоритет. За цим обмеженням також не можна використовувати втулки для додавання нових підкоманд до наявних команд kubectl. Наприклад, додавання підкоманди kubectl create foo, назвавши свій втулок kubectl-create-foo, призведе до його ігнорування.

kubectl plugin list виводить попередження для будь-яких вірних втулків, які намагаються це зробити.

Написання втулків для kubectl

Ви можете написати втулок будь-якою мовою програмування або скриптом, який дозволяє вам створювати команди для командного рядка.

Для втулків не потрібно жодної установки чи попереднього завантаження. Виконувані файли втулків успадковують середовище від бінарного файлу kubectl. Втулок визначає, який шлях команди він бажає реалізувати на основі свого імені. Наприклад, втулок з іменем kubectl-foo надає команду kubectl foo. Вам потрібно встановити виконуваний файл втулка десь у вашому PATH.

Приклад втулка

#!/bin/bash

# optional argument handling
if [[ "$1" == "version" ]]
then
    echo "1.0.0"
    exit 0
fi

# optional argument handling
if [[ "$1" == "config" ]]
then
    echo "$KUBECONFIG"
    exit 0
fi

echo "I am a plugin named kubectl-foo"

Користування втулком {#using-a-plugin

Для використання втулка зробіть його виконуваним:

sudo chmod +x ./kubectl-foo

та помістіть його в теку, яка знаходиться в вашому PATH:

sudo mv ./kubectl-foo /usr/local/bin

Тепер ви можете викликати втулок, використовуючи kubectl foo:

kubectl foo
I am a plugin named kubectl-foo

Всі аргументи та прапорці, передаються втулку як-вони-є для виконання:

kubectl foo version
1.0.0

Всі змінні оточення також передається у вигляді як-вони-є:

export KUBECONFIG=~/.kube/config
kubectl foo config
/home/<user>/.kube/config
KUBECONFIG=/etc/kube/config kubectl foo config
/etc/kube/config

Крім того, перший аргумент, який передається втуку, завжди буде повним шляхом до місця, де його було викликано ($0 буде дорівнювати /usr/local/bin/kubectl-foo в прикладі вище).

Іменування втулків

Як видно в прикладі вище, втулок визначає шлях команди, яку він буде реалізовувати, на основі свого імені файлу. Кожна підкоманда в шляху, для якої використовується втулок, розділена дефісом (-). Наприклад, втулок, який бажає запускатись, коли користувач викликає команду kubectl foo bar baz, матиме імʼя файлу kubectl-foo-bar-baz.

Обробка прапорців та аргументів

Втулки kubectl повинні розбирати та перевіряти всі передані їм аргументи. Див. використання пакета command line runtime для отримання відомостей про бібліотеку Go, призначену на авторів втулків.

Нижче наведено кілька додаткових випадків, коли користувачі викликають ваш втулок, надаючи додаткові прапорці та аргументи. Розберемо це на прикладі втулка kubectl-foo-bar-baz з вищезазначеного сценарію.

Якщо ви виконуєте kubectl foo bar baz arg1 --flag=value arg2, механізм втулків kubectl спочатку намагатиметься знайти втулок з найдовшим можливим імʼям, що в цьому випадку буде kubectl-foo-bar-baz-arg1. Не знайшовши цього втулка, kubectl тоді розглядає останнє значення, розділене дефісами, як аргумент (тут arg1) та намагається знайти наступне найдовше можливе імʼя, kubectl-foo-bar-baz. Знайшовши втулок з таким імʼям, kubectl викликає цей втулок, передаючи всі аргументи та прапорці після імені втулка як аргументи для процесу втулка.

Приклад:

# створимо втулок
echo -e '#!/bin/bash\n\necho "My first command-line argument was $1"' > kubectl-foo-bar-baz
sudo chmod +x ./kubectl-foo-bar-baz

# "iвстановимо" ваш втулок перемістивши його у теку з $PATH
sudo mv ./kubectl-foo-bar-baz /usr/local/bin

# перевіримо, що kubectl розпізнав ваш втулок
kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/kubectl-foo-bar-baz
# перевірте, що виклик вашого втулка через команду "kubectl" працює
# навіть тоді, коли користувач передає додаткові аргументи та прапорці
# до виконуваного користувачем виконуваного файлу вашого втулка.

kubectl foo bar baz arg1 --meaningless-flag=true
My first command-line argument was arg1

Як бачите, ваш втулок було знайдено на основі команди kubectl, вказаної користувачем, і всі додаткові аргументи та прапорці були передані як є до виконуваного файлу втулка після того, як його було знайдено.

Назви з дефісами та підкреслюваннями

Хоча механізм втулків kubectl використовує дефіси (-) у іменах файлів втулків для розділення послідовності підкоманд, оброблених втулком, все ж можна створити втулок з іменем, що містить дефіс в його виклику з командного рядка, використовуючи підкреслення (_) у його імені файлу.

Приклад:

# створимо втулок, що містить підкреслення в назві файлу
echo -e '#!/bin/bash\n\necho "I am a plugin with a dash in my name"' > ./kubectl-foo_bar
sudo chmod +x ./kubectl-foo_bar

# перемістимо втулок у теку з $PATH
sudo mv ./kubectl-foo_bar /usr/local/bin

# тепер ви можете викликати ваш втулок через kubectl:
kubectl foo-bar
I am a plugin with a dash in my name

Зауважте, що додавання підкреслення в імʼя файлу втулка не заважає створенню команд, таких як kubectl foo_bar. Команду з прикладу вище можна викликати як з дефісом (-), так і з підкресленням (_):

# Ви можете викликати вашу власну команду з дефісом
kubectl foo-bar
I am a plugin with a dash in my name
# Ви ткаож можете викликати свою власну команду з підкресленнями
kubectl foo_bar
I am a plugin with a dash in my name

Конфлікти імен та затьмарення

Можливе існування кількох втулків з однаковою назвою файлу в різних розташуваннях у вашому PATH. Наприклад, при наступному значенні PATH: PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins, копія втулка kubectl-foo може існувати в /usr/local/bin/plugins та /usr/local/bin/moreplugins, так що результат виклику команди kubectl plugin list буде наступним:

PATH=/usr/local/bin/plugins:/usr/local/bin/moreplugins kubectl plugin list
The following kubectl-compatible plugins are available:

/usr/local/bin/plugins/kubectl-foo
/usr/local/bin/moreplugins/kubectl-foo
  - warning: /usr/local/bin/moreplugins/kubectl-foo is overshadowed by a similarly named plugin: /usr/local/bin/plugins/kubectl-foo

error: one plugin warning was found

У вказаному вище сценарії попередження під /usr/local/bin/moreplugins/kubectl-foo говорить вам, що цей втулок ніколи не буде виконано. Замість цього виконується файл, який зʼявляється першим у вашому PATH, /usr/local/bin/plugins/kubectl-foo, завдяки механізму втулків kubectl.

Спосіб розвʼязання цієї проблеми — переконатися, що розташування втулка, яким ви хочете скористатися з kubectl, завжди стоїть на першому місці в вашому PATH. Наприклад, якщо ви хочете завжди використовувати /usr/local/bin/moreplugins/kubectl-foo в будь-який раз, коли викликається команда kubectl foo, змініть значення вашого PATH на /usr/local/bin/moreplugins:/usr/local/bin/plugins.

Виклик найдовшого імені виконуваного файлу

Є ще один вид перекриття, який може виникнути з іменами втулків. Допустимо, є два втулки у шляху користувача: kubectl-foo-bar та kubectl-foo-bar-baz. Механізм втулків kubectl завжди вибиратиме найдовше можливе імʼя втулка для заданої команди користувача. Нижче наведено деякі приклади, які докладніше пояснюють це:

# для заданої команди `kubectl` завжди буде вибиратися втулок з найдовшим можливим іменем файла
kubectl foo bar baz
Plugin kubectl-foo-bar-baz is executed
kubectl foo bar
Plugin kubectl-foo-bar is executed
kubectl foo bar baz buz
Plugin kubectl-foo-bar-baz is executed, with "buz" as its first argument
kubectl foo bar buz
Plugin kubectl-foo-bar is executed, with "buz" as its first argument

Цей вибір дизайну гарантує, що підкоманди втулків можуть бути реалізовані у кількох файлах, якщо це необхідно, і що ці підкоманди можуть бути вкладені під "батьківською" командою втулка:

ls ./plugin_command_tree
kubectl-parent
kubectl-parent-subcommand
kubectl-parent-subcommand-subsubcommand

Перевірка на наявність попереджень втулків

Ви можете скористатися вищезазначеною командою kubectl plugin list, щоб переконатися, що ваш втулок можна викликати за допомогою kubectl, та перевірити, чи немає попереджень, які перешкоджають його виклику як команди kubectl.

kubectl plugin list
The following kubectl-compatible plugins are available:

test/fixtures/pkg/kubectl/plugins/kubectl-foo
/usr/local/bin/kubectl-foo
  - warning: /usr/local/bin/kubectl-foo is overshadowed by a similarly named plugin: test/fixtures/pkg/kubectl/plugins/kubectl-foo
plugins/kubectl-invalid
  - warning: plugins/kubectl-invalid identified as a kubectl plugin, but it is not executable

error: 2 plugin warnings were found

Використання пакунка виконання командного рядка

Якщо ви пишете втулку для kubectl і використовуєте Go, ви можете скористатися cli-runtime бібліотеками.

Ці бібліотеки надають помічників для парсингу або оновлення файлу kubeconfig користувача, для виконання запитів REST до API-сервера або звʼязування прапорців повʼязаних з конфігурацією та друком.

Перегляньте Sample CLI Plugin для прикладу використання інструментів, які надаються у репозиторії CLI Runtime.

Розповсюдження втулків для kubectl

Якщо ви розробили втулок для того, щоб інші користувачі могли його використовувати, вам слід розглянути, як ви його пакуєте, розповсюджуєте і надаєте оновлення для користувачів.

Krew

Krew пропонує крос-платформний спосіб пакування та розповсюдження ваших втулків. Таким чином, ви використовуєте єдиний формат пакування для всіх цільових платформ (Linux, Windows, macOS і т.д.) і надаєте оновлення користувачам. Krew також підтримує індекс втулків, щоб інші люди могли знайти ваш втулок та встановлювати його.

Нативне / платформозалежне керування пакетами

З іншого боку, ви можете використовувати традиційні системи керування пакунками, такі як apt або yum в Linux, Chocolatey в Windows і Homebrew в macOS. Будь-який менеджер пакунків підійде, якщо він може розмістити нові виконувані файли в каталозі PATH користувача. Як автор втулка, якщо ви обираєте цей варіант, вам також слід покласти на себе тягар оновлення пакунка для розповсюдження ваших втулків для kubectl для кожного релізу на кожній платформі.

Сирці

Ви можете публікувати сирці, наприклад, як Git-репозиторій. Якщо ви обираєте цей варіант, той, хто хоче використовувати цей втулок, повинен витягти код, налаштувати середовище збірки (якщо він потребує компіляції) та використовувати втулок. Якщо ви також надаєте доступні скомпільовані пакунки або використовуєте Krew, це спростить процес встановлення.

Що далі

  • Перегляньте репозиторій Sample CLI Plugin для детального прикладу втулка, написаного на Go.
  • У разі будь-яких питань, не соромтеся звертатися до команди SIG CLI.
  • Дізнайтеся більше про Krew, менеджер пакунків для втулків kubectl.

16 - Керування HugePages

Налаштування та керування великими сторінками як запланованим ресурсом в кластері.
СТАН ФУНКЦІОНАЛУ: Kubernetes v1.14 [stable]

Kubernetes підтримує виділення та використання заздалегідь розміщених великих сторінок (huge pages) застосунками в Podʼі. Ця сторінка описує, як користувачі можуть використовувати великі сторінки.

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

На вузлах Kubernetes необхідно перед використанням резервувати місце під великі сторінки, щоб вузол зміг вказати свою ємність для великих сторінок.

Вузол може резервувати великі сторінки різних розмірів, наприклад, наступний рядок у файлі /etc/default/grub резервує 2*1 ГБ памʼяті для сторінок розміром 1 ГБ і 512*2 МБ для сторінок розміром 2 МБ:

GRUB_CMDLINE_LINUX="hugepagesz=1G hugepages=2 hugepagesz=2M hugepages=512"

Вузли автоматично виявлять та повідомлять про всі великі сторінки як планові ресурси.

Під час опису вузла ви повинні побачити щось подібне до наступного у розділах Capacity та Allocatable:

Capacity:
  cpu:                ...
  ephemeral-storage:  ...
  hugepages-1Gi:      2Gi
  hugepages-2Mi:      1Gi
  memory:             ...
  pods:               ...
Allocatable:
  cpu:                ...
  ephemeral-storage:  ...
  hugepages-1Gi:      2Gi
  hugepages-2Mi:      1Gi
  memory:             ...
  pods:               ...

API

Великі сторінки можна використовувати за допомогою вимог до ресурсів на рівні контейнера з використанням імені ресурсу hugepages-<size>, де <size> — це найбільш компактне двійкове позначення з використанням цілочисельних значень, які підтримуються на певному вузлі. Наприклад, якщо вузол підтримує розміри сторінок 2048KiB та 1048576KiB, він буде показувати розміри hugepages-2Mi та hugepages-1Gi. На відміну від CPU або памʼяті, великі сторінки не підтримують перевищення. Зверніть увагу, що при запиті ресурсів великих сторінок також потрібно вказувати ресурси памʼяті або CPU.

Pod може мати різні розміри великих сторінок в одній специфікації Podʼа. У цьому випадку для всіх точок монтування томів вона повинна використовувати нотацію medium: HugePages-<hugepagesize>.

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages-2Mi
      name: hugepage-2mi
    - mountPath: /hugepages-1Gi
      name: hugepage-1gi
    resources:
      limits:
        hugepages-2Mi: 100Mi
        hugepages-1Gi: 2Gi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage-2mi
    emptyDir:
      medium: HugePages-2Mi
  - name: hugepage-1gi
    emptyDir:
      medium: HugePages-1Gi

Pod може використовувати medium: HugePages лише у випадку, якщо він запитує великі сторінки лише одного розміру.

apiVersion: v1
kind: Pod
metadata:
  name: huge-pages-example
spec:
  containers:
  - name: example
    image: fedora:latest
    command:
    - sleep
    - inf
    volumeMounts:
    - mountPath: /hugepages
      name: hugepage
    resources:
      limits:
        hugepages-2Mi: 100Mi
        memory: 100Mi
      requests:
        memory: 100Mi
  volumes:
  - name: hugepage
    emptyDir:
      medium: HugePages
  • Запити великих сторінок повинні дорівнювати лімітам. Це є стандартним, якщо ліміти вказані, а запити — ні.
  • Великі сторінки ізольовані на рівні контейнера, тому кожен контейнер має власний ліміт, як вказано у специфікації контейнера.
  • Томи EmptyDir, що підтримуються великими сторінками, не можуть споживати більше памʼяті великих сторінок, ніж запитується для Podʼа.
  • Застосунки, які використовують великі сторінки за допомогою shmget() з SHM_HUGETLB, повинні працювати з допоміжною групою, яка відповідає за proc/sys/vm/hugetlb_shm_group.
  • Використанням великих сторінок у просторі імен можливо керувати за допомогою ResourceQuota, подібно іншим обчислювальним ресурсам, таким як cpu або memory, використовуючи токен hugepages-<size>.

17 - Планування GPU

Налаштування та планування GPU для використання як ресурсу вузлів у кластері.
СТАН ФУНКЦІОНАЛУ: Kubernetes v1.26 [stable]

Kubernetes має стабільну підтримку для управління графічними обчислювальними пристроями (GPU) від AMD та NVIDIA на різних вузлах вашого кластера, використовуючи втулки пристроїв.

На цій сторінці описано, як користувачі можуть використовувати GPU і наведені деякі обмеження в реалізації.

Використання втулків пристроїв

Kubernetes використовує втулки пристроїв, щоб дозволити Podʼам отримувати доступ до спеціалізованих апаратних можливостей, таких як GPU.

Як адміністратору, вам потрібно встановити драйвери GPU від відповідного виробника обладнання на вузлах і запустити відповідний втулок пристрою від виробника GPU. Ось кілька посилань на інструкції від виробників:

Після встановлення втулка ваш кластер використовує власний ресурс планування, такий як amd.com/gpu або nvidia.com/gpu.

Ви можете використовувати ці GPU у ваших контейнерах, запитуючи власний ресурс GPU,, так само як ви запитуєте cpu чи memory. Однак є обмеження у специфікації вимог до ресурсів для власних пристроїв.

GPU повинні бути вказані тільки в розділі limits, що означає:

  • Ви можете вказати limits для GPU без вказівки requests, оскільки Kubernetes за стандартно використовує ліміт як значення запиту.
  • Ви можете вказати GPU як в limits, так і в requests, але ці два значення повинні бути рівними.
  • Ви не можете вказати requests для GPU без вказівки limits.

Ось приклад маніфесту для Podʼі, який запитує GPU:

apiVersion: v1
kind: Pod
metadata:
  name: example-vector-add
spec:
  restartPolicy: OnFailure
  containers:
    - name: example-vector-add
      image: "registry.example/example-vector-add:v42"
      resources:
        limits:
          gpu-vendor.example/example-gpu: 1 # запит на 1 GPU

Керуйте кластерами з різними типами GPU

Якщо в різних вузлах вашого кластера є різні типи GPU, то ви можете використовувати мітки вузлів і селектори вузлів для планування Podʼів на відповідних вузлах.

Наприклад:

# Позначте свої вузли з типом прискорювача, яким вони володіють.
kubectl label nodes node1 accelerator=example-gpu-x100
kubectl label nodes node2 accelerator=other-gpu-k915

Ця мітка ключа accelerator лише приклад; ви можете використовувати інший ключ мітки, якщо це зручніше.

Автоматичне позначення вузлів

Як адміністратор, ви можете автоматично виявляти та мітити всі ваши вузли з підтримкою GPU, розгорнувши Виявлення функцій вузлів Kubernetes (NFD). NFD виявляє апаратні функції, які доступні на кожному вузлі в кластері Kubernetes. Зазвичай NFD налаштовується таким чином, щоб оголошувати ці функції як мітки вузла, але NFD також може додавати розширені ресурси, анотації та заплямування вузла (node taints). NFD сумісний з усіма підтримуваними версіями Kubernetes. Типово NFD створює мітки функцій для виявлених функцій. Адміністратори можуть використовувати NFD, щоб також мітити вузли конкретними функціями, щоб на них можна було планувати лише Podʼи, які запитують ці функції.

Вам також потрібен втулок для NFD, який додає відповідні мітки до ваших вузлів; це можуть бути загальні мітки або вони можуть бути вендор-специфічними. Ваш вендор GPU може надати втулок від третіх сторін для NFD; перевірте їх документацію для отримання додаткової інформації.

apiVersion: v1
kind: Pod
metadata:
  name: example-vector-add
spec:
  # Виможете використовувати Kubernetes node affinity для планування цього Podʼа на вузол
  # який надає kind типу GPU, який потрібе його контейнеру для роботи
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: "gpu.gpu-vendor.example/installed-memory"
            operator: Gt # (greater than)
            values: ["40535"]
          - key: "feature.node.kubernetes.io/pci-10.present" # NFD Feature label
            values: ["true"] # (optional) only schedule on nodes with PCI device 10
  restartPolicy: OnFailure
  containers:
    - name: example-vector-add
      image: "registry.example/example-vector-add:v42"
      resources:
        limits:
          gpu-vendor.example/example-gpu: 1 # запит 1 GPU

Вендори GPU