1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/dev-99cloud-training-kubernetes

Клонировать/Скачать
class-01-Kubernetes-Administration.md 130 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 07.06.2025 06:35 02df06a

Администрирование Kubernetes

Внимание ⚠️

  • Перепечатка без разрешения запрещена

Предварительные условия

1.2 В чем различие между контейнерами и виртуальными машинами?

  • Принципы
    • Одна KVM-виртуальная машина — это один KVM-процесс, N ядер — это N потоков
    • Один контейнер — это группа процессов, изолированных и ограниченных LXC API, процессы в контейнере — это процессы хостовой системы, они соответствуют друг другу
  • Практические применения
    • Разные ядра
    • Разные операционные системы
    • Разные наборы команд CPU

1.3 Каковы отношения между Docker и контейнерными технологиями?

  • Что такое Docker ( Быстрый старт )?

    - Каковы отношения между Docker и контейнерами (почему Линус не заботится о Docker)?

  • Какие конкурентные продукты у Docker? CRI-O ?, другие командные инструменты для контейнеров: GitHub или Gitee### 1.4 Какова архитектура и пространство понятий Docker?

  • Пространство понятий

  • Модульная архитектура

1.5 Что такое так называемые безопасные контейнерные технологии?

  • Природная неуязвимость и природная уязвимость контейнеров

  • Kata Containers, PDF

  • Конкуренты: gVisor / firecracker / rustVM

1.6 Практика: Быстрый старт Docker

1.6.1 CentOS 7

  • На CentOS 7

    # Удаление старых версий Docker
    yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
    yum install -y yum-utils
    
    yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
    
    yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
    
    # Установить для запуска при старте системы и запустить сразу
    systemctl enable docker --now
    
    # запустить hello-world
    docker run hello-world

1.6.1.1 Построение образа на CentOS 7

  • Как создать образ? Как запустить и отладить контейнер? Github или Gitee

    $ mkdir ~/test
    $ cd ~/test
    $ wget https://gitee.com/dev-99cloud/lab-openstack/raw/master/src/docker-quickstart/app.py
    $ wget https://gitee.com/dev-99cloud/lab-openstack/raw/master/src/docker-quickstart/requirements.txt
    $ wget https://gitee.com/dev-99cloud/lab-openstack/raw/master/src/docker-quickstart/Dockerfile
    ```    # Если используется CentOS 7.x, нужно установить python3
    $ yum install python3 python3-pip
    
    # pip3 install -r requirements.txt
    $ pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
    
    $ python3 app.py
    * Запущено на http://0.0.0.0:80/ (Нажмите CTRL+C для остановки)
    
    # В это время можно получить доступ к http://<ip>/ через браузер
    
    $ docker build --tag=friendlyhello .
    
    # Можно посмотреть локальный список образов
    $ docker images
    
    # Удалить существующий контейнер testFlask
    $ docker rm testFlask 2>/dev/null
    
    # Запустить контейнер testFlask с образом 99cloud/friendlyhello:3.9.6, порт 80 контейнера отображается на порт 4000 хоста
    # Если использовать --rm параметр, контейнер будет удален после остановки
    $ docker run -p 4000:80 --name=testFlask 99cloud/friendlyhello:3.9.6
     * Запущено на http://0.0.0.0:80/ (Нажмите CTRL+C для остановки)
    
    # В это время можно получить доступ к http://<ip>:4000 через браузер
    
    # Если нужно запустить в фоновом режиме, можно использовать -d параметр
    $ docker run -d -p 4000:80 --name=testFlask 99cloud/friendlyhello:3.9.6
    
    # Войти в контейнер для отладки
    $ docker exec -it testFlask /bin/bash
    root@4224b69e7ee3:/app# env
    HOSTNAME=4224b69e7ee3
    PWD=/app
    HOME=/root
    NAME=World
    ...
    
    root@4224b69e7ee3:/app# ps -ef
    
    # Просмотреть логи контейнера
    $ docker logs -f testFlask
    ```    # Остановить контейнер
    $ docker stop testFlask
    
    # Запустить контейнер
    $ docker start testFlask
    
    # Создать новый образ из контейнера
    $ docker stop testFlask
    $ docker commit testFlask test-new
    
    # Удалить контейнер
    $ docker rm testFlask    # Удалить образ
    $ docker rmi friendlyhello
    $ docker rmi 99cloud/friendlyhello:3.9.6

