poniedziałek, 27 września 2021

Behind the Kubernetes wheel

Everyone is containerizing with Docker. Thanks to this we can vastly implement new software versions, however, this post is not about the advantages of containerization. I want to focus more on Kubernetes.

In my work, I have very often encountered problems with scaling the application. When the load increases, you need to up the right number of containers to handle it. We must do it quickly with one command and not by calling another „docker run”. Thanks to this action savings appear.

The number of deployments based on Docker that we can have in our companies makes it necessary to manage a container farm. Without the right tools, resources can’t be controlled effectively. Additionally, it is necessary to keep the configuration in a place that can be shared to easily restore the services. So here we have a concept of orchestration.

The phenomenon of containerization is not so new, but the orchestration relatively is. If we already decided to put our software into Docker, then in order not to be a victim of our success, sooner or later, we will have to choose a solution that will provide us a stability when scaling up the project. It is worth to know the concept even if we do not plan to increase the size of the stack beyond one frontend container and one backend container.

What is the orchestration?

Let’s get through some basic definitions for rookies. An orchestration is a mechanism which allows you to administrate servers cluster with running a containerization service.

Kubernetes is a big open source project originally designed by the Google. Huge community means that code can have very little bugs, but if from the beginning it was written wrong or a community doesn’t have enough skills to make a code safe, then there may be a lot of holes in it, like in WordPress for example.

K8s (synonym for „Kubernetes”) is free and anyone can implement it on their own infrastructure. It’s great for managing containers on development servers that require frequent changes.

I don’t have to say that initiation of orchestration is necessary if you have a bunch of servers which run many applications in containers. Thanks to the orchestration we can keep our configuration in the repository, create changes and easily deploy, even in a case of accidental removal of the given containers. It also allows for comfortable scaling which is needed, when we want our solutions to have high availability.

Why not Docker Swarm?

It is worth noting that Docker himself encourages to use Kubernetes. Kubernetes gives you more options to scale your applications. There was a case when I wanted to have many replicas of Apache Kafka and ZooKeeper and doing it on Docker Swarm failed. However, with Kubernetes it did not cause any major problems.

Both orchestration technologies allow you to define in the file what applications you want to run and how. However, Kubernetes has many types of so-called manifestos. It gives more flexibility. In order to define deployments for Docker Swarm, we must additionally install the Docker Compose tool, for which the files must be in the right version.

Namespaces give us separation of our deployments. If we properly watch where we put the containers, then, for example, we don’t need to create additional Kubernetes clusters and fear data overwriting. With one cluster we can better manage our resources.

Kubernetes has native service discovery technology using etcd. In addition, it allows balancing the load. It makes the services register under a specific name and can be easily recognized by the name of the space.

For some people, it may be a defect that the Docker Compose only allows you to use YAML files when Kubernetes also accepts JSON.

If we want to use Kubernetes in the cloud we can use various solutions such as:

  • Google Container Engine (Google),
  • Cloud Kubernetes Service (IBM),
  • Azure Kubernetes Service (Microsoft),
  • VMware Kubernetes Engine (VMware).

If someone used Docker Compose earlier it will be difficult for him to go to manifestos but I think that is worth considering the benefits. There is a similar situation with the Vi editor which is very difficult to use in the beginning, but the more time we spend using it, the more it speeds up our work.

The main servers in the Kubernetes cluster provide APIs implementing the RESTful interface. Thanks to that we can communicate with the cluster using various programs.

Why companies should start using Kubernetes?

The benefits of having your own applications in containers are obvious. Orchestration using Kubernetes gives you the ability to automatically handle the network, store data (you can also choose your own Software Defined Storage), autoscaling, logging, notifications etc. However, the most important factor for business is that K8s can significantly reduce costs by better use of hardware resources without affecting application performance or user experience and even more – you can see a performance increase.

czwartek, 5 sierpnia 2021

Wspaniały DevOps

Docker:

Kubernetes:
Helm:
Terraform:
Ansible:
  • Ansible Tower.
CI CD:
AWS:
Wirtualizacja:
  • Vagrant.
Testowanie:
Inne:

środa, 4 sierpnia 2021

Konfiguracja i bezpieczeństwo SSH

 Generujemy klucz ECDSA, którego później będziemy używać do uwierzytelniania:

ssh-keygen -t ecdsa -b 521 -C "seprob"

