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

Если нода не переходит в состояние 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 нужно добавить:

featureGates:
  CSIMigration: false

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
/etc/systemd/system/multi-user.target.wants/kubelet.service 
ExecStart=/usr/bin/kubelet --v=2
sudo systemctl daemon-reload
sudo systemctl restart kubelet

CoreDNS

Конфигурация CoreDNS хранится в ConfigMap coredns

kubectl edit cm coredns -n kube-system

Редактируем параметр forward. Вместо /etc/resolv.conf пописываем адрес нужного DNS-сервера

forward . 192.168.1.100

Kubernetes monitoring

Чтобы работали команды

kubectl top nodes

и

kubectl top pods

Нужно установить сервер метрик.

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 добавляем:

  - --kubelet-insecure-tls

Без этого параметра metrics-server не запустится, а в логах будет примерно такое: «code>[unable to fully scrape metrics from node kub-worker01: unable to fetch metrics from node kub-sbl-apps-dev-worker01: Get “https://192.168.44.11:10250/stats/summary?only_cpu_and_memory=true”: x509: cannot validate certificate for 192.168.44.11 because it doesn't contain any IP SANs </code> Все. Через некоторое время команды kubectl top начнут выдавать осмысленную информацию.
Если этого не происходит, а в логах

kubectl logs -n kube-system metrics-server...

Видно такое:

reststorage.go:160] unable to fetch pod metrics for ...

То можно попробывать добавить в в spec.template.spec.containers.args такое:

- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP

Helm

С третьей версией Helm ничего в кластер ставить не нужно. Для использования достаточно бинарника и сконфигурированного context для доступа к кластеру.

Обновление kubernetes

смотрим какие версии kubeadm нам доступны

sudo apt-get update
apt-cache madison kubeadm

Разрешаем обновление kubernetes-cni и kubeadm:

sudo apt-mark unhold kubernetes-cni kubeadm

Ставим нужную версию kubeadm:

sudo apt-get install kubeadm=1.20.9-00

Проверяем возможность апгрейда:

sudo kubeadm upgrade plan

Если у нас хост с containerd (без docker), то нужно учесть, что kubeadm понадобится docker для получения images. Поэтому временно ставим его:

sudo apt-get install docker.io

Обновляем:

sudo kubeadm upgrade apply v1.20.9

Разрешаем обновление kubelet и kubectl

sudo apt-mark unhold kubelet kubectl

И обновляем их до нужной версии:

sudo apt-get install -y kubelet=1.20.9-00 kubectl=1.20.9-00

Морозим обратно версии пакетов:

sudo apt-mark hold kube*

Апгрейдим всю систему:

sudo apt-get update && sudo apt-get upgrade

Доступ к подам снаружи

Для доступа к сервисам, которые предоставляют поды, нужно установить и настроить:

  • LoadBalancer MetalLB для получения сервисами IP-адресов локальной (или нелокальной) сети.
  • Ingress Controller для маршрутизации HTTP/HTTPS запросов.
  • Cert Manager для управления сертификатами (в том числе и Let's Encrypt).

https://habr.com/ru/company/southbridge/blog/443110/
https://metallb.universe.tf/installation/

helm repo add metallb https://metallb.github.io/metallb
helm repo update
kubectl create ns metallb-system 
helm upgrade --install -n metallb-system metallb metallb/metallb \
  --set configInline.address-pools[0].name="default" \
  --set configInline.address-pools[0].protocol="layer2" \
  --set configInline.address-pools[0].addresses[0]="192.168.77.160-192.168.77.189"
helm repo update
helm upgrade -n metallb-system metallb metallb/metallb --reuse-values

https://docs.cert-manager.io/en/latest/tutorials/acme/quick-start/#
Либо тоже самое можно сделать с помощью helm-чарта:

kubectl create ns ingress
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
#helm install stable/nginx-ingress --name http-https --namespace ingress
helm upgrade --install nginx -n ingress ingress-nginx/ingress-nginx \
--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 --install nginx -n ingress ingress-nginx/ingress-nginx \
--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 появится сервис nginx-ingress-nginx-controller, у которого будет тип LoadBalancer и который получит указанный IP-адрес в локальной сети из диапазона, сконфигурированного для metallb.
Теперь можно создавать ingress'ы, которые будут смотреть на сервисы внутри кластера и предоставлять к ним доступ. Пользователи будут посылать запросы на ingress-controller, а он, в свою очередь, - пересылать их на ingress'ы.

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
Устанавливаем:

kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update

helm upgrade --install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.4.0 \
  --set installCRDs=true

https://docs.cert-manager.io/en/latest/tasks/issuers/index.html
Создаем издателя Let's Encrypt (ACME Issuer):

kubectl apply -f - << EOF
apiVersion: cert-manager.io/v1
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
EOF

Смотрим на его состояние:

kubectl describe clusterissuer letsencrypt

Должно быть так:

Status:
  ...
  Conditions:
    ...
    Status:                True
    Type:                  Ready
helm repo update
helm upgrade --namespace cert-manager cert-manager jetstack/cert-manager --reuse-values

https://docs.cert-manager.io/en/latest/getting-started/install/kubernetes.html
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
https://docs.cert-manager.io/en/latest/tutorials/acme/quick-start/#
Итак, у нас установлен MetalLB, Ingress Controller и CertManager.
В результате, сервис Ingress Controller должен получить адрес в локальной (или нелокальной сети) из диапазона, прописанного в конфигурации MetalLB:

# 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

Для примера буду делат доступ к AWX, который установлен в моем кластере Kubernetes.
В неймспейсе, куда развернут AWX (в моем случае это awx) делаем ingress, который будет направлять запросы в соотвествующий сервис, где работает web-морда AWX:

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

В результате, HTTP-запросы на IP-адрес Ingress-контроллера будут перенаправлены на сервис web-морды AWX.

Теперь нужно сконфигурировать ACME Issuer - это сущность, которая будет запрашивать сертификаты по протоколу ACME у серверов Let's Encrypt:

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

После того, как создан Issuer (в данном случае - ClusterIssuer с именем letsencrypt) можно добавить в манифест ингресса информацию о нем. Редактируем ресурс Ingress и приводим секцию annotations к виду:

kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt
    kubernetes.io/ingress.class: nginx
  name: awx-web
  namespace: awx

Все. Теперь можно увидеть, что появился сертификат:

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

соответствующий tolerationspec:template:spec:. Например - перед volumes):

      tolerations:
      - effect: NoSchedule
        operator: Exists
Enter your comment. Wiki syntax is allowed:
V U P J O
 
  • linux_faq/kubernetes_using_single_node_as_master_and_worker.txt
  • Last modified: 2021/07/16 08:39
  • by admin