Всем привет. Меня зовут Путилин Дмитрий (Добрый Кот) Telegram.

От коллектива FR-Solutions : Продолжаем серию статей о K8S.

Наша команда получила запрос на оценку managed K8S от MTS Cloud и составление объективного обзора проделанной работы инженеров МТС. Хочу отметить, что данная статья не является заказной, поэтому мы сосредоточимся на объективном анализе плюсов и минусов этого решения.

Регистрация

Перед началом работы с облаком требуется пройти процесс регистрации. Замечательной особенностью является возможность входа по номеру телефона. Если у вас есть номер от МТС, вся ваша информация будет автоматически подтянута.

Первоначальная авторизация

При первом входе на страницу вы увидите следующую панель с доступными услугами.

Хотим отметить, что явно присутствует недоработка на данной странице. Проблема заключается в том, что при первом входе вероятно выполняется операция создания рабочей области, и до тех пор, пока она не будет создана, невозможно создать какой-либо ресурс. Поэтому при выборе любой услуги вы увидите пустое окно, как показано на скриншоте выше. Было бы логично отображать информацию о создании области и сообщать, что в ближайшее время вы сможете приступить к работе с ней.

Создание кластера K8S

Чтобы создать кластер K8S, вам необходимо выбрать услугу "Containerum Kubernetes" и нажать кнопку "Добавить".

Однако параметры модификации кластера оказываются довольно ограниченными:

  • имя кластера

  • зоны доступности (Москва/Владивосток)

  • сеть для нод

  • сеть для подов

  • версия кластера

  • single master / HA (вид отказоустойчивости)

  • описание группы нод (воркеры)

  • плагины

После выбора необходимой конфигурации вы нажимаете кнопку "Создать" и ожидаете создания кластера.

Сеть

Перед тем, как приступить к выполнению любых задач, вам потребуется настроить сеть. В МТС Облаке для этого доступны ресурсы: Виртуальная частная сеть (VPC) и подсети.

Выражаем свое беспокойство относительно полосы пропускания в 100 Мбит на выходе (не проводили тестирование скорости между узлами).

Созданную подсеть можем указать базовой для наших будущих узлов кластера. Адресация будет выдана по DHCP.

Если речь идет о нодах, то ситуация в целом понятна. Однако, когда дело касается подов, возникают некоторые проблемы. Все решение рассчитано на использование оверлейной сети для контейнеров, что может привести к задержкам (летенси) и уменьшает гибкость доступа от контейнеров до внешних узлов.

Использование SNAT на адрес ноды может затруднить проведение аудита сетевой безопасности, так как необходимо иметь возможность четко отслеживать, какие конкретно запросы отправляются и откуда, так же может затруднить идентификацию точного отправителя в определенный момент времени. Это может создавать проблемы при анализе и мониторинге сетевых активностей для целей безопасности.

Masters

Поскольку мы не имеем доступа к конфигурации кластера из-за ограничений в инфраструктуре, мы можем полагаться на дедукцию и внешние факторы для нашей оценки.

  1. Мастер-ноды Kubernetes разворачиваются в виртуальной частной сети (VPC) пользователя (*проверяется через
    kubectl get endpoints -n default kubernetes
    там будет отображен список IP от мастер нод)

  2. При создании кластера пользователь указывает базовую подсеть нод, в которой будут развернуты мастер-ноды.

  3. Балансировщик нагрузки кластера ведет на мастера и может иметь либо внутреннюю адресацию, либо внешнюю.

Догадки:

  1. OC Flatcar

  2. Для control-plane и data-plane нод не создаются предварительно настроенные образы (Golden Images) - вся настройка производится через cloud-init.

  3. Для выдачи сертификатов вероятно используется инструмент, подобный Vault или аналогичная центральная служба аутентификации (ЦА).

  4. На борту системы имеется три пользователя, которые могут получить доступ к системе через SSH:

    1. core (не используется, пароля нет, ssh ключа нет)

    2. root (не используется, пароля нет, ssh ключа нет)

    3. admin - с доступом по ssh через ключи типа ssh-ed25519

      1. через него подключается containerum@config-manager

  5. Вся конфигурация передается с использованием файлов Ignition config:

    1. сертификаты (private/public ключи)

    2. конфигурационные файлы

    3. для скачивания необходимых бинарных файлов в систему используются триггер-сервисы, которые управляются через зависимости systemd services.