Edytujemy plik "/etc/ssh/sshd_config":

# Nasłuchuj na danym porcie.

Port 5022

# Użytkownik "root" nie może się zalogowac.

PermitRootLogin no

# Zadwaj uzytkownikowi dowolna liczbe wielowatkowych pytan.

ChallengeResponseAuthentication no

# Wlacz interfejs Pluggable Authentication Module (wtedy nawet zablokowany uzytkownik moze sie zalogowac).

UsePAM no

X11Forwarding yes

# Wyswietlaj Message Of The Day.

PrintMotd yes

AcceptEnv LANG LC_*

Subsystem sftp /usr/lib/openssh/sftp-server

# Nie wysyłaj pakietu aby sprawdzić czy serwer żyje.

TCPKeepAlive no

# Wysyłaj zaszyfrowaną wiadomość co 30 sekund.

ClientAliveInterval 30

# Rozłącz niaktywnego użytkownika po 120 minutach (30 sekund * 240).

ClientAliveCountMax 240

# Dozwolone uwierzytelnianie za pomocą hasła.

PasswordAuthentication yes

# Nie zezwalaj na zalogowanie się na konta posiadające puste hasła.

PermitEmptyPasswords no 

Walidacja konfiguracji demona SSH może być wykonana za pomocą komendy "sshd -t" (lub "sshd -T" jako wersja rozszerzona). 

wtorek, 3 sierpnia 2021

Kubernetes RBAC

Dane wejściowe:

  • system: Debian 9,
  • użytkownik: root,
  • Kubernetes 1.21.3.
Na samym początku stwórzmy dedykowaną dla użytkownika przestrzeń nazw:

kind: Namespace
apiVersion: v1
metadata:
  name: seprob
  labels:
    name: seprob

Przejdźmy do "/etc/kubernetes/pki" gdzie zlokalizowane jest CA Kubernetesa.

Teraz musimy wygenerować klucz i certyfikat:
openssl genrsa -out seprob.key 2048

openssl req -new -key seprob.key -out seprob.csr -subj "/CN=seprob/O=yolandi"

openssl x509 -req -in seprob.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out seprob.crt -days 500

Certyfikat i klucz dostarczamy użytkownikowi aby skonfigurował sobie odpowiednio kubectl. Może to zrobić np. w poniższy sposób:

kubectl config set-credentials seprob --client-certificate=~/Documents/seprob_yolandi_kubernetes.crt --client-key=~/Documents/seprob_yolandi_kubernetes.crt

kubectl config set-context seprob-yolandi --cluster=yolandi --namespace=seprob --user=seprob

Dodatkowo musi ustawić w konfiguracji adres oraz CA klastra. Aktualnie jeżeli będziemy się próbowali połączyć to dostaniemy błąd.

Najpierw stwórzmy obiekt Role:

kind: Role

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  namespace: seprob

  name: seprob-role

rules:

- apiGroups: ["", "extensions", "apps"]

  resources: ["deployments", "replicasets", "pods"]

  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

Teraz obiekt RoleBinding:

kind: RoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

  name: seprob-rolebinding

  namespace: seprob

subjects:

- kind: User

  name: seprob

  apiGroup: ""

roleRef:

  kind: Role

  name: seprob-role

  apiGroup: ""

W tym momencie już powinniśmy mieć możliwość sprawdzić np. czy mamy jakieś Pody w przestrzeni nazw. 

poniedziałek, 2 sierpnia 2021

Instalacja Kubernetes

Dane wejściowe:

  • system operacyjny: Debian 9,
  • użytkownik: root.

Do instalacji klastra Kubernetes użyjemy oprogramowania kubeadm. Nim je zainstalujemy upewnijmy się, że system obsługuje w odpowiedni sposób ruch sieciowy.

Ładowanie modułu br_netfilter podczas ładowania systemu:

cat <<EOF | tee /etc/modules-load.d/k8s.conf

br_netfilter

EOF

Ustaw opcję "net.bridge.bridge-nf-call-iptables" na 1:

cat <<EOF | tee /etc/sysctl.d/k8s.conf

net.bridge.bridge-nf-call-ip6tables = 1

net.bridge.bridge-nf-call-iptables = 1

EOF

sysctl --system

Partycja wymiana musi być wyłączona. Najpierw sprawdźmy czy jest dostępna:

swapon --show

Jeżeli nic się nie pojawi to nie mamy partycji wymiany. W innym wypadku musimy ją wyłączyć:

swapoff -a 

Nasz serwer musi zezwalać na ruch wchodzący dla następujących portów po protokole TCP:

  • 6443, serwer API Kubernetesa,
  • 2379-2380, API klienta do serwera etcd,
  • 10250, kubelet API,
  • 10251, kube-scheduler,
  • 10252, kube-controller-manager.
Zainstaluj środowisko uruchomieniowe dla kontenerów spośród:
  • Docker,
  • containerd,
  • CRI-O.
Mu użyjemy containerd, które już mam zainstalowane na serwerze (przychodzi wraz z instalacją Dockera) więc teraz pora na konfigurację.

Potrzebne moduły:
cat <<EOF | tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

modprobe overlay

modprobe br_netfilter

Parametry sysctl:

cat <<EOF | tee /etc/sysctl.d/99-kubernetes-cri.conf

net.bridge.bridge-nf-call-iptables  = 1

net.ipv4.ip_forward                 = 1

net.bridge.bridge-nf-call-ip6tables = 1

EOF

sysctl --system 

I na koniec dla containerd:

mkdir -p /etc/containerd

containerd config default | tee /etc/containerd/config.toml

systemctl restart containerd

Dodatkowo musimy użyć sterownika systemd dla cgroup. W tym celu edytujemy plik "/etc/containerd/config.toml" i szukamy sekcji

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]

i dodajemy pod nią

SystemdCgroup = true

Na koniec restartujemy demona:

systemctl restart containerd

I wreszcie przyszła pora na instalację kubeadm (inicjalizacja klastra), kubelet (uruchamianie Podów, znajduje się na wszystkich maszynach klastra) oraz kubectl (komunikacja z K8s):

apt-get update

apt-get install -y apt-transport-https ca-certificates curl

curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list

apt-get update

apt-get install -y kubelet kubeadm kubectl

apt-mark hold kubelet kubeadm kubectl

Ostatnia komenda sprawi, że apt nie będzie aktualizował podanych pakietów.

Wykonajmy zatem wreszcie inicjalizację klastra:

kubeadm init --apiserver-advertise-address=92.222.79.22 --pod-network-cidr=10.244.0.0/16

Jako argumenty podajemy odpowiednio adres IP, do którego mają się podłączać nasze węzły w klastrze oraz CIDR sieci Kubernetes.

Uzyskajmy dostęp poprzez kubectl:

mkdir -p $HOME/.kube

cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

chown $(id -u):$(id -g) $HOME/.kube/config

Poleceniem "kubectl get nodes" możemy sprawdzić czy mamy połączenie z klastrem.

Przyszła pora na zainstalowanie wtyczki sieciowej. My wybierzemy Flannel:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Z powodów bezpieczeństwa nie można uruchamiać Podów na głównym węźle więc zmieńmy to:

kubectl taint nodes --all node-role.kubernetes.io/master-

Sprawdźmy czy działa ścieżką szczęścia:

kubectl run bootcamp --image=docker.io/jocatalin/kubernetes-bootcamp:v1 --port=8080

iptables

Zezwól na wszystkie przychodzące połączenia SSH (zmień na inny port jeżeli nie używasz domyślnego):

iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT

Wiele portów jednocześnie:

iptables -A INPUT -p tcp -m multiport --dports 80,443 -m state --state NEW,ESTABLISHED -j ACCEPT

Zezwól na dostęp połączeń DNS:

iptables -A INPUT -p udp --sport 53 -j ACCEPT

Domyślnie nie wpuszczaj żadnego pakietu:

iptables -P INPUT DROP

Zezwól na pobieranie pakietów za pośrednictwem apt:

iptables -A INPUT -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT

iptables -A INPUT -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT

Zachowywanie zmian na stałe

Zacznijmy od zainstalowania pakietu iptables-persistent (możemy zaakceptować zachowanie akturalnych reguł do "/etc/iptables/rules.v4" i "/etc/iptables/rules.v6"):

apt-get install -y iptables-persistent

Jeżeli chcemy zachować aktualne reguły to:

iptables-save > /etc/iptables/rules.v4

ip6tables-save > /etc/iptables/rules.v6