k8s

Эта статья на Хабре https://habr.com/ru/articles/783586/

Введение

Зачем?

Представим ситуацию, что мы деплоим по push-модели. В качестве платформы для запуска деплоя у нас используется Gitlab: в нём настроен пайплайн и джобы, разворачивающие приложения в разные окружения в Kubernetes

Какой бы инструмент мы не использовали (kubectl, helm), для манипуляций с ресурсами API нам в любом случае будет необходимо аутентифицироваться при выполнении запросов к Kubernetes. Для этого в запросе надо передать данные для аутентификации, будь то токен или сертификат. И тут возникает несколько вопросов:

  1. Где хранить эти креды?

    Хранить креды от кластера можно в Gitlab CI/CD Variables и подставлять в джобу деплоя, но тогда потенциально все пользователи будут деплоить с одними и теми же доступами

  2. Как сделать так, чтобы у каждого пользователя были свои данные для доступа в кластер?

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

А что если сделать так, чтобы в качестве провайдера аутентификационных данных для Kubernetes выступал сам Gitlab? Тогда не надо было бы нигде хранить креды, и каждый пользователь мог бы аутентифицироваться в кубере под своей учёткой при запуске деплоя

FATAL post-upgrade error: unable to create/update the DNS service: services “kube-dns” not found

https://github.com/kubernetes/kubeadm/issues/2358

Необходимо скопировать текущий манифест сервиса coredns, изменить название на kube-dns, изменить clusterIP так, чтобы он заканчивался на .10 (например, 10.233.0.10) и применить новый манифест. Адрес .10 может уже занят другим сервисам, необходимо его будет освободить

kubectl -n kube-system get svc coredns -o yaml > kube-dns.yaml
# edit kube-dns.yaml
kubectl -n kube-system apply -f kube-dns.yaml

FATAL post-upgrade error: unable to create deployment: Post "https://127.0.0.1:6443/apis/apps/v1/namespaces/kube-system/deployments?timeout=10s": net/http: request canceled (Client.Timeout exceeded while awaiting headers)

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

  • медленная запись на диск через etcd
  • в процессе добавления ресурса срабатывает Mutation Webhook (kubectl get mutatingwebhookconfigurations. admissionregistration.k8s.io). Этот хук означает, что надо где-то изменить отправленный в API манифест. Тот сервис, который должен изменить, по какой-то причине делает это медленно или не делает это вообще - необходимо искать проблему там. Временное решение - отключить тот сервис, чтобы не срабатывал вебхук

Это примерный процесс обновления, который проверен на версиях 1.15-1.18 На более поздних версиях этот процесс должен быть сильно проще, особенно в плане обновления сертификата kubelet на master-нодах

На master-нодах

  1. Обновить сертификаты компонентов кубера с помощью kubeadm
    for cert in apiserver apiserver-kubelet-client front-proxy-client admin.conf controller-manager.conf scheduler.conf; do
        /usr/local/bin/kubeadm alpha certs renew $cert;
    done
    
  2. Обновить сертификаты kubelet
    #!/bin/bash
    set -eu -o pipefail
    
    ## kubelet
    kube_dir="/etc/kubernetes"
    
    ### v3 extensions settings
    cat << EOF > ${kube_dir}/v3_ext
    keyUsage=keyEncipherment,digitalSignature
    extendedKeyUsage=clientAuth
    EOF
    
    ### get cert and key
    cat ${kube_dir}/kubelet.conf | grep certificate-data | awk '{print $2}' | base64 -d > ${kube_dir}/kubelet.crt
    cat ${kube_dir}/kubelet.conf | grep key-data | awk '{print $2}' | base64 -d > ${kube_dir}/kubelet.key
    
    ### generate csr and sign it
    openssl x509 -x509toreq -in ${kube_dir}/kubelet.crt -out ${kube_dir}/kubelet.csr -signkey ${kube_dir}/kubelet.key
    openssl x509 -req -in ${kube_dir}/kubelet.csr -CA ${kube_dir}/ssl/ca.crt -CAkey ${kube_dir}/ssl/ca.key -CAcreateserial -out ${kube_dir}/kubelet.crt -days 365 -extfile ${kube_dir}/v3_ext
    
    ### insert cert to kubelet.conf
    cert_data=$(cat ${kube_dir}/kubelet.crt | base64 -w0)
    sed -i "s/client-certificate-data: .*$/client-certificate-data: $cert_data/" ${kube_dir}/kubelet.conf
    
    rm -f ${kube_dir}/kubelet.csr ${kube_dir}/kubelet.crt ${kube_dir}/kubelet.key ${kube_dir}/v3_ext
    