Догадки строятся основываясь на устройстве нод от data-plane.

Workers

При создании группы узлов в кластере, добавляются новые узлы с определенными метаданными:

  • OS IMAGE: Flatcar Container Linux by Kinvolk 3374.2.0 (Oklo)

  • KERNEL-VERSION: 5.15.74-flatcar

  • CONTAINER-RUNTIME: containerd://1.6.8

В официальной документации отмечается, что доступ к узлам для пользователей ограничен. Тем не менее, возникает желание ознакомиться с процессом создания воркер-узлов и изучить, какие компоненты добавляются и как они настраиваются.

Для получения доступа к узлу можно создать контейнер в привилегированном режиме и использовать механизм hostPath с монтированием директории /host. Затем можно выполнить команду chroot /host внутри контейнера, чтобы перейти в корневую файловую систему узла и получить доступ к его содержимому.

---
apiVersion: v1
kind: Pod
metadata:
  name: chroot-pod
spec:
  hostNetwork: true
  containers:
  - name: chroot-container
    image: nginx:stable
    securityContext:
      privileged: true
    volumeMounts:
    - name: host-root
      mountPath: /host
  volumes:
  - name: host-root
    hostPath:
      path: /

Этим способом мы сможем получить доступ к содержимому узла и взаимодействовать с ним.

Давайте рассмотрим процесс бутстрапа нашего хоста. Для этого выполним следующую команду:

journalctl --identifier=ignition --all
список созданных файлов
 writing file "/etc/environment"
 writing file "/etc/hostname"
 writing file "/etc/coredns.conf"
 writing file "/etc/hosts"
 writing link "/etc/resolv.conf" -> "/run/systemd/resolve/stub-resolv.conf"
 writing file "/etc/flatcar/update.conf"
 writing file "/etc/kubernetes/kube-proxy.yaml"
 writing file "/etc/kubernetes/node.kubeconfig"
 writing file "/etc/kubernetes/kube-proxy.kubeconfig"
 writing file "/etc/kubernetes/kubelet.yaml"
 writing file "/opt/kubelet/config.json"
 writing file "/etc/sysctl.d/k8s.conf"
 writing file "/etc/sysctl.d/80-swappiness.conf"
 writing file "/etc/modules-load.d/k8s.conf"
 writing file "/etc/logrotate.d/overrides.conf"
 writing file "/etc/systemd/resolved.conf"
 writing file "/etc/containerd/config.toml"
 writing file "/etc/kubernetes/pki/node.key"
 writing file "/etc/kubernetes/pki/ca.crt"
 writing file "/etc/systemd/system/vmtoolsd.service"

Далее приведен результат, содержащий список записанных файлов.

environment

/# cat /etc/environment

export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/opt/bin/:/opt/cni/bin
# For crictl
export CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock

Возникает вопрос, почему было необходимо передавать переменную окружения
CONTAINER_RUNTIME_ENDPOINT, если имеется опция --config, указывающая на
файл /etc/crictl.yaml.

MDNS

В системе используется плагин COREDNS - MDNS, который обеспечивает поддержку мультисервисного обнаружения (mDNS). Этот плагин позволяет устройствам и сервисам в сети обнаруживать и взаимодействовать друг с другом без необходимости во внешних DNS-серверах.

coredns-mdns bootstrap
#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/systemd/system-preset/20-ignition.preset | grep coredns
enable coredns.service
enable coredns-download.service