1.6.2 ubuntu Ubuntu 18.04 / Ubuntu 20.04

  • На Ubuntu 18.04 / Ubuntu 20.04

    # Обновить репозитории
    apt-get update -y || yum update -y
    
    # Установить Docker
    apt-get install docker.io -y || yum install docker -y
    systemctl enable docker
    systemctl start docker
    
    # Проверить статус службы docker
    systemctl status docker
    
    # На Ubuntu необходимо изменить cgroup driver Docker на systemd
    # !! На CentOS старые версии Docker 1.13 по умолчанию используют systemd, не изменяйте этот файл, новый Docker 24+ требует изменения
    vi /etc/docker/daemon.json
    
    {
      "exec-opts": ["native.cgroupdriver=systemd"]
    }
    
    systemctl restart docker
    
    # Запустить hello-world
    docker run hello-world
    ```  >Примечание: С июля 2021 года в окружении Ubuntu kubeadmin по умолчанию версии 1.22+, поэтому необходимо изменить cgroup driver Docker на systemd (ранее использовался cgroup). В противном случае, при выполнении kubeadm init будет ошибка: `[kubelet-check] HTTP-запрос, равный 'curl -sSL http://localhost:10248/healthz' вернул ошибку: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.`
    >
    >Проверьте journalctl -x -u kubelet, чтобы увидеть: `Aug 07 15:10:45 ckalab2 kubelet[11394]: E0807 15:10:45.179485   11394 server.go:294] "Failed to run kubelet" err="failed to run Kubelet: misconfiguration: kubelet cgroup driver: \"systemd\" is different from docker cgroup driver: \"cgroupfs\""`
    >
    >См. официальную документацию: <https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/configure-cgroup-driver/>: `В версии v1.22, если пользователь не устанавливает поле cgroupDriver под KubeletConfiguration, kubeadm по умолчанию установит его в systemd.`
    >
    >Поэтому нам необходимо изменить cgroup driver Docker на systemd.
    >
    >Шаги изменения см. в: <https://stackoverflow.com/questions/43794169/docker-change-cgroup-driver-to-systemd>
    >
    >После завершения изменений, проверьте cgroup Docker, чтобы убедиться, что cgroup Docker теперь systemd: `sudo docker info | grep -i cgroup`- [Официальная документация Docker для начинающих](https://docs.docker.com/get-started/)### 1.7 Сетевая модель Docker
    
  • Режим Bridge

    # Docker-контейнеры не помещают network namespaces в стандартное место `/var/run/netns`, поэтому команда `ip netns list` ничего не покажет
    
    # Однако можно проверить `ll /proc/<pid>/ns`, если идентификаторы пространств имен двух процессов совпадают, значит они находятся в одном пространстве имен
    
    [root@cloud025 ns]# ll /proc/2179/ns/
    total 0
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 ipc -> ipc:[4026531839]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 mnt -> mnt:[4026531840]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 net -> net:[4026531956]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 pid -> pid:[4026531836]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 user -> user:[4026531837]
    lrwxrwxrwx 1 root root 0 Aug 10 11:58 uts -> uts:[4026531838]
    
    # Создание символической ссылки позволяет увидеть netns
    
    [root@cloud025 ns]# docker ps
    CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                  NAMES
    07297a3ac7ea        nginx:latest                  "/docker-entrypoin..."   29 минут назад      Up 29 минут         80/tcp                 devtest
    0935b08509a4        test-new                      "python app.py"          35 минут назад      Up 35 минут         0.0.0.0:5000->80/tcp   testNew
    c23c76dd779c        99cloud/friendlyhello:3.9.6   "python app.py"          37 минут назад      Up 36 минут         0.0.0.0:4000->80/tcp   testFlask
    
    [root@cloud025 ns]# docker inspect testFlask | grep -i pid
                "Pid": 1159,
                "PidMode": "",
                "PidsLimit": 0,
    [root@cloud025 ns]# mkdir -p /var/run/netns
    [root@cloud025 ns]# ln -s /proc/1159/ns/net /var/run/netns/testFlask
    [root@cloud025 ns]# ip netns list
    testFlask (id: 0)
    devtest (id: 2)
    testNew (id: 1)
    ```    # Вход в соответствующее пространство имен и просмотр ip, виртуальная сетевая карта в пространстве имен pod имеет link-netnsid равный 0
    [root@cloud025 ns]# ip netns exec testNew ip a
    ...
    44: eth0@if45: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
        link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 172.17.0.3/16 scope global eth0
          valid_lft forever preferred_lft forever
        inet6 fe80::42:acff:fe11:3/64 scope link
          valid_lft forever preferred_lft forever

    В root-namespace можно увидеть link-netnsid = 1, где 1 — это id namespace из ip netns list

    [root@cloud025 ns]# ip a 45: vethb6d08be@if44: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 0e:d9:14:d1:86:98 brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::cd9:14ff:fed1:8698/64 scope link valid_lft forever preferred_lft forever # Это пара veth, их можно определить по номерам и if

    Видим, что виртуальная сетевая карта root-namespace подключена к сетевому мосту docker0

    На CentOS необходимо установить bridge-utils: yum install bridge-utils

    [root@cloud025 ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.02428c25c112 no vethb6d08be virbr0 8000.525400d583b9 yes virbr0-nic

    
    [Можно попробовать перехватить трафик виртуальной сетевой карты, чтобы отслеживать входящий и исходящий трафик контейнеров](#81-мониторинг-логов-и-исправление-ошибок)
    
  • Host-режим

  • CNM

1.8 Хранилище данных Docker

  • Mount-режим

    [root@cka-studenta-1 ~]# mkdir testhaha
    [root@cka-studenta-1 ~]# docker run -d -it --name devtest -v "$(pwd)"/testhaha:/app nginx:latest
    Не удалось найти изображение 'nginx:latest' локально
    Попытка загрузки репозитория docker.io/library/nginx ...
    latest: Загрузка из docker.io/library/nginx ...
    ...
    7897813b7065a0390db335656443782895155655f263de6ee8264a6f2185fe16
    
    [root@cka-studenta-1 ~]# docker ps
    CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                  NAMES
    7897813b7065        nginx:latest                  "/docker-entrypoin..."   6 seconds ago       Up 4 seconds        80/tcp                 devtest
    b667b8e2f90b        99cloud/friendlyhello:3.9.6   "python app.py"          3 hours ago         Up 3 hours          0.0.0.0:4000->80/tcp   testFlask
    [root@cka-studenta-1 ~]# docker exec -it 7897813b7065 /bin/sh
    
    # ls
    app  bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
    # cd app
    # ls
    # echo fsdfasfdasdfsa > xxxxxxxx.txt
    # exit
    
    [root@cka-studenta-1 ~]# ls
    test  testhaha
    [root@cka-studenta-1 ~]# cd testhaha/
    [root@cka-studenta-1 testhaha]# ls
    xxxxxxxx.txt
    [root@cka-studenta-1 testhaha]# cat xxxxxxxx.txt
    fsdfasfdasdfsa
  • Volumn模式

  • Volumn模式 → Вolume режим## Урок 02: Концепции Kubernetes

2.1 Что такое K8S?

  • Что такое Kubernetes? Это инструмент для управления контейнерами. Почему его называют K8S?

  • Как K8S связан с Docker? См.: Container runtimes

    • Orchestration API -> Container API (cri-runtime) -> Kernel API (oci-runtime)

    • OCI: runC / Kata (и его предшественники runV и Clear Containers), gVisor, Rust railcar

      • Стандарт для создания образов контейнеров, то есть ImageSpec. Основные положения: в сжатом архиве образа контейнера находятся файлы в определенной структуре.
      • Инструкции, которые должны принимать контейнеры, и их поведение, то есть RuntimeSpec. Основные положения: контейнеры должны быть способны выполнять команды "create", "start", "stop", "delete" и т.д., и их поведение должно быть стандартизировано.
      • Docker упаковывает libcontainer и публикует его как runC в качестве примера реализации OCI.
    • CRI: Docker (с использованием dockershim), containerd (с использованием CRI-containerd), CRI-O, Frakti. Это набор gRPC интерфейсов, cri-api/pkg/apis/services.go:

      • Набор интерфейсов для управления контейнерами, включая создание, запуск и остановку контейнеров.
      • Набор интерфейсов для управления образами, включая загрузку, удаление образов и т.д.
      • Набор интерфейсов для управления PodSandbox (окружением контейнеров).
    • Обычный вызов K8S Docker

    • containerd-1.0, адаптация CRI через отдельный процесс CRI-containerd

    • containerd-1.1, адаптация CRI как плагин, встроенный в основной процесс containerd

    • CRI-O, более специализированный CRI-режим, чистый и совместимый с CRI и OCI, специализированный для Kubernetes

  • Архитектура CRI-O:

  • Как K8S связан с Borg? Начинался с Borg, но полностью отличается!

2.2 Для чего нужен K8S?

  • Какие преимущества имеет K8S? Для каких сценариев он подходит? Автоматизация управления: автономное восстановление, быстрое масштабирование, однокнопочное развертывание и обновление, резервное копирование и восстановление
  • Как найти документацию? K8S, OpenShift Origin
  • Другие ресурсы: slack, cncf, quay.io

2.3 K8S не решает какие проблемы?

  • Управление пользователями
  • Ограничение пропускной способности и отключение: istio
  • Мониторинг и аудит: prometheus / grafana / alertmanager / elasticsearch / fluent / kibana
  • Пользовательский интерфейс
  • Среда выполнения (middleware)
  • Поддержка облачных платформ

2.4 Какова модульная архитектура K8S?

2.5 Какие конкурентные продукты K8S существуют?

  • OpenShift

    • Какие преимущества имеет OpenShift (самый ценный продукт Red Hat) по сравнению с K8S?

  • VMware

  • KubeSphere

  • Rancher

2.6 Как развернуть K8S кластер?

  • kubeadm
  • Ansible

2.7 Практика: развертывание K8S

2.7.1 Развертывание K8S на CentOS 7

# 1. Отключение огнестрельного экрана (по умолчанию отключено в CentOS 7.9 образе на Aliyun, поэтому не требуется)
systemctl stop firewalld.service
systemctl disable firewalld.service

# 2. Отключение SELinux (по умолчанию отключено в CentOS 7.9 образе на Aliyun, поэтому не требуется)
# Изменение SELINUX=enforcing на SELINUX=disabled
vi /etc/selinux/config

# Немедленное отключение SELinux
setenforce 0

# 3. Отключение swap (по умолчанию отключено в CentOS 7.9 образе на Aliyun, поэтому не требуется)
# Комментирование строки swapoff xxx, чтобы избежать включения swap при перезагрузке
vi /etc/fstab
# Немедленное отключение swap
swapoff -a

# 4. Включение параметров ядра
# Постоянное активирование модуля ядра br_netfilter
echo br_netfilter | tee -a /etc/modules
modprobe br_netfilter
# Включение ipv4 forward
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

# Запуск конфигурации параметров ядра
sysctl -p
sysctl --system

# 5. Настройка репозитория Kubernetes, здесь мы используем репозиторий Aliyun
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
        http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
```# 6. [опционально] Удаление предыдущего установленного Docker (возможно, старой версии)
yum remove -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
```# 7. Установка новой версии docker
```bash
yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y

Запуск containerd и docker при старте системы

systemctl enable containerd --now
systemctl enable docker --now

Инициализация конфигурации containerd

containerd config default > /etc/containerd/config.toml
vi /etc/containerd/config.toml
# Обновление sandbox-образа
# sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.6"  замените на внутренний репозиторий
# Обновление SystemdCgroup, false замените на true
# SystemdCgroup = true
vi /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"]
}

Перезапуск containerd и docker

systemctl restart containerd
systemctl restart docker

8. Установка Kubernetes 1.23.3

export k8s_version="1.23.3"
# Установка репозитория для версии 1.23.3
yum install -y kubelet-${k8s_version}-0 kubeadm-${k8s_version}-0 kubectl-${k8s_version}-0  --disableexcludes=kubernetes

Запуск kubelet

systemctl restart kubelet
systemctl enable kubelet

Предварительная загрузка образов

kubeadm config images pull --kubernetes-version ${k8s_version} --image-repository registry.aliyuncs.com/google_containers

Установка master

kubeadm init --image-repository registry.aliyuncs.com/google_containers --kubernetes-version=v${k8s_version} --pod-network-cidr=10.244.0.0/16

Конфигурация файла конфигурации kubectl

mkdir $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Установка Calico

kubectl apply -f https://gitee.com/dev-99cloud/lab-openstack/raw/master/src/ansible-cloudlab-centos/playbooks/roles/init04-prek8s/files/calico-${k8s_version}.yml
```Данный сценарий позволяет K8S работать с Docker. Если требуется использовать containerd напрямую, команда `kubeadm init` должна быть изменена на:
```bash
kubeadm init --cri-socket unix:///run/containerd/containerd.sock --image-repository registry.aliyuncs.com/google_containers --kubernetes-version=v${k8s_version} --pod-network-cidr=10.244.0.0/16

Для получения дополнительной информации см. Github или Gitee. Если требуется переустановка, можно выполнить kubeadm reset -f

  • Проверка состояния кластера

    # Проверка состояния узлов
    $ kubectl get nodes
    NAME           STATUS   ROLES                                           AGE    VERSION
    cka-node1      Ready    control-plane,master,worker                     30s    v1.23.3
  • Установка/добавление GPU-узла в кластер (только если CRI — Docker)

    # 1. Установка драйвера для графического адаптера на узле
    # См. https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#abstract
    
    # 2. Установка Docker
    # См. выше
    
    # 3. Установка nvidia-docker
    distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \ && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo
    
    yum install -y nvidia-docker2
    # См. документацию по установке на Ubuntu и других системах https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#installing-on-centos-7-8
    ```    # 4. Изменение по умолчанию контейнерного runtime
    # Редактирование файла `/etc/docker/daemon.json`, добавление следующих полей:
    {
        "default-runtime": "nvidia",
        "runtimes": {
            "nvidia": {
                "path": "nvidia-container-runtime",
                "runtimeArgs": []
            }
        }
    }
    systemctl daemon-reload && systemctl restart docker    # 5. Установка кластера Kubernetes или добавление узла в кластер
    
    # 6. Установка плагина vGPU
    # * Официальный плагин от Nvidia поддерживает только целый графический адаптер, что не подходит для нашего сценария
    # * Плагин gpushare от Alibaba Cloud поддерживает частичное использование GPU, но не обеспечивает изоляцию памяти
    # * Плагин vGPU от 4Paradigm поддерживает разбиение графического адаптера и изоляцию памяти
    # Установка плагина vGPU от 4Paradigm см. `https://github.com/4paradigm/k8s-device-plugin/blob/master/README_cn.md#Kubernetes%E5%BC%80%E5%90%AFvGPU%E6%94%AF%E6%8C%81`
    

2.7.2 Установка k8s на Ubuntu 18.04 / 20.04

  1. Установка kubeadm

    # Включение прослушивания bridged трафика
    cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
    net.bridge.bridge-nf-call-ip6tables = 1
    net.bridge.bridge-nf-call-iptables = 1
    EOF
    sudo sysctl --system

    Установка kubeadm

    apt-get update && apt-get install -y apt-transport-https curl curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add - cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list deb https://apt.kubernetes.io/ kubernetes-xenial main EOF apt-get update -y && apt-get install -y kubelet kubeadm kubectl

    Перезапуск kubelet

    systemctl daemon-reload systemctl restart kubelet

    Проверка статуса kubelet, до присоединения к k8s кластеру, статус kubelet будет постоянно активироваться

    systemctl status kubelet

  2. Создание k8s кластера с помощью kubeadm

    # Запуск k8s кластера
    kubeadm init --pod-network-cidr 192.168.1.90/16
    ```    # Настройка kubectl клиента
    mkdir -p $HOME/.kube
    sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
    sudo chown $(id -u):$(id -g) $HOME/.kube/config
  3. В данный момент можно заметить, что узлы не готовы, что приводит к невозможности распределения coredns. Далее необходимо: установка сетевого плагина, список плагинов, здесь мы будем использовать Calico.

    # Добавление сетевого плагина
    # Установка оператора Tigera Calico и пользовательских ресурсов
    kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
    # Установка Calico путем создания необходимых пользовательских ресурсов
    kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml
    
    # Просмотр состояния pods и узлов
    kubectl get pods --all-namespaces
    kubectl get nodes

2.8 Что такое Pod?

  • Какова связь между Pod и контейнерами? ```bash

    Pod содержит pause контейнер и бизнес-контейнер, которые делят ipc / net / user namespaces

    [root@cloud025 ~]# ll /proc/10982/ns/ total 0 lrwxrwxrwx 1 root root 0 авг 10 16:14 ipc -> ipc:[4026532513] lrwxrwxrwx 1 root root 0 авг 10 15:33 mnt -> mnt:[4026532592] lrwxrwxrwx 1 root root 0 авг 10 15:33 net -> net:[4026532516] lrwxrwxrwx 1 root root 0 авг 10 15:33 pid -> pid:[4026532594] lrwxrwxrwx 1 root root 0 авг 10 16:14 user -> user:[4026531837] lrwxrwxrwx 1 root root 0 авг 10 16:14 uts -> uts:[4026532593]
    [root@cloud025 ~]# ll /proc/10893/ns/
    total 0
    lrwxrwxrwx 1 root root 0 10 авг 15:33 ipc -> ipc:[4026532513]
    lrwxrwxrwx 1 root root 0 10 авг 15:33 mnt -> mnt:[4026532511]
    lrwxrwxrwx 1 root root 0 10 авг 15:33 net -> net:[4026532516]
    lrwxrwxrwx 1 root root 0 10 авг 16:15 pid -> pid:[4026532514]
    lrwxrwxrwx 1 root root 0 10 авг 16:15 user -> user:[4026531837]
    lrwxrwxrwx 1 root root 0 10 авг 16:15 uts -> uts:[4026532512]
    ```- Почему основной единицей планирования является pod, а не контейнер?
    

2.9 Запуск pod

  • YAML pod

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
      labels:
        env: test
    spec:
      containers:
      - name: nginx
        image: nginx
  • Запуск pod

    # Запуск pod nginx
    kubectl apply -f nginx.yaml
    
    # Просмотр информации о pod
    kubectl describe pod nginx
    
    # Удаление маркера, чтобы pod мог быть запланирован на master
    kubectl taint nodes $(hostname) node-role.kubernetes.io/master:NoSchedule-
    
    # Просмотр запланированных pod
    kubectl get pods
    
    # Просмотр запущенных контейнеров
    docker ps | grep nginx
  • Развертывание Python-приложения на платформе контейнеризации

    • Что мы будем делать?
    • Какие предварительные условия необходимы?
      • Установленная Python 3.6+ на локальной машине
      • Установленный Git (необязательно)
      • Установленный Docker
      • Платформа Kubernetes
    • Код
      • Файл requirements.txt содержит только одну строку: flask, это файл с зависимостями

      • Файл main.py содержит логику приложения ```python from flask import Flask import os import socket

        app = Flask(name)

        @app.route("/") def hello(): html = "

        Hello {name}!

        "
        "Hostname: {hostname}
        " return html.format(name=os.getenv("NAME", "мир"), hostname=socket.gethostname())
    mkdir ~/test-hello-world
    cd ~/test-hello-world/
    
    wget https://gitee.com/dev-99cloud/training-kubernetes/raw/master/src/hello-python/main.py
    wget https://gitee.com/dev-99cloud/training-kubernetes/raw/master/src/hello-python/requirements.txt
    wget https://gitee.com/dev-99cloud/training-kubernetes/raw/master/src/hello-python/Dockerfile
    wget https://gitee.com/dev-99cloud/training-kubernetes/raw/master/src/hello-python/deployment.yaml
  • Локальное выполнение:

    • Установка зависимостей: pip3 install -r requirements.txt
    • Локальное выполнение: python3 main.py, что запускает локальный веб-сервер для разработки.
    • Вы можете открыть браузер и перейти по адресу http://<IP>:5000.
  • Docker-файл для построения образа, в директории находится файл Dockerfile

    FROM python:3.7
    
    RUN mkdir /app
    WORKDIR /app
    ADD . /app/
    RUN pip install -r requirements.txt
    
    EXPOSE 5000
    CMD ["python", "/app/main.py"]
    ```- Построение Docker-образа и его запуск
    - Команда для построения образа: `docker build -f Dockerfile -t hello-python:latest .`
    - После построения образа, вы можете увидеть его с помощью команды `docker image ls`.
    - Запустите образ: `docker run -p 5001:5000 hello-python`.
    - Перейдите по адресу `http://<IP>:5001`, чтобы увидеть строку `Hello from Python!`.
  • Убедиться, что Kubernetes и kubectl работают корректно

    • Команда: kubectl version, если команда выполнена без ошибок, значит kubectl установлен.
    • Команда: kubectl get nodes, если возвращаются данные о узлах, значит K8S работает корректно, а kubectl настроен правильно.
  • Файл YAML для развертывания в Kubernetes: ```yaml apiVersion: v1 kind: Service metadata: name: hello-python-service spec: type: NodePort selector: app: hello-python ports: - protocol: "TCP" port: 6000 targetPort: 5000 nodePort: 31000

    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-python
    spec:
      selector:
        matchLabels:
          app: hello-python
      replicas: 4
      template:
        metadata:
          labels:
            app: hello-python
        spec:
          containers:
          - name: hello-python
            image: hello-python:latest
            imagePullPolicy: Never
            ports:
            - containerPort: 5000
    • Разверните Deployment на K8S платформе

    • Выполните команду: kubectl apply -f deployment.yaml

    • Выполните команду: kubectl get pods, проверьте pods

    • Затем открыть браузер и перейти по адресу http://<IP>:31000, чтобы увидеть сообщение Hello from Python!

    • Эксперимент

      # Обратите внимание на связь service / deployment и pod, используя метки
      kubectl get pods -l app=hello-python
      kubectl get deploy
      kubectl get deployment
      
      kubectl get svc
      kubectl get service
      kubectl get pods -l app=hello-python -o wide
      
      # Доступ к приложению
      curl http://<pod-ip>:5000
      curl http://<cluster-ip>:6000
      curl http://<host-ip>:31000

Урок 03: концепции K8S

3.1 Что такое YAML?

  • Как понять YAML? Списки / Словари / Числа / Строки / Логические значения

3.2 Что такое Namespace & Quota?

  • Namespace & изоляция арендаторов

  • Эксперимент: namespace & quota

    # Создайте namespace
    kubectl create namespace quota-mem-cpu-example
    ```    # Ограничьте ресурсы для этого namespace
    kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu.yaml --namespace=quota-mem-cpu-example
    
    # Просмотрите детали ограничений
    kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml
    
    # Создайте 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
    
    # Просмотрите детали ограничений, проверьте использованные ресурсы
    kubectl get resourcequota mem-cpu-demo --namespace=quota-mem-cpu-example --output=yaml
    
    # Попытайтесь запустить второй pod, из-за ограничений запуск не удался
    kubectl apply -f https://k8s.io/examples/admin/resource/quota-mem-cpu-pod-2.yaml --namespace=quota-mem-cpu-example
    
    # Ошибка от сервера (Запрещено): ошибка при создании "examples/admin/resource/quota-mem-cpu-pod-2.yaml": pods "quota-mem-cpu-demo-2" запрещен: превышен лимит: mem-cpu-demo, запрошен: requests.memory=700Mi, использовано: requests.memory=600Mi, ограничен: requests.memory=1Gi

    Удаление неймспейса

    kubectl delete namespace quota-mem-cpu-example

3.3 Что такое Deployment и ReplicaSet?

  • Эксперимент: Pod Label и Replica Controller

  • Эксперимент: Deployment

    kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
    kubectl get pods
    kubectl get deployments
    
    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 --record
    kubectl get pods
    
    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.161 --record=true
    kubectl get pods
    ```    kubectl rollout history deployment.v1.apps/nginx-deployment
    kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
    kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2
    kubectl get pods
    # Если оригинальный pod с ошибкой ErrorImagePull продолжает неудачно запускаться, можно удалить его командой kubectl delete pod <pod-name>    kubectl scale deployment.v1.apps/nginx-deployment --replicas=10
    kubectl get pods

3.4 Что такое Services?

  • Основные понятия: Service

  • Эксперимент: Service

    Создайте файл service.yaml с содержимым:

    apiVersion: v1
    kind: Service
    metadata:
      name: hello-python-service
    spec:
      type: NodePort
      selector:
        app: nginx
      ports:
      - protocol: "TCP"
        port: 6000
        targetPort: 80
        nodePort: 31000
    kubectl apply -f service.yaml
    # После создания сервиса можно посмотреть iptables
    $ iptables -t nat -n -L -v
    
    # Вы увидите следующее, запросы на сервис IP распределяются между pod'ами
    Chain KUBE-SVC-C5I534CP62HG2LN3 (2 references)
    pkts bytes target     prot opt in     out     source               destination
        0     0 KUBE-SEP-FBKE4RDEE4U4O7NI  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/hello-python-service */ statistic mode random probability 0.50000000000
        0     0 KUBE-SEP-ZIK7TOCY5OVWTBMA  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/hello-python-service */
    
    kubectl get pods -o wide
    kubectl run curl --image=radial/busyboxplus:curl -i --tty

    nslookup hello-python-service curl http://hello-python-service.default.svc.cluster.local:6000

    В разных namespaces или на узлах хоста требуется полное доменное имя (FQDN)

    nslookup hello-python-service.default.svc.cluster.local 10.96.0.10

    
    
  • LB типа Service: metallb

3.5 Эксперимент: K8S Dashboard

3.6 Эксперимент: Как K8S публикует сервисы и масштабирует их?

3.7 DaemonSet & StatefulSet- DaemonSet

```yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      containers:
      - name: fluentd-elasticsearch
        image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
      terminationGracePeriodSeconds: 30
```
  • StatefulSet

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
      labels:
        app: statefulset
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: None
      selector:
        app: statefulset
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: statefulset # должно совпадать с .spec.template.metadata.labels
      serviceName: "nginx"
      replicas: 3 # по умолчанию равно 1
      template:
        metadata:
          labels:
            app: statefulset # должно совпадать с .spec.selector.matchLabels
        spec:
          terminationGracePeriodSeconds: 10
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
              name: web
    kubectl apply -f test-statefulset.yaml
    kubectl get pods
    
    # headless сервис, у которого нет IP-адреса сервиса
    kubectl run curl --image=radial/busyboxplus:curl -i --tty
    nslookup nginx
    
    # выполните команду nslookup nginx несколько раз, чтобы увидеть, что порядок возврата каждый раз разный
    # headless сервис использует изменение порядка возврата DNS для реализации балансировки нагрузки
    ```### 3.8 Эксперимент: Операции с ETCD
    
  • Установка и получение

    # Установка etcd-client в Ubuntu с помощью apt-get
    root@ckalab001:~# apt install etcd-client
    
    # В других окружениях можно скачать двоичный файл с https://github.com/etcd-io/etcd/releases
    
    root@ckalab001:~# ps -ef | grep api | grep -i etcd
    root       24761   24743  3 10:17 ?        00:06:53 kube-apiserver --advertise-address=172.31.43.206 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/etc/kubernetes/pki/ca.crt --enable-admission-plugins=NodeRestriction --enable-bootstrap-token-auth=true --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --insecure-port=0 --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=6443 --service-account-issuer=https://kubernetes.default.svc.cluster.local --service-account-key-file=/etc/kubernetes/pki/sa.pub --service-account-signing-key-file=/etc/kubernetes/pki/sa.key --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/etc/kubernetes/pki/apiserver.crt --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    
    # Установка переменной окружения ETCDCTL_API=3
    root@ckalab001:~# export ETCDCTL_API=3
    ```    root@ckalab001:~# etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" --endpoints=https://127.0.0.1:2379 put /firstkey trystack
    OK
    ```    root@ckalab001:~# etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" --endpoints=https://127.0.0.1:2379 get /firstkey
    /firstkey
    trystack
    ```    ```bash
    # список всех ключей
    etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" --endpoints=https://127.0.0.1:2379 get --prefix --keys-only ""
    
    # список всех ключей и значений
    etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" --endpoints=https://127.0.0.1:2379 get --prefix ""
    
    # резервное копирование и восстановление
    etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" snapshot save a.txt
    
    # шаги восстановления резервной копии
    # 1. остановить службу ETCD
    # 2. удалить каталог данных ETCD
    # 3. восстановить снимок
    # 4. перезапустить службу ETCD
    
    etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" snapshot restore a.txt

    Эксперимент по резервному копированию и восстановлению:

    [root@ckalab003 ~]# etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" put /firstkey-1 test1
    OK
    
    [root@ckalab003 ~]# etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" get /firstkey-1
    /firstkey-1
    test1
    
    [root@ckalab003 ~]# etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt  --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key snapshot save backup1
    ...
    Snapshot saved at backup1
    ```    [root@ckalab003 ~]# etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" put /firstkey-2 test2
    OK

    [root@ckalab003 ~]# etcdctl --cert="/etc/kubernetes/pki/apiserver-etcd-client.crt" --key="/etc/kubernetes/pki/apiserver-etcd-client.key" --cacert="/etc/kubernetes/pki/etcd/ca.crt" get /firstkey-2 /firstkey-2 test2 [root@ckalab003 ~]# mv /etc/kubernetes/manifests/etcd.yaml . [root@ckalab003 ~]# rm -rf /var/lib/etcd/* [root@ckalab003 ~]# etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key snapshot restore backup1 --data-dir /var/lib/etcd ... 2023-06-17T17:35:25+08:00 info snapshot/v3_snapshot.go:272 restored snapshot {"path": "backup1", "wal-dir": "/var/lib/etcd/member/wal", "data-dir": "/var/lib/etcd", "snap-dir": "/var/lib/etcd/member/snap"}

    [root@ckalab003 ~]# mv ./etcd.yaml /etc/kubernetes/manifests/

    [root@ckalab003 ~]# etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key get /firstkey-1 /firstkey-1 test1 [root@ckalab003 ~]# etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key get /firstkey-2

    
    `/firstkey-1` виден, а `/firstkey-2` не виден, что соответствует ожиданиям.
    
    Восстановление кластера:
    
    ```bash
    etcdctl --name ckalab001 --initial-cluster ckalab001=https://139.224.191.4:2380 --initial-cluster-token etcd-cluster --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --cacert=/etc/kubernetes/pki/etcd/ca.crt --initial-advertise-peer-urls https://139.224.191.4:2380 snapshot restore /root/backup-1 --data-dir /var/lib/etcd

3.9 Что такое статический Pod?

  • Статический Pod

  • /etc/kubernetes/manifests

    root@CKA003:~# ps -ef | grep kubelet
    root     10572     1  2 09:37 ?        00:07:41 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cgroup-driver=cgroupfs --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2 --resolv-conf=/run/systemd/resolve/resolv.conf

Урок 04: Аутентификация и безопасность K8S### 4.1 Что такое 3A в K8S?

В Kubernetes (K8S) термин "3A" относится к трем основным аспектам аутентификации и авторизации:

  1. Аутентификация: Процесс идентификации пользователя или сущности, которая пытается получить доступ к системе. В K8S это может быть выполнено с помощью различных методов, таких как токены, сертификаты X.509, OAuth и другие.

  2. Авторизация: Процесс проверки прав доступа пользователя или сущности после успешной аутентификации. В K8S это обычно реализуется с помощью объектов Role и RoleBinding или ClusterRole и ClusterRoleBinding.

  3. Управление доступом: Процесс контроля доступа к ресурсам системы на основе аутентификации и авторизации. В K8S это может включать использование объектов типа NetworkPolicy для контроля сетевого доступа, а также других механизмов для управления доступом к различным ресурсам.

Эти три аспекта работают вместе, чтобы обеспечить безопасный и надежный доступ к кластеру Kubernetes. - Аутентификация / Авторизация / Управление доступом

  • Процесс аутентификации K8S? Аутентификация, Авторизация (RBAC / ABAC / WebHook), Управление доступом

4.2 Как настроить kubectl?

4.3 Как K8S обеспечивает сетевую безопасность?

  • Выбор сетевого решения Kubernetes? flannel, calico, ovs, ovn

4.4 Что такое пользователи и роли?

4.5 Практика: добавление пользователей & привязка ролей- Практика: создание обычного пользователя для использования инструмента kubectl

```console
root@CKA003:~# kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin

root@CKA003:~# kubectl create namespace ns1
namespace/ns1 created
root@CKA003:~# kubectl create namespace ns2
namespace/ns2 created

root@CKA003:~# useradd -m -d /home/poweruser -s /bin/bash poweruser
root@CKA003:~# passwd poweruser
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@CKA003:~# cd /home/poweruser

root@CKA003:/home/poweruser# openssl genrsa -out poweruser.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
......................+++++
..........................................................+++++
e is 65537 (0x010001)

root@CKA003:/home/poweruser# openssl req -new -key poweruser.key -out poweruser.csr -subj "/CN=poweruser/O=ns1"
Can't load /root/.rnd into RNG
139904427348416:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd

root@CKA003:/home/poweruser# cat poweruser.key
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAt07RnBpZFkux9vEmSUDiHsVFtAu1FmJQjia0ls2A/fbMBt2T
...
njzqykeT5cLiixwUf6x35nF2r5VydsZMHypk6dgPgC6LikTbfsL0
-----END RSA PRIVATE KEY-----

root@CKA003:/home/poweruser# cat poweruser.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICZzCCAU8CAQAwIjESMBAGA1UEAwwJcG93ZXJ1c2VyMQwwCgYDVQQKDANuczEw
...
O5+ia4aC6Hn/lMsRNYzeSK/ovjMuzH7gjnYEogG8QdpIVLFF1a1D2/S1kQ==
-----END CERTIFICATE REQUEST-----

root@CKA003:/home/poweruser# openssl x509 -req -in poweruser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out poweruser.crt -days 60
Signature ok
subject=CN = poweruser, O = ns1
Getting CA Private Key
```    root@CKA003:/home/poweruser# chmod 777 poweruser.*
root@CKA003:/home/poweruser# cd
```    root@CKA003:~# kubectl config set-credentials poweruser --client-certificate=/home/poweruser/poweruser.crt --client-key=/home/poweruser/poweruser.key
Пользователь "poweruser" создан.
root@CKA003:~# mkdir /home/poweruser/.kube
root@CKA003:~# cp .kube/config /home/poweruser/.kube
root@CKA003:~# chown -R poweruser:poweruser /home/poweruser/.kube
root@CKA003:~# su poweruser    poweruser@CKA003:/root$ cd
 poweruser@CKA003:~$ cd .kube/
 poweruser@CKA003:~/.kube$ vi config    # Откройте и отредактируйте файл /home/poweruser/.kube/config
  # Удалите пользователя kubernetes-admin и его сертификаты, включая
  root@CKA003:~# vi /home/poweruser/.kube/config
  # Удалите следующее содержимое и измените пользователя в контексте на poweruser
  #- name: kubernetes-admin
  #  user:# данные-ключа-клиента: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJS2xBVjZ0SGg0R3d3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TVRBNE1Ea3dOREkyTVRaYUZ3MHlNakE0TURrd05ESTJNVGhhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTNQT2J2Ly9JK2FrQkFzR3YKeERPRlpEYnF6c1AwWCtTOExRa1dmMGhCKzBOc3dySHhQR0VTcmw0OXhhbUI4RVVod0NQTG9BTTVWZkRxOVNXWApsV1A0ZE1qM1cyclFFdmp4ZnlZZDhjbWdReTNVWndJSE1tUCtRS0NJNVdwV0tRcldlTE8rTXZUd1hjM0wwcWUyClRiMEhlQ0FjMy9FdWlZcFVvRjFrVndESGl5N3BpQTVTSVpsbGhub1Q1NjAyb3RDV0RYWDg1L09UOGRtWEViQTAKSlpkeDdhM2krT3I5dVdHQk1aaldEQms2dk02U3QxQm00MGhIcGM4VGpQSERZOVdwcldoVXZOUXpuNEtTSDdYQQpJQ1h1d3VmVVRVQXpGVWdobm5yZXc5MzFXd3NXejJpUlN6dldzUGdDOE8xMllNQmZQWGhoeXRRazFMSTdQaEVHCm1qbGw2UUlEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFGN3hOdWs5eEhtUDUydjh6REU3Uk9MTEU5cVlTMVhrd3I5Zgp2eTEvMVZKTklXV2Jlc3hQMUsvTm1JMTVJUS9TWWtoKzVoMWE4eGppRkc4dk5ia3BMNDAxUVpKTy93SEJYWU5WCkdXR0V4ZXRpdG5KSlpSam81NWN2NHpwTUNSOWZadmIvaGc3RzBCQmw2RW84MHNzSDJYdG5Ca04vV0VPY0VoZk8KQ2pFYkNGREVmbzFMRG9aTDZjZGwxU3dnTFlWZ3l6ZlQrcjdJVmNaZEVuWXZjMDhZNThLKzVPL3dMZnBaSjQxdwpVbWtQdzE3R0sxUjVUQUJrWHBRRFE5M3pwTnZ6M1k3ZUZlQTM5V2ZocGthZU4yRUpTQ3ZwdzdNaGQyTCtVUHFYCkdOSVpkNVYyOGF3d21GUUhURm9DdlZ0SUszMEFYMWQyTTkvNkpwMEUvNDdNZE1meWFNWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= # client-certificate-data:LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBM1BPYnYvL0krYWtCQXNHdnhET0ZaRGJxenNQMFgrUzhMUWtXZjBoQiswTnN3ckh4ClBHRVNybDQ5eGFtQjhFVWh3Q1BMb0FNNVZmRHE5U1dYbFdQNGRNajNXMnJRRXZqeGZ5WWQ4Y21nUXkzVVp3SUgKTW1QK1FLQ0k1V3BXS1FyV2VMTytNdlR3WGMzTDBxZTJUYjBIZUNBYzMvRXVpWXBVb0Yxa1Z3REhpeTdwaUE1UwpJWmxsaG5vVDU2MDJvdENXRFhYODUvT1Q4ZG1YRWJBMEpaZHg3YTNpK09yOXVXR0JNWmpXREJrNnZNNlN0MUJtCjQwaEhwYzhUalBIRFk5V3ByV2hVdk5Rem40S1NIN1hBSUNYdXd1ZlVUVUF6RlVnaG5ucmV3OTMxV3dzV3oyaVIKU3p2V3NQZ0M4TzEyWU1CZlBYaGh5dFFrMUxJN1BpRUdtamxsNlFJREFRQUJBb0lCQUYvUmtYaTVMMm45dmI5NQpTWVUzcHFCb0pIb1lockRUWER2WGxoY0t1ZnFDS2ZkZy9iSG1reGhsTERxOUlPbVd3V1UyNE1aNnYzR2lzZkl3CkpFV1gvaFovVks0amF5cmZKTE8wVHdZZEgxQWkzdHJ4Q1RmMEh6M2RvS0NFOWVxRWxhL3dtd28wS00wMVF6QU8KcFVPZk4wOEQ5aUd6MFMrNmVxcTA5Wis1YWMvVUNLQWgyczNzL2hjSHlEUk9yeFpCd1JDY3RoTXlGdU03SklIaQp3dXZKTEJqZkdXVnhGNGdkWWRiMXdIMFdNZ2txanhkVDEvcWthSEE2TmtNTEZrRnZNdzRSTDdpbUdmMmcrVDBGCnB1WVorRW9oRGVtTEpCVGF5ZTM3ZjlST3pRRTBtTFNEa1E5WnRmZDBMOE5telJ2eUIzTTdXV0FtVXhaM2FuTk4KSE5RV0k1VUNnWUVBM3RNZVNUcEhyMU1YcmhzdTZ6SlNwUEh6a3hQVE95VlE3NjY5cjJ6ay9UeDNVUUFzSktoSAovNFlTWUNyTzh6Q09HY09va1EzWUNCQlJBU2dlNmp5ekR3L0duRmxzT3o4MVdIYWp0NlRjKzVzVFh3dk9EUkxrCmxCSDBHblhIbHJ0M2FzV1JBRS9xVVlzeVJhUEs4a0tzRHRidlk4RllnbzB0NHFCYnBTelo2VHNDZ1lFQS9ka1oKSGtNZG5OWHFYTk9jTnJ0YzlDaGRjeUpXR2ppMEpPQ3dSVVNyNzMxMGFhcm1JU2tOR0Q1NTNUSlhnRlBRb2t3QQpRaWNVQ2VUY0t3SkM0VUUrWEVYaEZKU3RwNmR4VDJlR1AyU2ZtMUxENUp2cWZiU2xuNE81Ni9MTTY1Ym9BbVJCCmtjMVl1M0ZjZ1BiYlRIMXI4K2FqK1IxUG5NdTBsT2ZRNUJHeUd5c0NnWUJ2MTZvSStXN0h5cjVGRHJIakxmUWIKaExKTXJaUEZ5VG94eEJURHU3WElnaFFsblIrTEdzaGdzbXdBeHh2dkp2ejhZNS8xaHV4YlI4MVE5bEZtSXllQgpOTnJzMlZtZzkxNFFWQ1JpNWlaaFIvcFdKN2U2Q2pTZk9jKzdoRWkxR00yYzB5T3Y4MnphbHpLWmo5Z3E5MW9qCmJMRGw4a001N0NFTDhveHRnUEN6eHdLQmdCQXZ6c1U2UEdJcTFkWHpmR3VWQ1BsY3RaREk2THFsVVA5bEFIaDYKUjRodU5JUmtiR1pDNnQzWDVnZHYxVnFPZmFoTHRseUJoMnFXR0YvNXRmQU5LLy9RU09qNkRoUzV2YVQxa2Y3cQoySzZiMlhmelpVRjh5bTdsbmw1b1RoN2JzWkd0ZU96bUxqbE5vanRyQWxMZlVJbnQ5QmpIZ0xNYjNqajhpenB2CjBtNmZBb0dBUGtqY0lhWE93dnhXdFhReCttWEVQWkQxUEoyeElWN240am9hNHJ4L0pqNmx6dmRhQ2xmZzlSYjIKVC81QjREcWViNC9udlA0WUpBSTR4RUxCTVI0N1EvbVBHVkFldXc0SmgyZ3RBdmx3S0RDOGliaTg0NzlKSi9qOQo2T2gzNkR1UVpoeU56QU1namxCZG5oNEk0UitYYmZZcEo2UjNSUE5jM2dWT1cvVkFPc009Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==     ```Вот файл kubeconfig:```yaml

apiVersion: v1 clusters:

  • cluster: certificate-authority-data: LS0tLS1CRUdJTiBD...S0tLS0tCg== server: https://172.19.221.124:6443 name: kubernetes contexts:
  • context: cluster: kubernetes user: poweruser name: kubernetes-admin@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users:
  • name: poweruser user: client-certificate: /home/poweruser/poweruser.crt client-key: /home/poweruser/poweruser.key

Затем приведите пользователя poweruser к соответствующим правам

```console
# Попытка получить pod завершится ошибкой, так как у нас нет rbac-прав для этого пользователя
poweruser@CKA003:~/.kube$ kubectl get pods
Error from server (Forbidden): pods is forbidden: User "poweruser" cannot list resource "pods" in API group "" in the namespace "default"

Приведите пользователя poweruser к соответствующим правам

root@CKA003:~# vi pod-read.yaml
root@CKA003:~# cat pod-read.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: ns1
  name: pod-reader
rules:
- apiGroups: [""] # "" указывает на основной API-группу
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

root@CKA003:~# kubectl apply -f pod-read.yaml
role.rbac.authorization.k8s.io/pod-reader created
```root@CKA003:~# vi role-binding.yaml
root@CKA003:~# cat role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows the user "poweruser" to read pods in the namespace "ns1".
# You need to already have a role named "pod-reader" in this namespace.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: ns1
subjects:
# You can specify more than one "subject"
- kind: User
  name: poweruser # "name" is case-sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" points to the binding to a role / cluster role
  kind: Role # this should be Role or ClusterRole
  name: pod-reader # this should correspond to the name of the role or cluster role to which you want to bind
  apiGroup: rbac.authorization.k8s.io```Теперь пользователь poweruser получает ошибку при попытке получить список pod в стандартном пространстве имен default.

```console
# Ошибка -- нет прав на получение списка pod в пространстве имен default
poweruser@CKA003:~/.kube$ kubectl get pods
# Возвращаемое значение: Error from server (Forbidden): pods is forbidden: User "poweruser" cannot list resource "pods" in API group "" in the namespace "default"

# Ок
poweruser@CKA003:~/.kube$ kubectl get pods -n ns1
  • Эксперимент: создание обычного пользователя и предоставление ему прав суперадминистратора

    root@CKA003:~# openssl genrsa -out superuser.key 2048
    Generating RSA private key, bk 2048 bit long modulus (2 primes)
    ..........................+++++
    ........................................................................................+++++
    e is 65537 (0x010001)
    
    root@CKA003:~# openssl req -new -key superuser.key -out superuser.csr -subj "/CN=superuser/O=system:masters"
    Can't load /root/.rnd into RNG
    139767359660480:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
    
    root@CKA003:~# openssl x509 -req -in superuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out superuser.crt -days 60
    Signature ok
    subject=CN = "superuser", O = system:masters
    Getting CA Private Key

    system:masters — это стандартная группа cluster-rolebinding, см. также: Как просмотреть членов субъекта с типом Group

    root@CKA003:~# kubectl get clusterrolebindings -o go-template='{{range .items}}{{range .subjects}}{{.kind}}-{{.name}} {{end}} {{" - "}} {{.metadata.name}} {{"\n"}}{{end}}' | grep "^Group-system:masters"
    Group-system:masters   -  cluster-admin

    ```console
    root@CKA003:~# kubectl config set-credentials superuser --client-certificate=superuser.crt --client-key=superuser.key
    Пользователь "superuser" установлен.
    root@CKA003:~# tail -f .kube/config
    ...
    - name: poweruser
    user:
        client-certificate: /home/poweruser/poweruser.crt
        client-key: /home/poweruser/poweruser.key
    - name: superuser
    user:
        client-certificate: /root/superuser.crt
        client-key: /root/superuser.key
    ```    
    root@CKA003:~# kubectl config set-context superuser-context --cluster=kubernetes --user=superuser
    Контекст "superuser-context" создан.

    root@CKA003:~# kubectl config get-contexts
    CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
    *         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin
            superuser-context             kubernetes   superuser

    root@CKA003:~# kubectl config use-context superuser-context
    Переключен на контекст "superuser-context".

    root@CKA003:~# kubectl config get-contexts
    CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
            kubernetes-admin@kubernetes   kubernetes   kubernetes-admin
    *         superuser-context             kubernetes   superuser

    root@CKA003:~# kubectl get pods
    NAME       READY   STATUS    RESTARTS   AGE
    dnsutils   1/1     Running   4          4h49m
    web-0      0/1     Pending   0          149m
    ```

- Эксперимент: создание Service Account и привязка роли

    ```console
    root@CKA003:~# kubectl create serviceaccount sa-cluster-admin --namespace=kube-system
    serviceaccount/sa-cluster-admin создан
    ```    root@CKA003:~# kubectl get secret --all-namespaces | grep sa-cluster-admin
    kube-system             sa-cluster-admin-token-k9xfp                     kubernetes.io/service-account-token   3      53s
    ```    
    root@CKA003:~# kubectl describe secret -n kube-system sa-cluster-admin-token-k9xfp
    Name:         sa-cluster-admin-token-k9xfp
    Namespace:    kube-system
    Labels:       <none>
    Annotations:  kubernetes.io/service-account.name: sa-cluster-admin
                  kubernetes.io/service-account.uid: 11deb8dd-5625-4d75-ad44-3beef1bcd995
    Type:         kubernetes.io/service-account-token
    Data
    ====
    ca.crt:     1025 байт
    namespace:  11 байт
    token:      eyJhbGciOiJSUzI1NiIsImtpZCI6InRBUkJ6bkxQMHNHSi1MejR4T2ZtYk43b1Y0S2M3MXZOMTMtQmtOaHpsbXMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJzYS1jbHVzdGVyLWFkbWluLXRva2VuLWs5eGZwIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6InNhLWNsdXN0ZXItYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIxMWRlYjhkZC01NjI1LTRkNzUtYWQ0NC0zYmVlZjFiY2Q5OTUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06c2EtY2x1c3Rlci1hZG1pbiJ9.Dr1aOVdwAXO_BPXlJAohKBjoxRhMmTWyfVy2AP3-D0V-2jzdzWKoEP_17wnAS3FP-hxuOtTr3XWN0zM4oAI8-CeXtP7AdB0sqZ9P7Wnp2s88DqDUNK0JUuYGke3js9xd44Bt5vhtRovNEMYEnLXj_NLOunW33f4g46ep4NvQpNGTd48BcgzFhiiWuXLKKGGoOZGrWlkXqyofE4li83B3D08oW-hjP4S0JBBXqmzpa0_PYi-hkPbirmn9J7F-oQd0So05uAzZROHSd7n8INlYwbJx2zRF8PKipscxu47ddEumr6R9b8qDDVolP5iawqFPeDTt9lOY7OdgEaVcL651UQ
    root@CKA003:~# vi sa-cluster-admin-rolebinding.yaml    root@CKA003:~# cat sa-cluster-admin-rolebinding.yaml
    apiVersion: rbac.authorization.k8s.io/v1
    # This cluster role binding allows any user from the "manager" group to read secrets in any namespace.
    kind: ClusterRoleBinding
    metadata:
      name: read-secrets-global
    subjects:
    - kind: ServiceAccount
      name: sa-cluster-admin
      namespace: kube-system
    roleRef:
      kind: ClusterRole
      name: cluster-admin
      apiGroup: rbac.authorization.k8s.io

    root@CKA003:~# kubectl create -f sa-cluster-admin-rolebinding.yaml
    clusterrolebinding.rbac.authorization.k8s.io/read-secrets-global created

    Token can be directly written to the file `.kube/config`:

    ```bash
    kubectl config set-credentials аккаунт --token=eyJhbGciOiJSUzI1NiIsImtpZCI6InRBUkJ6bkxQMHNHSi1MejR4T2ZtYk43b1Y0S2M3MXZOMTMtQmtOaHpsbXMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJzYS1jbHVzdGVyLWFkbWluLXRva2VuLWs5eGZwIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6InNhLWNsdXN0ZXItYWRtaW4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIxMWRlYjhkZC01NjI1LTRkNzUtYWQ0NC0zYmVlZjFiY2Q5OTUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06c2EtY2x1c3Rlci1hZG1pbiJ9.Dr1aOVdwAXO_BPXlJAohKBjoxRhMmTWyfVy2AP3-D0V-2jzdzWKoEP_17wnAS3FP-hxuOtTr3XWN0zM4oAI8-CeXtP7AdB0sqZ9P7Wnp2s88DqDUNK0JUuYGke3js9xd44Bt5vhtRovNEMYEnLXj_NLOunW33f4g46ep4NvQpNGTd48BcgzFhiiWuXLKKGGoOZGrWlkXqyofE4li83B3D08oW-hjP4S0JBBXqmzpa0_PYi-hkPbirmn9J7F-oQd0So05uAzZROHSd7n8INlYwbJx2zRF8PKipscxu47ddEumr6R9b8qDDVolP5iawqFPeDTt9lOY7OdgEaVcL651UQ
    kubectl config set-context аккаунт-контекст --cluster=kubernetes --user=аккаунт
    kubectl config use-context аккаунт-контекст
    ```

## Урок 05: Расписание K8S### 5.1 Как развернуть кластер K8S с несколькими узлами? - Ссылки
    - Официальная документация: [Как использовать kubeadm для создания высокодоступного кластера?](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/)
    - [Как развернуть многоподключенное K8S окружение?](deploy-k8s-manual.md)
    - [Как развернуть K8S окружение с двойным стеком и высокой доступностью?](basic.md)
    - [Развертывание высокодоступного Kubernetes кластера на AWS](deploy-aws-ha-k8s-cluster.md)
    - [руководства по реализации архитектуры openshift-container-platform-reference](https://blog.openshift.com/openshift-container-platform-reference-architecture-implementation-guides/)        ![](../images/openshift-ha-deployment.png)        ![](../images/openshift-network-arch-azure.png)

- Нетворкирование с несколькими плоскостями

        ![](../images/k8s-deploy-ha-multus.png)

- Шаги

    1. Получение токена и ca_hash на узле master

        ```console
        root@ckamaster003:~# kubeadm token create
        b6k3qj.avofghaucefqe0a8

        root@ckamaster003:~# kubeadm token list | grep -v TOKEN | awk '{print $1}' | head -n -1
        b6k3qj.avofghaucefqe0a8

        root@ckamaster003:~# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
        d7d0906ebe29f607587e404ef6c393169a51e5f6c81e22a2a48f30ef8702e12a

        root@ckamaster003:~# ifconfig | grep eth0 -A 1
        eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
                inet 172.31.43.105  netmask 255.255.240.0  broadcast 172.31.47.255
        ```

    2. Использование команды kubeadm для добавления нового узла в кластер k8s, сначала сгенерировать команду на узле master

        ```bash
        kubeadm token create
        master_ip=$(ifconfig | grep eth0 -A 1 | grep inet | awk '{print $2}')
        token=$(kubeadm token list | grep -v TOKEN | awk '{print $1}' | head -n 1)
        ca_hash=$(openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //')

        echo kubeadm join $master_ip:6443 --token $token --discovery-token-ca-cert-hash sha256:$ca_hash
        ```

    3. Затем скопировать сгенерированную команду на узел worker и выполнить её, **перед выполнением на узле worker должны быть установлены Docker и kubeadm**
      - См. раздел 1.6, [Настройка Docker на Ubuntu 18.04](#16-эксперимент-docker-quick-start)
      - См. раздел 2.7, [Установка kubeadm](#27-эксперимент-установки-k8s)
      - Затем скопировать сгенерированную команду на узел worker и выполнить её: `kubeadm join $master_ip:6443 --token $token --discovery-token-ca-cert-hash sha256:$ca_hash`    4. Настройка kubectl на новом узле

        ```bash
        mkdir -p $HOME/.kube
        scp root@$master_ip:/etc/kubernetes/admin.conf $HOME/.kube/config
        chown $(id -u):$(id -g) $HOME/.kube/config
        ```

    5. Затем на новом узле можно увидеть, что узел готов к работе с помощью команды kubectl

        ```console
        root@ckaslave003:~# kubectl get nodes
        ИМЯ           СТАТУС   РОЛИ    ВОЗРАСТ   ВЕРСИЯ
        ckamaster003   Ready    master   20m       v1.18.3
        ckaslave003    Ready    <none>   33s       v1.18.3
        ```

### 5.2 Как развернуть приложение на определённом узле?

- См. также: [Labels and Selectors](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/)

    ```yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: label-demo
      labels:
        environment: production
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
    ```

    ```bash
    # Создайте объект pod с метками label-pod.yaml, содержимое которого указано выше

    kubectl apply -f label-pod.yaml
    ```

    ```console
    root@ckatest:~# kubectl get pods -l environment=production
    ИМЯ         ГОТОВ   СТАТУС    ПЕРЕЗАПУСКИ   ВОЗРАСТ
    label-demo   1/1     Running   0             4m16s
    root@ckatest:~# kubectl get pods -l environment=production,tier=frontend
    Нет ресурсов в пространстве имен default.
    root@ckatest:~# kubectl get pods -l 'environment in (production),tier in (frontend)'
    Нет ресурсов в пространстве имен default.
    root@ckatest:~# kubectl get pods -l 'environment in (production, qa)'
    ИМЯ         ГОТОВ   СТАТУС    ПЕРЕЗАПУСКИ   ВОЗРАСТ
    label-demo   1/1     Running   0             4m42s
    root@ckatest:~# kubectl get pods -l 'environment,environment notin (frontend)'
    ИМЯ         ГОТОВ   СТАТУС    ПЕРЕЗАПУСКИ   ВОЗРАСТ
    label-demo   1/1     Running   0             4m50s
    ```- См. также: [Assigning Pods to Nodes](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/)
- Эксперимент:
  1. Как выбрать узел для запуска pod на основе меток? (см. пример выше)
  1. Как выбрать узел для запуска pod по имени узла?

      ```yaml
      apiVersion: v1
      kind: Pod
      metadata:
        name: nginx
      spec:
        nodeName: foo-node # расписание pod на определённый узел
        containers:
        - name: nginx
          image: nginx
          imagePullPolicy: IfNotPresent
      ```

Node Selector на самом деле является узловой симметрией, которая используется для размещения pod на определённых узлах.

### 5.3 Что такое Taints & Toleration?
- Ссылка: [Taints и Tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)
- Эксперимент

    Примените taint к узлу master

    ```bash
    kubectl taint nodes k8s-masterXXX key=value:NoSchedule
    ```

    ```yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx5
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
      nodeSelector:
        node-role.kubernetes.io/master: ""
      tolerations:
      - key: "key"
        operator: "Equal"
        value: "value"
        effect: "NoSchedule"
    ```

- Если не использовать nodeSelector, а вместо этого использовать nodeName, можно проигнорировать taint NoSchedule, и под будет распределен. Однако после распределения он будет вытеснен taint NoExecute.

    ```yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
    spec:
      nodeName: ckamaster003
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
    ```

Taint фактически представляет собой противоположность узловому сродству, запрещающую размещение pod'ов на узлах с taint.Комбинация Toleration и Taint позволяет определенным узлам размещать только определенные pod'ы, обеспечивая специализацию узлов.

### 5.4 Что такое Node Affinity?

- Ссылка: [Назначение подов на узлы с помощью Node Affinity](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/)

Рассуждение:

1. Каковы отношения между Node Affinity и Node Selector?

### 5.5 Что такое Pod Affinity?

- [Межузловое сродство и противоположное сродство](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity)

### 5.6 Эксперимент: Распределение подов

## Урок 06: Хранилище K8S

### 6.1 Что такое ConfigMap и Secret?

- [ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/)
    - Ссылка: [Настройка пода для использования ConfigMap](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/)
    - Ссылка: [Настройка Redis с помощью ConfigMap](https://kubernetes.io/docs/tutorials/configuration/configure-redis-using-configmap/)

    ```yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: example
    data:
      example.property.1: hello
      example.property.2: world
      example.property.file: |-
        property.1=value-1
        property.2=value-2
        property.3=value-3
    ```

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: nginx
      image: nginx
      env:
        - name: TEST1
          valueFrom:
            configMapKeyRef:
              name: example
              key: example.property.2
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
    - name: config
      configMap:
        name: example
root@ckamaster003:~# kubectl exec -it configmap-demo-pod /bin/sh

# env | grep TEST
TEST1=world

# ls /config
example.property.1  example.property.2  example.property.file
```- [Секрет](https://kubernetes.io/docs/concepts/configuration/secret/)

    Секреты и ConfigMap используются для конфигурации, но Секрет шифрует или кодирует содержимое.

    ```bash
    # Создание двух секретов
    kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
    kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests

    # Создание двух pod, которые используют указанные секреты в качестве конфигурационных файлов
    cat <<EOF > pod.yaml
    apiVersion: v1
    kind: List
    items:
    - kind: Pod
      apiVersion: v1
      metadata:
        name: prod-db-client-pod
        labels:
          name: prod-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: prod-db-secret
        containers:
        - name: db-client-container
          image: nginx
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"
    - kind: Pod
      apiVersion: v1
      metadata:
        name: test-db-client-pod
        labels:
          name: test-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: test-db-secret
        containers:
        - name: db-client-container
          image: nginx
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"
    EOF
    ```    cat <<EOF >> kustomization.yaml
    ресурсы:
    - pod.yaml
    EOF

    kubectl apply -k .
    ```

    Затем войдите в контейнер, чтобы увидеть, как секрет был отображен в виде файлов

    ```console
    [root@k8slab001 ~]# kubectl exec -it prod-db-client-pod -- sh

    # cd /etc/secret-volume
    # ls
    password  username
    # cat password
    Y4nys7f11

    # cat username
    produser

    # exit
    ```

  См. также: <https://kubernetes.io/ru/docs/concepts/configuration/secret/#use-case-as-container-environment-variables> для отображения секрета в виде переменных окружения в pod### 6.2 Что такое PV / PVC?

- [Типы томов](https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes)
- [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)
- [Настройка pod для использования тома для хранения](https://kubernetes.io/docs/tasks/configure-pod-container/configure-volume-storage/)
- [Настройка pod для использования PersistentVolume для хранения](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/) обратите внимание на то, что hostPath соответствует пути на узле, поэтому убедитесь, что вы знаете, на каком узле запускается pod. Лучше всего заранее использовать taint для slave узла или использовать node selector для указания запуска pod на master узле, чтобы избежать проблем.

### 6.3 Что такое Storage Class?

- [Storage Classes](https://kubernetes.io/docs/concepts/storage/storage-classes/)
- [Динамическое выделение томов](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/)
    - [NFS](../src/config-lab/README.md#3-интеграция-nfs-хранилища)
    - [Динамическое локальное экспериментирование](https://gitee.com/wu-wen-xiang/lab-kubernetes/blob/master/doc/kubernetes-best-practices.md#45-local-%E5%92%8C%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D)
    - [NFS экспериментирование](https://gitee.com/wu-wen-xiang/lab-kubernetes/blob/master/doc/kubernetes-best-practices.md#42-%E5%AF%B9%E6%8E%A5-nfs-%E5%92%8C-nas)

### 6.4 Эксперимент: ConfigMap / Secret / PV & PVC / StorageClass

## Урок 07: Service

### 7.1 Как реализован Service на уровне реализации?

- [Service](https://kubernetes.io/docs/concepts/services-networking/service/)
- [Подключение приложений с помощью Service](https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/)
- [Руководство по модели сетей Kubernetes](https://sookocheff.com/post/kubernetes/understanding-kubernetes-networking-model/)

### 7.2 Эксперимент: публикация сервиса- Адресное пространство кластера (Cluster IP)
- Полное имя домена сервиса (Service FQDN)
- Узел с портом (Nodeport)
- Тип балансера (LB Type Service)

```bash
# Отметить slave узел, так как в некоторых средах не разрешены туннели, мы вынуждены запускать все pod на master узле
kubectl taint node ckaslave001 key=value:NoExecute

# Создать Deployment
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/service/networking/run-my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80
# Создать Service
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/service/networking/nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
root@ckamaster001:~# kubectl get svc --all-namespaces -o wide
NAMESPACE     NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                  AGE   SELECTOR
default       kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP                  8h    <none>
default       my-nginx     ClusterIP   10.98.172.84   <none>        80/TCP                   36m   run=my-nginx
kube-system   kube-dns     ClusterIP   10.96.0.10     <none>        53/UDP,53/TCP,9153/TCP   8h    k8s-app=kube-dns

root@ckamaster001:~# dig @10.96.0.10 my-nginx.default.svc.cluster.local

; <<>> DiG 9.11.3-1ubuntu1.12-Ubuntu <<>> @10.96.0.10 my-nginx.default.svc.cluster.local
; (1 server found)
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23475
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 2661e0af4b0ce87e (echoed)
;; QUESTION SECTION:
;my-nginx.default.svc.cluster.local. IN	A
;; ОТВЕТНАЯ СЕКЦИЯ:
my-nginx.default.svc.cluster.local. 30 IN A	10.98.172.84
```;; ВРЕМЯ ЗАПРОСА: 0 msec
;; СЕРВЕР: 10.96.0.10#53(10.96.0.10)
;; ВРЕМЯ: Сб Июн 13 16:15:23 CST 2020
;; РАЗМЕР СООБЩЕНИЯ: 125

### 7.3 Что такое Ingress?

- [Ingress Controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/)
  - [Установка Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal)

    ```bash
    # Скачать файл конфигурации ingress controller
    # wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml
    wget https://gitee.com/dev-99cloud/training-kubernetes/raw/master/src/amd-lab/deploy.yaml
    ```

    В новых версиях по умолчанию не слушаются порты 80 и 443, их нужно настроить самостоятельно

    ```bash
    # Проверить, заняты ли порты
    # lsof -i :xxx
    netstat -putln | grep 80
    netstat -putln | grep 443
    # Добавить следующие строки в конфигурацию, чтобы слушать локальные порты
    hostNetwork: true

    # spec:
    #  hostNetwork: true
    #  containers:
    #  - name: nginx
    #    image: nginx:1.7.9

    # Обычно порт 443 занят Calico, порт 80 не занят, можно закомментировать строки в конфигурации, относящиеся к порту 443, и контроллер перестанет слушать этот порт

    # Установить ingress controller
    kubectl apply -f deploy.yaml
    # Проверить
    kubectl get pods -n ingress-nginx
    kubectl get svc -n ingress-nginx
    # Увидим, что состояние EXTERNAL-IP для ingress-nginx-controller — pending, нужно добавить externalIPs вручную

    # Добавить externalIPs в конфигурацию сервиса ingress-nginx-controller, 10.0.0.118 — IP узла:
    #   externalIPs:
    #   - 10.0.0.118

    # spec:
    #  externalIPs:
    #  - 192.168.132.253
    #  ports:
    #   - name: highport
    #     nodePort: 31903

    kubectl edit svc ingress-nginx-controller -n ingress-nginx
    ```
- [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)  - Развернуть сервисы-backend
    ```console
    [root@iZuf6g226c4titrnrwds2tZ ~]# vim deploy-demo.yaml
    ```
    ```yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: myapp
      namespace: default
    spec:
      selector:
        app: myapp
        release: canary
      ports:
      - name: http
        targetPort: 80
        port: 80
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myapp-backend-pod
      namespace: default
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: myapp
          release: canary
      template:
        metadata:
          labels:
            app: myapp
            release: canary
        spec:
          containers:
          - name: myapp
            image: ikubernetes/myapp:v2
            ports:
            - name: http
              containerPort: 80
    ```
    ```console
    [root@iZuf6g226c4titrnrwds2tZ ~]# kubectl apply -f deploy-demo.yaml
    [root@iZuf6g226c4titrnrwds2tZ ~]# kubectl get pods
    NAME                                 READY   STATUS    RESTARTS   AGE
    myapp-backend-pod-58b7f5cf77-krmzh   1/1     Running   2          17h
    myapp-backend-pod-58b7f5cf77-vqlgl   1/1     Running   2          17h
    myapp-backend-pod-58b7f5cf77-z7j7z   1/1     Running   2          17h
    ```

  - Предоставление сервиса через ingress-controller и создание для него сервиса

    ```bash
    # Загрузка yaml-файла
    wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/baremetal/service-nodeport.yaml
    vim service-nodeport.yaml
    ```

    ```yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: ingress-nginx
      namespace: ingress-nginx
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    spec:
      type: NodePort
      ports:
        - name: http
          port: 80
          targetPort: 80
          protocol: TCP
          nodePort: 30080
        - name: https
          port: 443
          targetPort: 443
          protocol: TCP
          nodePort: 30443
      selector:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
    ```    ---
    ```console
    [root@iZuf6g226c4titrnrwds2tZ ~]# kubectl apply -f service-nodeport.yaml
    [root@iZuf6g226c4titrnrwds2tZ ~]# kubectl get svc -n ingress-nginx
    NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
    ingress-nginx   NodePort   10.105.153.191   <none>        80:30080/TCP,443:30443/TCP   9h
    ```

  - Развертывание Ingress
    ```console
    [root@iZuf6g226c4titrnrwds2tZ ~]# vim ingress-myapp.yaml
    ```
    ```yaml
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: ingress-myapp
      namespace: default
      annotations:
        kubernetes.io/ingress.class: "nginx"
    spec:
      rules:
      - host: myapp.test.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp
                port:
                  number: 80
    ```
    ```console
    [root@iZuf6g226c4titrnrwds2tZ ~]# kubectl apply -f ingress-myapp.yaml
    [root@iZuf6g226c4titrnrwds2tZ ~]# kubectl get ingress
    NAME            CLASS    HOSTS            ADDRESS          PORTS   AGE
    ingress-myapp   <none>   myapp.test.com   10.105.153.191   80      17h
    ```
  - Результат тестирования

    ```bash
    # Изменение локального файла hosts
    sudo vi /etc/hosts
    # Добавление
    xxx.xxx.xxx.xxx myapp.test.com
    # Доступ через терминал
    curl http://myapp.test.com
    # Результат
    Привет MyApp | Версия: v2 | <a href="hostname.html">Имя хоста</a>
    # При внешнем доступе может возникнуть проблема с отсутствием регистрации сайта, несколько попыток помогут
    # Внутри сети также требуется настройка hosts для доступа по доменному имени
    ```

  - Проблемы с подключением к k8s.gcr.io приводят к неудаче при загрузке образа
    - Скачайте файл yaml на локальную машину
    - Найдите соответствующий образ на Docker Hub
    - Измените соответствующий образ в файле на найденный на Docker Hub, например:      ```console
      #image: k8s.gcr.io/ingress-nginx/controller:v0.47.0@sha256:52f0058bed0a17ab0fb35628ba97e8d52b5d32299fbc03cc0f6c7b9ff036b61a
      image: willdockerhub/ingress-nginx-controller:v0.47.0
      ```

    - Примените изменения с помощью команды `kubectl apply -f xxx`

## Урок 08: Расширенные возможности

### 8.1 Мониторинг, логирование, отладка

- Мониторинг: Grafana / Prometheus / AlertManager
- Логирование: ElasticSearch / Fluent (Logstash) / Kibana
- Отладка:
    - Как сделать дамп сетевого трафика для pod?

        ```bash
        # Просмотр node, на котором запущен указанный pod
        kubectl describe pod <pod> -n <namespace>

        # Получение PID контейнера
        docker inspect -f {{.State.Pid}} <container>

        # Вход в сетевой namespace контейнера
        nsenter --target <PID> -n

        # Использование tcpdump для дампа пакетов, указание eth0
        tcpdump -i eth0 tcp and port 80 -vvv

        # Или дамп пакетов и запись в файл
        tcpdump -i eth0 -w ./out.cap

        # Выход из nsenter, не забудьте выйти!
        exit
        ```

    - Инструменты отладки

        ```yaml
        apiVersion: v1
        kind: Pod
        metadata:
          name: demo-pod
          labels:
            app: demo-pod
        spec:
          containers:
            - name: nginx
              image: nginx
              ports:
                - containerPort: 80
              env:
                - name: DEMO_GREETING
                  value: "Привет из окружения"
                - name: DEMO_FAREWELL
                  value: "Такая сладкая скорбь"
            - name: busybox
              image: busybox
              args:
                - sleep
                - "1000000"
        ```

        ```console
        root@ckatest001:~# kubectl exec -it demo-pod -c busybox -- /bin/sh
        / # ping 192.168.209.193
        PING 192.168.209.193 (192.168.209.193): 56 data bytes
        64 bytes from 192.168.209.193: seq=0 ttl=63 time=0.099 ms
        64 bytes from 192.168.209.193: seq=1 ttl=63 time=0.093 ms
        64 bytes from 192.168.209.193: seq=2 ttl=63 time=0.089 ms
        ```        ```console
        root@ckalab001:~# tcpdump -i eth0 udp
        12:34:10.972395 IP 45.77.183.254.vultr.com.42125 > 45.32.33.135.vultr.com.4789: VXLAN, flags [I] (0x08), vni 4096
        IP 192.168.208.4 > 192.168.209.193: ICMP echo request, id 3072, seq 0, length 64
        12:34:10.972753 IP 45.32.33.135.vultr.com.41062 > 45.77.183.254.vultr.com.4789: VXLAN, flags [I] (0x08), vni 4096
        IP 192.168.209.193 > 192.168.208.4: ICMP echo reply, id 3072, seq 0, length 64
        12:34:11.972537 IP 45.77.183.254.vultr.com.42125 > 45.32.33.135.vultr.com.4789: VXLAN, flags [I] (0x08), vni 4096
        IP 192.168.208.4 > 192.168.209.193: ICMP echo request, id 3072, seq 1, length 64
        ```

        На Алибаба-клауде Calico/Flannel VXLAN модифицированы в маршрутизационные протоколы, поэтому требуется добавление маршрутов в VPC для каждого узла. Каждый узел получает маршрут для своего IP-адреса и сегмента подсети pod. [Эта статья KB](https://yq.aliyun.com/articles/704175) очень нечетко формулирует, что VXLAN не поддерживается, а вместо этого используется модифицированный VXLAN, реализованный с использованием маршрутизации.        Обычный VXLAN:
        ```console
        root@ckalab001:~# ip r
        default via 45.77.182.1 dev ens3 proto dhcp src 45.77.183.254 metric 100
        192.168.208.1 dev cali65a032ad3e5 scope link
        192.168.209.192/26 via 192.168.209.192 dev vxlan.calico onlink
        ```        
        ```console
        root@ckalab001:~# ip a
        2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
            link/ether 56:00:02:d8:35:5a brd ff:ff:ff:ff:ff:ff
            inet 45.77.183.254/23 brd 45.77.183.255 scope global dynamic ens3
              valid_lft 73639sec preferred_lft 73639sec
            inet6 fe80::5400:2ff:fed8:355a/64 scope link
              valid_lft forever preferred_lft forever
        4: cali65a032ad3e5@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UP group default
            link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
            inet6 fe80::ecee:eeff:feee:eeee/64 scope link
              valid_lft forever preferred_lft forever
        9: vxlan.calico: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UNKNOWN group default
            link/ether 66:f1:80:3e:ea:c6 brd ff:ff:ff:ff:ff:ff
            inet 192.168.208.0/32 brd 192.168.208.0 scope global vxlan.calico
              valid_lft forever preferred_lft forever
            inet6 fe80::64f1:80ff:fe3e:eac6/64 scope link
              valid_lft forever preferred_lft forever
        ```        
        Алибаба облако модифицированное VXLAN: `192.168.209.192/26 через 172.31.43.146 dev eth0 proto 80 onlink`, где 80 — это протокол маршрутизации IGRP. Поэтому при отправке пакетов ping между контейнерами на разных узлах с помощью tcpdump пакеты TCP не отлавливаются, а пакеты ICMP отлавливаются.```console
root@ckalab001:~# ip r
default через 172.31.47.253 dev eth0 proto dhcp src 172.31.43.145 metric 100
192.168.208.1 dev cali77bffbebec8 scope link
192.168.209.192/26 через 172.31.43.146 dev eth0 proto 80 onlink

root@ckalab001:~# ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:16:3e:08:2e:5f brd ff:ff:ff:ff:ff:ff
    inet 172.31.43.145/20 brd 172.31.47.255 scope global dynamic eth0
      valid_lft 315356000sec preferred_lft 315356000sec
    inet6 fe80::216:3eff:fe08:2e5f/64 scope link
      valid_lft forever preferred_lft forever
4: cali77bffbebec8@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UP group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::ecee:eeff:feee:eeee/64 scope link
      valid_lft forever preferred_lft forever
8: vxlan.calico: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UNKNOWN group default
    link/ether 66:f1:80:3e:ea:c6 brd ff:ff:ff:ff:ff:ff
    inet 192.168.208.0/32 brd 192.168.208.0 scope global vxlan.calico
      valid_lft forever preferred_lft forever
    inet6 fe80::64f1:80ff:fe3e:eac6/64 scope link
      valid_lft forever preferred_lft forever

8.2 Что такое HPA / CA / VA?

HPA (Horizontal Pod Autoscaler) — это компонент Kubernetes, который автоматически масштабирует количество подов в зависимости от нагрузки. CA (Cluster Autoscaler) — это компонент Kubernetes, который автоматически масштабирует количество узлов в кластере в зависимости от потребностей приложений. VA (Vertical Pod Autoscaler) — это компонент Kubernetes, который автоматически масштабирует ресурсы подов в зависимости от потребностей приложений.- Как понять HPA / CA / VPA?

  • Настройка metrics server
mkdir metrics
cd metrics
# Скачать все yaml-файлы из соответствующего репозитория на GitHub
# for file in auth-delegator.yaml auth-reader.yaml metrics-apiservice.yaml metrics-server-deployment.yaml metrics-server-service.yaml resource-reader.yaml ; do wget https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/metrics-server/$file;done
# В новых версиях могут возникнуть проблемы с прохождением проверки состояния, поэтому рекомендуется использовать ту же версию metrics server, что и ваш kubernetes
# Скачать версию v1.20.1 с внутреннего репозитория
for file in auth-delegator.yaml auth-reader.yaml metrics-apiservice.yaml metrics-server-deployment.yaml metrics-server-service.yaml resource-reader.yaml ; do wget https://gitee.com/dev-99cloud/training-kubernetes/raw/master/src/amd-lab/metrics-server/$file;done
kubectl apply -f .

Измените metrics-server-deployment.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: metrics-server
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: metrics-server-config
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: EnsureExists
data:
  NannyConfiguration: |-
    apiVersion: nannyconfig/v1alpha1
    kind: NannyConfiguration
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: metrics-server-v0.3.6
  namespace: kube-system
  labels:
    k8s-app: metrics-server
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    version: v0.3.6
spec:
  selector:
    matchLabels:
      k8s-app: metrics-server
      version: v0.3.6
  template:
    metadata:
      name: metrics-server
      labels:
        k8s-app: metrics-server
        version: v0.3.6
    spec:
      securityContext:
        seccompProfile:
          type: RuntimeDefault
```         priorityClassName: system-cluster-critical
          serviceAccountName: metrics-server
          nodeSelector:
            kubernetes.io/os: linux
          containers:
          - name: metrics-server
            # image: k8s.gcr.io/metrics-server-amd64:v0.3.6
            image: opsdockerimage/metrics-server-amd64:v0.3.6
            command:
            - /metrics-server
            - --metric-resolution=30s
            # Эти строки нужно удалить для кластеров, отличных от GKE, и когда GKE поддерживает аутентификацию на основе токенов.
            # - --kubelet-port=10255
            # - --deprecated-kubelet-completely-insecure=true
            - --kubelet-insecure-tls
            - --kubelet-preferred-address-types=InternalIP
            # - --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
            ports:
            - containerPort: 443
              name: https
              protocol: TCP
          - name: metrics-server-nanny
            # image: k8s.gcr.io/addon-resizer:1.8.11
            image: opsdockerimage/addon-resizer:1.8.11
            resources:
              limits:
                cpu: 100m
                memory: 300Mi
              requests:
                cpu: 5m
                memory: 50Mi
            environment:
              - name: MY_POD_NAME
                valueFrom:
                  fieldRef:
                    fieldPath: metadata.name
              - name: MY_POD_NAMESPACE
                valueFrom:
                  fieldRef:
                    fieldPath: metadata.namespace
            volumeMounts:
            - name: metrics-server-config-volume
              mountPath: /etc/config
            command:
              - /pod_nanny
              - --config-dir=/etc/config
              # - --cpu={{ base_metrics_server_cpu }}
              - --extra-cpu=0.5m
              # - --memory={{ base_metrics_server_memory }}
              # - --extra-memory={{ metrics_server_memory_per_node }}Mi
              - --threshold=5
              - --deployment=metrics-server-v0.3.6
              - --container=metrics-server
             - --период-мониторинга=300000
              - --оценка=экспоненциальная
              # Определяет наименьший кластер (определенный количеством узлов)
              # ресурсы будут масштабироваться.
              # - --minClusterSize={{ metrics_server_min_cluster_size }}
              - --minClusterSize=2
              # Использует метрики kube-apiserver для избежания периодического перечисления узлов.
              - --использовать-метрики=true
          тома:
            - имя: metrics-server-config-volume
              конфиг_карта:
                имя: metrics-server-config
          терпимости:
            - ключ: "CriticalAddonsOnly"
              оператор: "Exists"
    ```  Измените resource-reader.yaml  ```yaml
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRole
  metadata:
    name: system:metrics-server
    labels:
      kubernetes.io/cluster-service: "true"
      addonmanager.kubernetes.io/mode: Reconcile
  rules:
  - apiGroups:
    - ""
    resources:
    - pods
    - nodes
    # Add nodes/stats
    - nodes/stats
    - namespaces
    verbs:
    - get
    - list
    - watch
  - apiGroups:
    - "apps"
    resources:
    - deployments
    verbs:
    - get
    - list
    - update
    - watch
  - nonResourceURLs:
    - /metrics
    verbs:
    - get
  ---
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRoleBinding
  metadata:
    name: system:metrics-server
    labels:
      kubernetes.io/cluster-service: "true"
      addonmanager.kubernetes.io/mode: Reconcile
  roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: system:metrics-server
  subjects:
  - kind: ServiceAccount
    name: metrics-server
    namespace: kube-system
  • Проверьте доступность метрик

    # Проверьте, работают ли pod-ы
    kubectl get pods -n kube-system
    # Проверьте api-versions, обычно должны появиться метрики metrics.k8s.io/v1beta1
    kubectl api-versions
    # Проверьте мониторинг узлов
    kubectl top nodes
    NAME                      CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
    izuf6g226c4titrnrwds2tz   129m         6%     1500Mi          42%
    # Проверьте мониторинг pod-ов
    kubectl top pods
    NAME                                 CPU(cores)   MEMORY(bytes)
    myapp-backend-pod-58b7f5cf77-krmzh   0m           1Mi
    myapp-backend-pod-58b7f5cf77-vqlgl   0m           1Mi
    myapp-backend-pod-58b7f5cf77-z7j7z   0m           1Mi
    • Если метрики недоступны, ошибка может быть "unable to fully collect metrics: unable to fully scrape metrics from source kubelet_summary". Можно попробовать изменить сертификаты

      mkdir certs; cd certs
      cp /etc/kubernetes/pki/ca.crt ca.pem
      cp /etc/kubernetes/pki/ca.key ca-key.pem
      ```    Создайте файл kubelet-csr.json
      ```json
      {
        "CN": "kubernetes",
        "hosts": [
          "127.0.0.1",
          "<node_name>",
          "kubernetes",
          "kubernetes.default",
          "kubernetes.default.svc",
          "kubernetes.default.svc.cluster",
          "kubernetes.default.svc.cluster.local"
        ],
        "key": {
          "algo": "rsa",
          "size": 2048
        },
        "names": [{
          "C": "RU",
          "ST": "Moscow",
          "L": "City",
          "O": "Org",
          "OU": "Unit"
        }]
      }

      Создайте файл ca-config.json```json { "signing": { "default": { "expiry": "8760h" }, "profiles": { "kubernetes": { "usages": [ "signing", "key encipherment", "server auth", "client auth" ], "expiry": "8760h" } } } }


Обновить сертификаты

```bash
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem --config=ca-config.json -profile=kubernetes kubelet-csr.json | cfssljson -bare kubelet
scp kubelet.pem <nodeip>:/var/lib/kubelet/pki/kubelet.crt
scp kubelet-key.pem <nodeip>:/var/lib/kubelet/pki/kubelet.key
systemctl restart kubelet
  • Настроить Docker-образ

    FROM php:5-apache
    COPY index.php /var/www/html/index.php
    RUN chmod a+rx index.php

    Содержимое файла index.php:

    <?php
      $x = 0.0001;
      for ($i = 0; $i <= 1000000; $i++) {
          $x += sqrt($x);
      }
    ?>
  • Настроить Deployment для запуска образа и экспонирования сервиса

    php-apache.yaml:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: php-apache
    spec:
      selector:
        matchLabels:
          run: php-apache
      replicas: 1
      template:
        metadata:
          labels:
            run: php-apache
        spec:
          containers:
          - name: php-apache
            #image: k8s.gcr.io/hpa-example
            image: 0layfolk0/hpa-example
            ports:
            - containerPort: 80
            resources:
              limits:
                cpu: 50m
              requests:
                cpu: 20m
    
    ---
    
    apiVersion: v1
    kind: Service
    metadata:
      name: php-apache
      labels:
        run: php-apache
    spec:
      ports:
      - port: 80
      selector:
        run: php-apache
  • Создать HPA

# В процессе эксперимента изменения копий, нагрузки на процессор и т.д. требуют некоторого времени и не изменяются мгновенно, обычно это занимает несколько минут
# Создайте HPA для управления Deployment из предыдущего шага, чтобы количество копий поддерживалось в диапазоне от 1 до 10, с средним использованием процессора 50%
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled
# Проверьте состояние Autoscaler
kubectl get hpa
# Запустите контейнер в новом терминале, увеличивая нагрузку
kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
# Вернитесь в старый терминал
# Проверьте нагрузку на процессор, она должна увеличиться
kubectl get hpa
# Проверьте количество копий Deployment, оно должно увеличиться
kubectl get deployment php-apache
# Остановите выполнение контейнера в новом терминале (Ctrl+C), проверьте состояние нагрузки в старом терминале, использование процессора должно упасть до 0, количество копий должно стать равно 1
kubectl get hpa
kubectl get deployment php-apache

8.3 Что такое Federation?

# During the experiment, changes in the number of copies, processor load, and other factors take some time and do not change instantly; usually, this takes several minutes.
# Create an HPA to manage the Deployment from the previous step, ensuring that the number of copies is maintained within the range of 1 to 10, with an average CPU utilization of 50%.
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled
# Check the Autoscaler status
kubectl get hpa
# Run a container in a new terminal, increasing the load
kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
# Return to the old terminal
# Check the processor load, it should increase
kubectl get hpa
# Check the number of Deployment copies, it should increase
kubectl get deployment php-apache
# Stop the container execution in the new terminal (Ctrl+C), check the load status in the old terminal, the processor usage should drop to 0, and the number of copies should become 1
kubectl get hpa
kubectl get deployment php-apache
```- Kubenetes Federation vs ManageIQ

### 8.4 Как K8S обрабатывает сервисы с состоянием?

- [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
- [Примеры StatefulSet](https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/)
- CRD & Operator, например [mariadb-operator](https://github.com/mmontes11/mariadb-operator)

### 8.5 Другие ссылки

- [Тестирование K8S](https://jimmysong.io/kubernetes-handbook/develop/testing.html)
- Установка OpenStack тестового окружения
  - [vultr](https://www.vultr.com/) Ubuntu 18.04 / 20.04 Bare Metal сервер
  - [Установка DevStack](https://docs.openstack.org/devstack/latest/)
  - [Загрузка облачных образов](https://docs.openstack.org/image-guide/obtain-images.html)

      ```bash
      wget http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-2009.qcow2
      wget https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img
      wget https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img
      ```

  - [Загрузка образов в OpenStack](https://docs.openstack.org/murano/pike/reference/appendix/articles/image_builders/upload.html)

      ```bash
      openstack image create --public --disk-format qcow2 --file focal-server-cloudimg-amd64.img ubuntu-20.04
      openstack image create --public --disk-format qcow2 --file bionic-server-cloudimg-amd64.img ubuntu-18.04
      openstack image create --public --disk-format qcow2 --file CentOS-7-x86_64-GenericCloud-2009.qcow2 centos-7.8
      ```

  - [Включение ipip](https://www.infoq.cn/article/mqluyhvsdw8xopdwp17v)
- [Установка k3s](https://github.com/k3s-io/k3s#quick-start---install-script)
- [Пример KubeEdge](https://kubeedge.io/zh/blog/kubeedge-deployment-manual/)

## Урок 09: CKA

### 9.1 Советы по сдаче экзамена

### 9.2 Обзор тестовых вопросов

### 9.3 Практика: выполнение тестовых вопросов

## Урок 10: Часто задаваемые вопросы

1. Что такое настройка egress IP? [Решение Red Hat](https://docs.openshift.com/container-platform/4.1/networking/openshift_sdn/assigning-egress-ips.html), [Открытое решение](https://github.com/nirmata/kube-static-egress-ip)

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/dev-99cloud-training-kubernetes.git
git@api.gitlife.ru:oschina-mirror/dev-99cloud-training-kubernetes.git
oschina-mirror
dev-99cloud-training-kubernetes
dev-99cloud-training-kubernetes
master