Это же можно сделать проще /usr/local/bin/kubeadm alpha kubeconfig user --org system:nodes --client-name system:node:$(hostname) >/etc/kubernetes/kubelet.conf

Для версии >=1.20 можно также задать cluster-name /usr/local/bin/kubeadm alpha kubeconfig user --org system:nodes --client-name system:node:$(hostname) --cluster-name cluster.local >/etc/kubernetes/kubelet.conf

  1. Перезапустить docker и kubelet systemctl restart docker kubelet

На worker-нодах надо что-то делать только если уже не было включено автоматическое обновление

  1. Включить автоматическое обновление сертификата kubelet grep -Fr rotateCertificates /etc/kubernetes/kubelet-config.yaml || echo "rotateCertificates: true" >> /etc/kubernetes/kubelet-config.yaml

  2. Перезапустить kubelet systemctl restart kubelet

  1. Пользователь генерит csr и передаёт администратору кластера openssl req -new -newkey rsa:2048 -nodes -keyout i.ivanov.key.pem -out i.ivanov.csr -subj "/CN=i.ivanov /O=MY.ORG"
  2. Администратор кластера создаёт манифест CSR в kubernetes, где сертификат закодирован в base64
    cat <<EOF | kubectl apply -f -
    apiVersion: certificates.k8s.io/v1
    kind: CertificateSigningRequest
    metadata:
      name: i.ivanov
    spec:
      request:
        <CSR_CONTENT>
      signerName: kubernetes.io/kube-apiserver-client
      usages:
      - client auth
    EOF
    
  3. Администратор кластера выпускает сертификат, подписанный куберовским CA kubectl certificate approve i.ivanov
  4. Администратор кластера копирует полученный сертификат и пересылает его пользователю. Подтвержденный csr хранится в течение часа kubectl get csr/i.ivanov -o json | jq -r '.status.certificate' | base64 -D > i.ivanov.crt.pem
  5. Пользователь добавляет в свой локальный ~/.kube/config настройки для кластера. Администратор передаёт значения certificate-authority-data, server, client-certificate-data (п. 4) пользователю.
  6. Управление доступом пользователей осуществляется через RBAC

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

Контейнеры продолжают работать, но в момент синхронизации состояния подов с api-server поды могут переходить в состояние 0/1 Running, когда трафик на них перестаёт направляться

Остановить kubelet

Нода переходит в состояние NotReady, никаких событий на подах не происходит - они продолжают пребывать в состоянии 1/1 Running, но трафик на них перестаёт идти (из endpoint’ов сервисов удаляются IP адрес подов, которые находятся на “мёртвой” ноде).

Спустя указанный pod-eviction-timeout (5m по-умолчанию) для kube-controller-manager поды переходят в статус Terminating и начинают запускаться на живых воркерках. Поды будут продолжать находиться в статусе Terminating либо до старта kubelet, либо до удаления ноды из кластера. При этом не выгоняются поды DaemonSet и поды, поднятые kubelet’ом(kube-proxy, nginx-proxy, kube-flannel, nodelocaldns и т.п.)

Немного о RBAC

В kubernetes есть сущность, которая называется RoleBinding. Она определяет, каким субъектам какая роль будет назначена. RoleBinding действует в рамках namespace, в котором она создана. Для разрешения действий во всех namespace без ограничений необходимо создавать ClusterRoleBinding, который является cluster-wide объектом.

Пример RoleBinding

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: edit
  namespace: project-a-devel
subjects:
- kind: Group
  name: sre
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: edit
  apiGroup: rbac.authorization.k8s.io

Эта роль:

  • Действует в рамках namespace project-a-devel - (metadata:)
  • Применяется к пользователям в группе sre (/O=sre) - (subjects:)
  • Разрешает действия, описанные в созданной по-умолчанию кластерной роли (ClusterRole) edit - (roleRef:)

Чем отличаются всякие там NodePorts, LoadBalancer и Ingress? Все они дают возможность внешнему трафику попасть в ваш кластер, но дают эту возможность по-разному. Давайте-ка разберёмся, как они это делают и когда какой тип сервиса лучше использовать.

ClusterIP

ClusterIP — это дефолтный тип сервиса в кубах, он поднимает вам сервис внутри кластера на внутрекластеровом IP. Доступа для внешнего трафика нет, только внутри кластера.

YAML для ClusterIP выглядит как-то так:

apiVersion: v1
kind: Service
metadata:  
  name: my-internal-service
spec:
  selector:    
    app: my-app
  type: ClusterIP
  ports:  
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP

“Так, ну и зачем же рассказывать нам о ClusterIP, если он не принимает внешний трафик?” — спросите вы. А всё потому, что попасть на этот сервис можно через Kubernetes proxy!