#######################################################
set-thrush-bce3f9-233564 /home/admin # systemctl status coredns.service
● coredns.service - CoreDNS DNS server
     Loaded: loaded (/etc/systemd/system/coredns.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2023-06-10 18:26:18 UTC; 21h ago
       Docs: https://coredns.io
   Main PID: 934 (mdns-coredns)
      Tasks: 8 (limit: 15420)
     Memory: 33.4M
        CPU: 46.814s
     CGroup: /system.slice/coredns.service
             └─934 /opt/bin/mdns-coredns -conf=/etc/coredns.conf

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/systemd/system/coredns-download.service
[Unit]
Description=downloading coredns

Wants=network-online.target network.target
After=network-online.target
ConditionFileNotEmpty=|/opt/coredns64.tgz
ConditionFileNotEmpty=|!/opt/bin/mdns-coredns

[Service]
Type=oneshot

Environment="URL=https://nexus.dev.cloud.mts.ru/repository/golang-raw/containers/mdns-coredns/mdns-coredns-v1.10.0-3.tgz"
Environment="FILE=/opt/coredns64.tgz"

ExecStart=/usr/bin/wget -c --retry-connrefused --tries 50 -O $FILE $URL
ExecStartPost=tar -xvf $FILE -C /opt/

[Install]
WantedBy=multi-user.target


#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/coredns.conf
.:5000 {
  log . "[global] {combined}"{
    class denial error
  }

  debug

  bind lo
  cache 30

  forward . 10.99.43.154 {
    policy sequential
    expire 60s # expire (cached) connections after this time
  }
}

containerum:5000 {
  debug
  bind lo
  log . "[mdns] {combined}"
  mdns containerum 3
}

Используют плагин COREDNS - MDNS

KUBE-PROXY

kube-proxy bootstrap
#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/systemd/system-preset/20-ignition.preset | grep proxy
enable kube-proxy.service
enable kube-proxy-download.service

#######################################################
set-thrush-bce3f9-233564 /home/admin # systemctl status kube-proxy.service
● kube-proxy.service - kube-proxy
     Loaded: loaded (/etc/systemd/system/kube-proxy.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2023-06-10 21:47:16 UTC; 18h ago
   Main PID: 142352 (kube-proxy)
      Tasks: 5 (limit: 15420)
     Memory: 39.9M
        CPU: 4min 7.100s
     CGroup: /system.slice/kube-proxy.service
             └─142352 /opt/bin/kube-proxy --config=/etc/kubernetes/kube-proxy.yaml

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/kubernetes/kube-proxy.yaml
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
clientConnection:
  kubeconfig: "/etc/kubernetes/kube-proxy.kubeconfig"
mode: "ipvs"
clusterCIDR: 100.70.0.0/16

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/systemd/system/kube-proxy-download.service
[Unit]
Description=downloading kube-proxy

Wants=network-online.target network.target
After=network-online.target
ConditionFileNotEmpty=|!/opt/bin/kube-proxy
ConditionFileIsExecutable=|!/opt/bin/kube-proxy

[Service]
Type=oneshot

# Environment="URL=https://storage.googleapis.com/kubernetes-release/release/v1.24.6/bin/linux/amd64/kube-proxy"
Environment="URL=https://nexus.dev.cloud.mts.ru/repository/containerum-binaries/kube-proxy/kube-proxy-v1.24.6"
Environment="FILE=/opt/bin/kube-proxy"

ExecStart=/usr/bin/wget -c --retry-connrefused --tries 50 -O $FILE $URL
ExecStartPost=/usr/bin/chmod 755 $FILE

[Install]
WantedBy=multi-user.target

KUBELET BOOT

kubelet-bootstrap
#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/systemd/system-preset/20-ignition.preset | grep kubelet
enable kubelet.service
enable kubelet-download.service

#######################################################
set-thrush-bce3f9-233564 /home/admin # systemctl status kubelet.service
● kubelet.service - Kubernetes Kubelet
     Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2023-06-11 00:04:06 UTC; 16h ago
   Main PID: 206921 (kubelet)
      Tasks: 0 (limit: 15420)
     Memory: 53.1M
        CPU: 226ms
     CGroup: /system.slice/kubelet.service
             ‣ 206921 /opt/bin/kubelet \
        --config=/etc/kubernetes/kubelet.yaml \
        --kubeconfig=/etc/kubernetes/node.kubeconfig \
        --container-runtime=${KUBELET_RUNTIME} \
        --container-runtime-endpoint=${KUBELET_RUNTIME_ENDPOINT} \
        --cloud-provider=external \
        --runtime-cgroups=/systemd/system.slice \
        --root-dir=/opt/kubelet \
        --v=2

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/systemd/system/kubelet-download.service
[Unit]
Description=downloading kubelet

Wants=network-online.target network.target
After=network-online.target
ConditionFileNotEmpty=|!/opt/bin/kubelet
ConditionFileIsExecutable=|!/opt/bin/kubelet

[Service]
Type=oneshot

# Environment="URL=https://storage.googleapis.com/kubernetes-release/release/v1.24.6/bin/linux/amd64/kubelet"
Environment="URL= https://nexus.dev.cloud.mts.ru/repository/containerum-binaries/kubelet/kubelet-v1.24.6"
Environment="FILE=/opt/bin/kubelet"

ExecStart=/usr/bin/wget -c --retry-connrefused --tries 50 -O $FILE $URL
ExecStartPost=/usr/bin/chmod 755 $FILE

[Install]
WantedBy=multi-user.target

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/kubernetes/kubelet.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: "/etc/kubernetes/pki/ca.crt"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - 100.71.0.10
podCIDR: 100.70.0.0/16
runtimeRequestTimeout: "15m"
cgroupDriver: systemd
kubeletCgroups: "/systemd/system.slice"
systemCgroups: ""
tlsCertFile: "/etc/kubernetes/pki/node.crt"
tlsPrivateKeyFile: "/etc/kubernetes/pki/node.key"
staticPodPath: /etc/kubernetes/manifests
volumePluginDir: "/opt/kubelet/libexec/kubernetes/kubelet-plugins/volume/exec/"

#######################################################

set-thrush-bce3f9-233564 /home/admin # cat /etc/kubernetes/node.kubeconfig
apiVersion: v1
kind: Config
current-context: default
contexts:
- context:
    cluster: set-thrush-bce3f9
    user: system:node:set-thrush-bce3f9-233564
  name: default
clusters:
- cluster:
    certificate-authority-data: ***
    server: https://ExternalIP:6443
    tls-server-name: set-thrush-bce3f9
  name: set-thrush-bce3f9
users:
- user:
    client-certificate-data: ***==
    client-key-data: ***
  name: system:node:set-thrush-bce3f9-233564

Хочется обратить внимание на конфигурационный файл kubelet, kubeconfig и файл /opt/kubelet/config.json

KUBELET-CONFIG

mts-kubelet-config
---
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: "/etc/kubernetes/pki/ca.crt"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - 100.71.0.10
podCIDR: 100.70.0.0/16
runtimeRequestTimeout: "15m"
cgroupDriver: systemd
kubeletCgroups: "/systemd/system.slice"
systemCgroups: ""
tlsCertFile: "/etc/kubernetes/pki/node.crt"
tlsPrivateKeyFile: "/etc/kubernetes/pki/node.key"
staticPodPath: /etc/kubernetes/manifests
volumePluginDir: "/opt/kubelet/libexec/kubernetes/kubelet-plugins/volume/exec/"

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

fr-solutions-kubelet-config
registerNode: true
cgroupDriver: "systemd"
clusterDomain: "cluster.local"
cpuManagerReconcilePeriod: "0s"
fileCheckFrequency: "0s"
healthzBindAddress: "127.0.0.1"
httpCheckFrequency: "0s"
imageMinimumGCAge: "0s"
memorySwap: {}
nodeStatusReportFrequency: "1s"
nodeStatusUpdateFrequency: "1s"
resolvConf: "/run/systemd/resolve/resolv.conf"
runtimeRequestTimeout: "0s"
shutdownGracePeriod: "15s"
shutdownGracePeriodCriticalPods: "5s"
streamingConnectionIdleTimeout: "0s"
syncFrequency: "0s"
volumeStatsAggPeriod: "0s"
containerLogMaxSize: "50Mi"
maxPods: 250
kubeAPIQPS: 50
kubeAPIBurst: 100
podPidsLimit: 4096
serializeImagePulls: false
rotateCertificates: false
serverTLSBootstrap: true
tlsMinVersion: "VersionTLS12"
evictionPressureTransitionPeriod: "5s" 
imageGCHighThresholdPercent: 55
imageGCLowThresholdPercent: 50

authentication:
    anonymous:
        enabled: false
    webhook:
        cacheTTL: "0s"
        enabled: true

authorization:
    mode: "Webhook"
    webhook:
      cacheAuthorizedTTL:   "0s"
      cacheUnauthorizedTTL: "0s"

logging:
  flushFrequency: 0
  options:
    json: 
        infoBufferSize:  0
  verbosity:  0

systemReserved:
  ephemeral-storage: "1Gi"


featureGates:
  RotateKubeletServerCertificate: true
  APIPriorityAndFairness:         true
  DownwardAPIHugePages:           true
  PodSecurity:                    true
  CSIMigrationAWS:                false
  CSIMigrationAzureFile:          false
  CSIMigrationGCE:                false
  CSIMigrationvSphere:            false


tlsCipherSuites:
  - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
  - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
  - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
  - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
  - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"
  - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"

allowedUnsafeSysctls:
  - "net.core.somaxconn"

evictionSoft:
  "memory.available":   "3Gi"
  "nodefs.available":   "25%"
  "nodefs.inodesFree":  "15%"
  "imagefs.available":  "30%"
  "imagefs.inodesFree": "25%"

evictionSoftGracePeriod:
  "memory.available":   "2m30s"
  "nodefs.available":   "2m30s"
  "nodefs.inodesFree":  "2m30s"
  "imagefs.available":  "2m30s"
  "imagefs.inodesFree": "2m30s"

evictionHard:
  "memory.available":   "2Gi"
  "nodefs.available":   "20%"
  "nodefs.inodesFree":  "10%"
  "imagefs.available":  "25%"
  "imagefs.inodesFree": "15%"

Кроме указанного примера, существует ряд дополнительных мер, которые могут быть применены для повышения отказоустойчивости и безопасности. Однако конкретные значения и настройки будут зависеть от требований и характеристик инфраструктуры каждой организации.

Из нашего опыта, рекомендуется указывать все значения в конфигурационном файле, даже если они соответствуют базовым значениям. Это обеспечивает ясность и недвусмысленность для инженера, а также гарантирует однозначное поведение, даже если значения по умолчанию изменятся при обновлении.

KUBELET-KUBECONFIG

node-kubeconfig
#######################################################

set-thrush-bce3f9-233564 /home/admin # cat /etc/kubernetes/node.kubeconfig
apiVersion: v1
kind: Config
current-context: default
contexts:
- context:
    cluster: set-thrush-bce3f9
    user: system:node:set-thrush-bce3f9-233564
  name: default
clusters:
- cluster:
    certificate-authority-data: ***
    server: https://ExternalIP:6443
    tls-server-name: set-thrush-bce3f9
  name: set-thrush-bce3f9
users:
- user:
    client-certificate-data: ***==
    client-key-data: ***
  name: system:node:set-thrush-bce3f9-233564

Данный конфигурационный файл дает пищу для размышления.

  • Сертификаты передаются в открытом и нешифрованном виде через ignition config

  • В MTS Cloud, основанной на виртуализации VMWARE, конфигурационные файлы
    ignition хранятся в метаданных виртуализации в виде параметра kwargs, к
    которому могут иметь доступ инженеры

    • *Мы очень надеемся, что приватные ключи Центра Аутентификации (ЦА) не
      передаются вместе с бутстрап-конфигурацией кластера таким образом

  • Поскольку механизм bootstrap-kubeconfig не используется, можно предположить, что инженеры:

    • Используют внешний PKI из-за чего kube-controller-manager не обладает приватным ключом Центра Аутентификации (ЦА) и, следовательно, не может выписывать сертификаты узлам через стандартный механизм CSR APPROVE

    • Не используют данный механизм и разработали собственный метод обновления сертификатов, через ssh под пользователем admin - сервисом containerum@config-manager

  • Сертификаты выписываются на 1год, что по нашему мнению слишком много:

    • В нашей практике мы придерживаемся использования срока действия сертификатов не более одного месяца и регулярно обновляем их каждые две недели

В любом случае, есть куда расти и оптимизировать, так как многие best practice базируются на том, что в мультитенантных окружениях провайдер обеспечивает только стабильность своей области и не пересекает периметр клиента, где может размещаться критическая информация. Для реализации такого подхода рекомендуется использовать pull-модель и четко разделять ответственность между владельцами control-plane и data-plane.

/opt/kubelet/config.json

{"auths":{"registry.resources.cloud.mts.ru":{"username":"containerum-registry-guest","password":"cxqZrnmCy7LK7AQ","auth":"Y29udGFpbmVydW0tcmVnaXN0cnktZ3Vlc3Q6Y3hxWnJubUN5N0xLN0FR"}}}

Предположим, что эта учетная запись не имеет критического значения для безопасности системы, но ее можно получить, тогда как доступ к registry можно получить только с узлов K8S.

CONTAINERD

Данный инструмент сконфигурирован интересным образом.

containerd bootstrap
#######################################################
set-thrush-bce3f9-233564 /home/admin # systemctl status containerd
● containerd.service - containerd container runtime
     Loaded: loaded (/run/systemd/system/containerd.service; enabled-runtime; vendor preset: disabled)
    Drop-In: /etc/systemd/system/containerd.service.d
             └─10-use-custom-config.conf
     Active: active (running) since Sat 2023-06-10 18:26:17 UTC; 22h ago
       Docs: https://containerd.io
    Process: 852 ExecStartPre=mkdir -p /run/docker/libcontainerd (code=exited, status=0/SUCCESS)
    Process: 856 ExecStartPre=ln -fs /run/containerd/containerd.sock /run/docker/libcontainerd/docker-containerd.sock (code=exited, status=0/SUCCESS)
   Main PID: 857 (containerd)
      Tasks: 29
     Memory: 142.6M
        CPU: 25min 14.223s
     CGroup: /system.slice/containerd.service

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/systemd/system/containerd.service.d/10-use-custom-config.conf 
[Service]
ExecStart=
ExecStart=/usr/bin/containerd --config /etc/containerd/config.toml

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/containerd/config.toml
version = 2

# persistent data location
root = "/var/lib/containerd"
# runtime state information
state = "/run/containerd"
# set containerd as a subreaper on linux when it is not running as PID 1
subreaper = true
# set containerd's OOM score
oom_score = -999
disabled_plugins = []

# grpc configuration
[grpc]
address = "/run/containerd/containerd.sock"
# socket uid
uid = 0
# socket gid
gid = 0

[plugins."containerd.runtime.v1.linux"]
# shim binary name/path
shim = "containerd-shim"
# runtime binary name/path
runtime = "runc"
# do not use a shim when starting containers, saves on memory but
# live restore is not supported
no_shim = false

[plugins."io.containerd.grpc.v1.cri"]
# enable SELinux labeling
enable_selinux = true

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
# setting runc.options unsets parent settings
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry]
# registries mirrors path
config_path = "/etc/containerd/certs.d"

#######################################################
set-thrush-bce3f9-233564 /home/admin # ls -al /etc/containerd/certs.d
total 36
drwxr-xr-x. 9 root root 4096 Jun 10 17:35 .
drwxr-xr-x. 3 root root 4096 Jun 10 17:35 ..
drwxr-xr-x. 2 root root 4096 Jun 10 17:35 docker.io
drwxr-xr-x. 2 root root 4096 Jun 10 17:35 gcr.io
drwxr-xr-x. 2 root root 4096 Jun 10 17:35 ghcr.io
drwxr-xr-x. 2 root root 4096 Jun 10 17:35 k8s.gcr.io
drwxr-xr-x. 2 root root 4096 Jun 10 17:35 quay.io
drwxr-xr-x. 2 root root 4096 Jun 10 17:35 registry-1.docker.io
drwxr-xr-x. 2 root root 4096 Jun 10 17:35 registry.k8s.io

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/containerd/certs.d/k8s.gcr.io/hosts.toml 
server = "https://k8s.gcr.io"

[host."https://registry.resources.cloud.mts.ru/v2/k8s-gcr-io-proxy"]
  override_path = true
  capabilities = ["pull", "resolve"]

#######################################################
set-thrush-bce3f9-233564 /home/admin # cat /etc/containerd/certs.d/k8s.gcr.io/hosts.toml 
server = "https://k8s.gcr.io"

[host."https://registry.resources.cloud.mts.ru/v2/k8s-gcr-io-proxy"]
  override_path = true
  capabilities = ["pull", "resolve"]

В данной конфигурации осуществляется проксирование основных репозиториев через Nexus-registry от MTS Cloud. Это позволяет получать полезную информацию о используемых образах, их уязвимостях и других статистических данных при правильной настройке и подходе. Однако, в случае недоступности реестра МТS, сборки клиентов станут недоступными.

Как говорится два стула.

MTS cloud-controller

Cloud-controller имеет ряд контроллеров:

  • Node controller

    • Маркировка и аннотирование узла

    • Обогощение ресурса Node информацией полученной из MTS Cloud

    • Проверка работоспособности узла

    • Получение Internal и external ip адресов

  • Route controller

    • Отвечает за правильную настройку маршрутов в облаке

  • Service controller

    • Создание K8S сервиса type=LoadBalancer

Сразу вопросы:

  • Node controller:

  • Route controller:

    • Предположим, что данный контроллер не описан, поскольку для оверлейных сетей не требуется его использование или настройка

  • Service controller:

    • В данном случае все в порядке, однако некоторое беспокойство вызывает время создания сервиса типа LoadBalancer, которое иногда достигает 15-20 секунд

MTS csi-controller

Поскольку все окружение основано на стеке VMware, рекомендуется использовать нативный провайдер дисков для VMware, что и сделали инженеры MTS.

По умолчанию в MTS K8S имеется 4 класса хранилищ (storage class):

dk@dobry-kot-system:~/$ kubectl get  storageclass
NAME                     PROVISIONER              RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
mts-ssd-basic            csi.vsphere.vmware.com   Delete          Immediate           false                  2d23h
mts-ssd-fast (default)   csi.vsphere.vmware.com   Delete          Immediate           false                  2d23h
mts-ssd-ultra            csi.vsphere.vmware.com   Delete          Immediate           false                  2d23h
mts-ssd-ultra-plus       csi.vsphere.vmware.com   Delete          Immediate           false                  2d23h

Эта статья является обзорной и не содержит результатов нагрузочного тестирования выделяемых дисковых пространств.

IAM

MTS обладает значительным опытом в области управления идентификацией и аутентификацией (IAM), однако на данный момент не предоставлены интеграции с Kubernetes (K8S). Мы ожидаем, что в будущем будут предоставлены возможности интеграции для предоставления доступа командам с использованием нативного механизма управления доступом на основе ролей (RBAC), аналогичного тому, что предоставляет Yandex Cloud.

*Т.К не предоставляется возможным изменять конфигурацию kube-apiserver то сделать интеграцию со своим IDP не получится.

IAC

К сожалению, в настоящий момент отсутствует поддержка инструментов IaC, таких как Terraform и Pulumi. Это означает, что всю конфигурацию придется настраивать вручную, без возможности автоматизировать этот процесс.

Reconciliation loop

На момент создания кластера MTS разворачивает базовое окружение внутри K8S кластера:

  • calico

  • kube-state-metrics

  • coredns

  • etc..

Заострять внимание на компонентах не будем, так как неясно, для чего они там существуют. Клиент в любой момент может их удалить, исправить или сломать, и нет никакого механизма защиты от этого со стороны MTS. Если защита отсутствует, то скорее всего MTS не отвечают за эти компоненты и не помогут, если они сломаются. По моему мнению, лучше предоставить пустой кластер или написать собственный контроллер, который будет поддерживать актуальное состояние управляемых компонентов и их конфигурационных файлов.

Вывод

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

Лично нам продукт не подходит т.к отсутствуют необходимые инструменты взаимодействия, такие как поддержка terraform providers, механизмы изменения Feature Gates или хотя бы чтения установленных. Кроме того, в перспективе мы надеемся на наличие модифицированной конфигурации kubelet, так как при определенных нагрузках на узлы клиенты могут столкнуться с проблемами при использовании текущей конфигурации.

В целом, продукт MTS представляет собой перспективное решение, требующее дальнейшей доработки, но уже доступное для клиентов. Так же у MTS достаточно ресурсов и клиентской базы, что бы довести свое решение до нужного уровня и стать полноценным конкурентом того же Yandex Cloud.

В перспективе проведем анализ managed kubernetes от еще нескольких российских вендоров и постараемся собрать сравнительную базу, что бы показать плюсы и минусы каждого.

Контакты

Благодарим вас за прочтение статьи до конца, надеемся, что она оказалась для вас полезной. Мы будем рады, если вы подключитесь к нашему телеграм-каналу и зададите нам вопросы.

telegram community: https://t.me/fraima_ru
telegram mehttps://t.me/Dobry_kot

Комментарии (5)


  1. dobry-kot Автор
    26.06.2023 19:31

    Будем очень признательны если опишите в комментариях, что именно понравилось или не понравилось в статье и как бы мы ее могли улучшить.


  1. deepblack
    26.06.2023 19:31

    Замечательной особенностью является возможность входа по номеру телефона.

    Вот человек зарегистрировался, закинул денег на счет на год вперед, развернул проект, спустя пол-года поменял номер телефона. При этом доступа к старому номеру нет.
    Понимаю что вопрос не к вам, но

    Как в такой ситуации восстановить доступ?

    В последнее время наблюдаю тенденцию что многие российские сервисы внедрили возможность входа по номеру телефона, при этом удалив возможность войти по email и паролю.

    Из недавнего был случай

    Из недавнего был случай с Додо Пиццей (Dodo Engineering): не найдя ни на сайте ни в приложении возможности изменить номер (имея залогиненую на старый номер учетку в приложении) обратился в техподдержку с просьбой сменить номер привязанный в личном кабинете (при этом указал что нет доступа к старому номеру телефона, но есть доступ к почте указанной в учетной записе). На что получил ответ что такой возможности нет и прийдется заводить новую учетную запись с нуля на новый номер. Очень клиентоориентированно, учитывая что на старую учетку они слали промокоды со скидкой (при этом сам промокод можно активировать только на той учетной записи на email которой он высылался).


    1. dobry-kot Автор
      26.06.2023 19:31

      На самом деле очень интересный вопрос, что является ID клиента у МТС.
      Если использовать номер телефона МТС вроде все прозрачно по идеи должен быть внешний ID пользователя и к нему цепляют телефоны, если использовать внешние ну тут как с почтой даже хуже, если неиспользуется и перешел другому человеку тогда и доступ к ресурсам появится... . В любом случае почта наше все)


  1. biff_33
    26.06.2023 19:31
    +2

    Хочу прямой канал до Амстердама без всяких ТСПУ и на максимально возможной скорости. Возможно?


    1. dobry-kot Автор
      26.06.2023 19:31

      Тут не подскажу, лучше обратиться в поддержку МТС)