Differences

This shows you the differences between two versions of the page.


Previous revision
Next revision
linux_faq:kubernetes_using_single_node_as_master_and_worker [2020/07/09 15:01] – [Ingress, Cert-Manager и сертификаты Let's Encrypt] admin
Line 1: Line 1:
 +====== Single node Kubernetes setup - Ubuntu 18.04 ======
 +  sudo apt-get -y purge cloud-init && sudo rm -rf /etc/cloud
 +  sudo apt-get install -y apt-transport-https \
 +  ca-certificates curl \
 +  gnupg-agent \
 +  software-properties-common
 +  sudo apt-add-repository universe
 +  
 +  echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' | sudo tee /etc/apt/sources.list.d/kubernetes.list
 +  curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add
 +  
 +  sudo apt-get update
 +  sudo apt-get install -y docker.io kubeadm kubelet
 +  
 +  # Setup Docker daemon.
 +  cat > /etc/docker/daemon.json <<EOF
 +  {
 +    "exec-opts": ["native.cgroupdriver=systemd"],
 +    "log-driver": "json-file",
 +    "log-opts": {
 +      "max-size": "100m"
 +    },
 +    "storage-driver": "overlay2"
 +  }
 +  EOF
 +  
 +  sudo mkdir -p /etc/systemd/system/docker.service.d
 +  
 +  # Enable services and restart docker.
 +  sudo systemctl daemon-reload
 +  sudo systemctl enable docker
 +  sudo systemctl restart docker
 +  sudo systemctl enable kubelet.service
 +  
 +  sudo usermod -a -G docker $USER
 +  
 +  # To ensure that kubelet starts only after docker:
 +  cat << EOF | sudo tee /etc/systemd/system/kubelet.service.d/12-after-docker.conf
 +  [Unit]
 +  After=docker.service
 +  EOF
 +   
 +  # Kubernetes Cluster Init 
 +  sudo kubeadm init --pod-network-cidr=10.244.0.0/16
 +  
 +  mkdir -p $HOME/.kube
 +  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
 +  sudo chown $(id -u):$(id -g) $HOME/.kube/config
  
 +  #https://github.com/cloudnativelabs/kube-router/blob/master/docs/kubeadm.md
 +  #kubectl apply -f https://raw.githubusercontent.com/cloudnativelabs/kube-router/master/daemonset/kubeadm-kuberouter.yaml
 +  kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
 +  
 +  #kubectl -n kube-system patch deployment coredns --type=json -p='[{"op": "replace", "path": "/spec/replicas", "value":1}]'
 +  
 +  kubectl taint nodes --all node-role.kubernetes.io/master-
 +
 +====== Изменение редактора kubectl edit ======
 +  sudo awk -v line='export KUBE_EDITOR="/bin/nano"' 'FNR==NR && line==$0{f=1; exit} END{if (!f) print line >> FILENAME}' /etc/bash.bashrc
 +====== Автодополнение kubectl ======
 +  sudo awk -v line='source <(kubectl completion bash)' 'FNR==NR && line==$0{f=1; exit} END{if (!f) print line >> FILENAME}' /etc/bash.bashrc
 +===== /var/lib/kubelet/config.yaml =====
 +==== Failed to initialize CSINodeInfo ====
 +Если нода не переходит в состояние **Ready**, а при выполнении
 +  kubectl describe nodes
 +Видна ошибка:
 +  Failed to initialize CSINodeInfo: error updating CSINode annotation: timed out waiting for the condition; caused by: the server could not find the requested resource
 +то в файлике **/var/lib/kubelet/config.yaml** нужно добавить:
 +<code>featureGates:
 +  CSIMigration: false</code>
 +==== cgroupDriver ====
 +https://kubernetes.io/docs/setup/production-environment/container-runtimes/ \\
 +Также в файлике **/var/lib/kubelet/config.yaml** нужно включить такой же **cgroupDriver**, что и для **docker** (в файлике **/etc/docker/daemon.json**). По дефолту стоит **cgroupfs**. \\
 +И вообще - если в системе есть **systemd**, то нужно использовать **cgroupDriver**. Если будет включен **cgroupfs**, то ресурсами будут управлять два менеджера одновременно, что негативно сказыватся на стабильности. \\
 +То есть просто в конце файлика с нулевым отступом можно дописать: 
 +  cgroupDriver: systemd
 +
 +
 +
 +===== Kubelet Log level =====
 +  /etc/systemd/system/multi-user.target.wants/kubelet.service 
 +
 +  ExecStart=/usr/bin/kubelet --v=2
 +
 +  sudo systemctl daemon-reload
 +  sudo systemctl restart kubelet
 +====== CoreDNS ======
 +https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/#coredns \\
 +===== Изменяем дефолтный DNS-сервер для forwarding'a запросов =====
 +Конфигурация **CoreDNS** хранится в **ConfigMap coredns**
 +  kubectl edit cm coredns -n kube-system
 +Редактируем параметр **forward**. Вместо **/etc/resolv.conf** пописываем адрес нужного DNS-сервера
 +  forward . 192.168.1.100
 +
 +====== Kubernetes monitoring ======
 +Чтобы работали команды
 +<code>kubectl top nodes</code>и<code>kubectl top pods</code>
 +Нужно установить сервер метрик. 
 +===== Установка сервера метрик kubernetes =====
 +https://github.com/kubernetes-sigs/metrics-server
 +  kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml
 +В результате в неймспейсе **kube-system** появится **deployment** **metrics-server** и развернется **pod** **metrics-server-...**.\\
 +В нашем кластере отключен **ssl**, но он включен по-дефолту в **metrics-server**. Поэтому выполняем:
 +  kubectl edit deploy -n kube-system metrics-server
 +и в **spec.template.spec.containers.args** добавляем:
 +<code>  - --kubelet-insecure-tls</code>
 +Все. Через некоторое время команды **kubectl top** начнут выдавать осмысленную информацию. \\
 +Если этого не происходит, а в логах 
 +  kubectl logs -n kube-system metrics-server...
 +Видно такое:
 +  reststorage.go:160] unable to fetch pod metrics for ...
 +То можно попробывать добавить в в **spec.template.spec.containers.args** такое:<code>- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP</code>
 +====== Helm ======
 +С третьей версией **Helm** ничего в кластер ставить не нужно. Для использования достаточно бинарника и сконфигурированного **context** для доступа к кластеру.
 +
 +====== Доступ к подам снаружи ======
 +Для доступа к сервисам, которые предоставляют поды, нужно установить и настроить:
 +  * [[https://metallb.universe.tf/|LoadBalancer MetalLB]] для получения сервисами IP-адресов локальной (или нелокальной) сети.
 +  * **Ingress Controller** для маршрутизации **HTTP/HTTPS** запросов.
 +  * **Cert Manager** для управления сертификатами (в том числе и **Let's Encrypt**).
 +===== LoadBalancer - MetalLB =====
 +https://habr.com/ru/company/southbridge/blog/443110/ \\
 +https://metallb.universe.tf/installation/ \\
 +  kubectl create ns metallb-system
 +  helm install --name metallb stable/metallb --namespace metallb-system
 +При установке с помощью **helm** конфигурация хранится в **ConfigMap** с именем **metallb-config**, который лежит в том же неймспейсе куда установлен **metallb** (в данном случае - **metallb-system**).
 +<code>apiVersion: v1
 +kind: ConfigMap
 +metadata:
 +  namespace: metallb-system
 +  name: metallb-config
 +data:
 +  config: |
 +    address-pools:
 +    - name: default
 +      protocol: layer2
 +      addresses:
 +      - 192.168.77.160-192.168.77.189
 +</code>
 +==== Апгрейдим metallb ====
 +  helm repo update
 +  helm upgrade metallb stable/metallb --namespace metallb-system
 +
 +===== Ingress-controller =====
 +https://docs.cert-manager.io/en/latest/tutorials/acme/quick-start/# \\
 +Либо тоже самое можно сделать с помощью helm-чарта:
 +  kubectl create ns ingress
 +  #helm install stable/nginx-ingress --name http-https --namespace ingress
 +  helm install stable/nginx-ingress --name http-https --namespace ingress --set controller.service.type=LoadBalancer,controller.service.externalTrafficPolicy=Local,controller.service.loadBalancerIP=192.168.77.160 \
 +  --set controller.addHeaders."X-XSS-Protection"="1\;mode=block" \
 +  --set controller.addHeaders."Content-Security-Policy"="default-src 'self'; \
 +  script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://www.googletagmanager.com 'nonce-HM305BL1jG4mB1xm' https://mc.yandex.ru; \
 +  img-src 'self' https://www.google-analytics.com https://*.yandex.ru; \
 +  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \
 +  font-src 'self' https://themes.googleusercontent.com;" \
 +  --set controller.addHeaders."Strict-Transport-Security"="max-age=31536000;includeSubdomains;preload" \
 +  --set controller.addHeaders."X-Frame-Options"="SAMEORIGIN" \
 +  --set controller.addHeaders."X-Content-Type-Options"="nosniff"
 +Ну или без драконовских запретов (заголовки **Content-Security-Policy** влияют на все ресурсы за этим **ingress-controller** и что-то может просто не заработать):
 +  helm upgrade http-https stable/nginx-ingress --set controller.service.type=LoadBalancer \
 +  --set controller.service.externalTrafficPolicy=Local \
 +  --set controller.service.loadBalancerIP=192.168.77.160 \
 +  --set controller.addHeaders."X-XSS-Protection"="1\;mode=block" \
 +  --set controller.addHeaders."Strict-Transport-Security"="max-age=31536000;includeSubdomains;preload" \
 +  --set controller.addHeaders."X-Frame-Options"="SAMEORIGIN" \
 +  --set controller.addHeaders."X-Content-Type-Options"="nosniff"
 +
 +В результате - в неймспейсе **ingress** появится сервис **http-https-nginx-ingress-controller**, у которого будет тип **LoadBalancer** и который получит указанный IP-адрес в локальной сети из диапазона, сконфигурированного для **metallb**. \\
 +Теперь можно создавать **ingress**'ы, которые будут смотреть на сервисы внутри кластера и предоставлять к ним доступ. Пользователи будут посылать запросы на **ingress-controller**, а он, в свою очередь, - пересылать их на **ingress**'ы.
 +
 +===== Cert manager =====
 +https://docs.cert-manager.io/en/latest/tutorials/acme/quick-start/
 +https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html#installing-with-helm \\
 +Устанавливаем:
 +<code># Install the CustomResourceDefinition resources separately
 +kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.11/deploy/manifests/00-crds.yaml
 +
 +# Create the namespace for cert-manager
 +kubectl create namespace cert-manager
 +
 +# Add the Jetstack Helm repository
 +helm repo add jetstack https://charts.jetstack.io
 +
 +# Update your local Helm chart repository cache
 +helm repo update
 +
 +# Install the cert-manager Helm chart
 +helm install \
 +  --name cert-manager \
 +  --namespace cert-manager \
 +  --version v0.11.0 \
 +  jetstack/cert-manager
 +</code>
 +https://docs.cert-manager.io/en/latest/tasks/issuers/index.html \\
 +Создаем издателя **Let's Encrypt** (ACME Issuer):
 +<code>apiVersion: cert-manager.io/v1alpha2
 +kind: ClusterIssuer
 +metadata:
 +  name: letsencrypt
 +spec:
 +  acme:
 +    # You must replace this email address with your own.
 +    # Let's Encrypt will use this to contact you about expiring
 +    # certificates, and issues related to your account.
 +    email: mike@autosys.tk
 +    server: https://acme-staging-v02.api.letsencrypt.org/directory
 +    privateKeySecretRef:
 +      # Secret resource used to store the account's private key.
 +      name: cert-issuer-account-key
 +    # Add a single challenge solver, HTTP01 using nginx
 +    solvers:
 +    - http01:
 +        ingress:
 +          class: nginx
 +</code>
 +Смотрим на его состояние:
 +  kubectl describe clusterissuer letsencrypt
 +Должно быть так:
 +<code>Status:
 +  ...
 +  Conditions:
 +    ...
 +    Status:                True
 +    Type:                  Ready
 +</code>
 +
 +==== Апгрейдим cert-manager ====
 +  helm repo update
 +  helm upgrade --namespace cert-manager cert-manager jetstack/cert-manager
 +===== Ingress, Cert-Manager и сертификаты Let's Encrypt =====
 +<del>https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html</del>\\
 +<del>https://docs.bitnami.com/kubernetes/how-to/secure-kubernetes-services-with-ingress-tls-letsencrypt/#step-3-configure-tls-with-let-s-encrypt-certificates-and-cert-manager</del>\\
 +https://docs.cert-manager.io/en/latest/tutorials/acme/quick-start/#\\
 +Итак, у нас установлен **MetalLB**, **Ingress Controller** и **CertManager**. \\
 +В результате, сервис **Ingress Controller** должен получить адрес в локальной (или нелокальной сети) из диапазона, прописанного в конфигурации **MetalLB**:
 +<code># kubectl get svc -n ingress
 +NAME                                       TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                      AGE
 +http-https-nginx-ingress-controller        LoadBalancer   10.100.109.113   192.168.77.160   80:31833/TCP,443:32690/TCP   5h1m
 +http-https-nginx-ingress-default-backend   ClusterIP      10.107.152.17    <none>           80/TCP                       5h1m
 +</code>
 +Для примера буду делат доступ к **AWX**, который установлен в моем кластере **Kubernetes**. \\
 +В неймспейсе, куда развернут **AWX** (в моем случае это **awx**) делаем **ingress**, который будет направлять запросы в соотвествующий сервис, где работает web-морда **AWX**:
 +<code>apiVersion: extensions/v1beta1
 +kind: Ingress
 +metadata:
 +  annotations:
 +    kubernetes.io/ingress.class: nginx
 +  name: awx-web
 +  namespace: awx
 +spec:
 +  rules:
 +  - host: awx.autosys.tk
 +    http:
 +      paths:
 +      - backend:
 +          serviceName: awx-web-svc
 +          servicePort: 80
 +        path: /
 +  tls:
 +  - hosts:
 +    - awx.autosys.tk
 +    secretName: awx-autosys-tk-tls
 +</code>
 +В результате, HTTP-запросы на **IP**-адрес **Ingress**-контроллера будут перенаправлены на сервис web-морды AWX. \\
 +\\
 +Теперь нужно сконфигурировать **ACME Issuer** - это сущность, которая будет запрашивать сертификаты по протоколу **ACME** у серверов **Let's Encrypt**:
 +<code>apiVersion: cert-manager.io/v1alpha2
 +kind: ClusterIssuer
 +metadata:
 +  name: letsencrypt
 +  namespace: cert-manager
 +spec:
 +  acme:
 +    # You must replace this email address with your own.
 +    # Let's Encrypt will use this to contact you about expiring
 +    # certificates, and issues related to your account.
 +    email: mike@autosys.tk
 +    server: https://acme-v02.api.letsencrypt.org/directory
 +    privateKeySecretRef:
 +      # Secret resource used to store the account's private key.
 +      name: letsencrypt-cert-issuer-account-key
 +    # Add a single challenge solver, HTTP01 using nginx
 +    solvers:
 +    - http01:
 +        ingress:
 +          class: nginx
 +</code>
 +После того, как создан **Issuer** (в данном случае - **ClusterIssuer** с именем **letsencrypt**) можно добавить в манифест ингресса информацию о нем. Редактируем ресурс **Ingress** и приводим секцию **annotations** к виду:
 +<code>kind: Ingress
 +metadata:
 +  annotations:
 +    cert-manager.io/cluster-issuer: letsencrypt
 +    kubernetes.io/ingress.class: nginx
 +  name: awx-web
 +  namespace: awx
 +</code>
 +Все. Теперь можно увидеть, что появился сертификат: 
 +  kubectl get certificate -A
 +Если сертификат долго не готов, то нужно смотреть логи пода **cert-manager**:
 +  kubectl get po -n cert-manager
 +  kubectl logs cert-manager-55c44f98f-g9vrb -n cert-manager -f
 +У меня, например, возникали проблемы с резолвингом доменнного имени, для которого получаем сертификат в поде **cert-manager**, хотя из интернета имя резолвилось корректно и маршрутизация трафика работала нормально, но **cert-manager**, перед тем как запросить проверку у ACME-сервера, пытается сам убедиться, что проверка проходит, но так как имя в локалке не резолвилось корректно, то проверка не прошла.
 +====== Rancher ======
 +**Rancher** - это web-консоль для управления кластеами **kubernetes**. Устанавливать осторожно, поскольку она ставит с кластер много всякого и полностью удалить ее может быть проблематично. \\
 +https://rancher.com/docs/rancher/v2.x/en/installation/ha/helm-rancher/ \\
 +Если у нас уже установлены **MetalLB**, **Ingress Controller** и **Cert-Manager**, то можно ставить так:
 +  helm install rancher-latest/rancher --name rancher --namespace cattle-system --set hostname=rancher.autosys.tk --set ingress.tls.source=letsEncrypt --set letsEncrypt.email=mike@autosys.tk
 +В моем случае, **helm** ругнулся:
 +  Error: validation failed: unable to recognize "": no matches for kind "Issuer" in version "certmanager.k8s.io/v1alpha1"
 +После этого я скачал чарт:
 +  helm fetch rancher-latest/rancher
 +Распаковал его:
 +  tar -xvf ./rancher-2.3.0.tgz
 +И просто удалил файл, который создает **Issuer**, так как у меня в кластере уже есть **ClusterIssuer**.
 +  rm ./rancher/templates/issuer-letsEncrypt.yaml
 +Затем запаковал чарт обратно и установил его:
 +  mv ./rancher-2.3.0.tgz ./rancher-2.3.0.tgz.orig
 +  helm package rancher
 +  helm install ./rancher-2.3.0.tgz --name rancher --namespace cattle-system --set hostname=rancher.autosys.tk --set ingress.tls.source=letsEncrypt --set letsEncrypt.email=mike@autosys.tk
 +В итоге **rancher** установился нормально! \\
 +Затем я отредактировал **ingress**, заменив issuer:
 +  certmanager.k8s.io/issuer: rancher
 +на уже существующий в кластере:
 +    cert-manager.io/cluster-issuer: letsencrypt
 +    kubernetes.io/ingress.class: nginx
 +Залогиниться в первый раз можно с помощью логина/пароля admin/admin.
 +====== awx on single nodes kubernetes ======
 +Для базы данных я использую внешний сервер **postgres**, то есть **StorageClass** и **PersistentVolume** для базы мне не нужны. \\
 +После запуска playbook'a будет создан **StatefullSet**. Однако, если нода достаточно слабая, то под не запустится, потому что не хватит ресурсов. Нужно удалить все объекты **resources** с резервированием и лимитами ресурсов:
 +  kubectl edit sts awx -n awx
 +Также, нода будет иметь **taint**:
 +  taints:
 +  - effect: NoSchedule
 +    key: node.kubernetes.io/disk-pressure
 +    timeAdded: "2019-10-01T12:28:30Z"
 +Чтобы под запустился нужно добавить в конфигурацию **StatefullSet**:
 +  kubectl edit sts awx -n awx
 +соответствующий **toleration** (в **spec:template:spec:**. Например - перед **volumes**):
 +<code>      tolerations:
 +      - effect: NoSchedule
 +        operator: Exists
 +</code>
  • linux_faq/kubernetes_using_single_node_as_master_and_worker.txt
  • Last modified: 2024/04/22 12:38
  • by admin