Kubernetes Complete Course

Kubernetes Complete Course

Post Date : 2024-04-01T09:09:35+07:00

Modified Date : 2024-04-01T09:09:35+07:00

Category: devops

Tags: k8s , kubenertes

Overview

🔥 What is Kubernetes 🔥 ► What problems does Kubernetes solve? ► What features do container orchestration tools offer?

What Kubenertes is?

  • Open source Container Orchestration Tool
  • Developed by Google
  • Helps you mange containerized applications in different deployment environments: physical, virtual machine, cloud, hybrid

What problem does Kubenertes solve?

1st problem: The need for a container orchestration tool

  • Trend from Monolith to Microservices
  • Increased usage of containers
  • Demand for a proper way of managing those hundreds of containers

What features do container orchestration tools offer?

  • High Availability or no downtime
  • Scalability or high performance
  • Disater recovery - backup and restore

Introduction to K8s

Kubenertes Components

Pod

  • Smallest unit of K8s
  • Abstraction over container
  • Usually 1 application per Pod
  • You can run multiple applications per Pod ( bad practice)
  • Each pod gets its own IP Address
  • New IP Address on re-recreation

Eg: your DB container died, then k8s auto re-create and replace the dead one but it use another IP Address.

image

Replication

image

Advanced Concepts

Master Node and Worker Node in Kubernetes

Overview

image

Worker Node

There are 3 processes in Worker Node

  • Container Runtime ( can be docker )
  • KubeProxy : Kind of virtual network
  • Kubelet: connect node and container

Master Node

There are 4 processes in Master Node

  • Api: cluster’s gateway, gate keeper for authentication
  • Scheduler: add/remove pods
  • Controller Manager: detects cluster state changes
  • ETCD: Cluster Brain, Key-Value Store

Let’s dive into practical excercises

Minikube

  • A K8s Node in your local

image

Kubetl

  • CLI Tool to interact with K8s API Server

image

You can use kubectl to interact with Cloud K8s Nodes

image

Let’s install them

Install kubectl

If in our computer, Docker Desktop had been installed already, just enable K8s

image

If not, please follow k8s official doc to install on your appropriate os.

In this topic, i used MACOS, so here is the details

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
sudo chown root: /usr/local/bin/kubectl
kubectl version --client

When it is ready, you can test with this command line

kubectl cluster-info

image

Install minikube

The sample will be in MACOS

brew install minikube
# remove if you don't need it anymore
brew unlink minikube
brew link minikube

This sample is for window

winget install minikube
minikube start

image

When it’s ready

image

Let’s run some check commands

kubectl get nodes
# and you get
minhson@NBVN-KIENTC blog.phpguru.net % kubectl get nodes
NAME       STATUS   ROLES           AGE    VERSION
minikube   Ready    control-plane   3m4s   v1.28.3
minikube status
# and
minhson@NBVN-KIENTC blog.phpguru.net % minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

And with docker desktop, you may see this

image

Kubectl Basic Commands

kubectl get nodes
kubectl get pod
kubectl get services

You don’t create Pod directly, always create a deployment - abstractions over Pods

kubctl create deployment --help
# and you will see this instruction
kubectl create deployment NAME --image=image -- [COMMAND] [args...] [options]

Let’s practice with creating a nginx deployment

kubectl create deployment k8s-nginx --image=nginx
kubectl get deployment
# and
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
k8s-nginx   1/1     1            1           51s
# then
kubectl get replicaset
# and
NAME                   DESIRED   CURRENT   READY   AGE
k8s-nginx-6fcb9b44f8   1         1         1       89s
# then
kubectl get pod
# and
NAME                         READY   STATUS    RESTARTS   AGE
k8s-nginx-6fcb9b44f8-85rbz   1/1     Running   0          29s

Everything under deployment will be created automatically by k8s

image

Let’s change nginx image version

kubectl edit deployment k8s-nginx
# then edit the image from nginx to nginx:stable-alpine3.17
# just edit like vim editor, press i to edit and save with :wq!

And you will see this

image

One Pod is creating while the other one is running, it will be removed soon

What about replicaset

image

kubectl logs k8s-nginx-f57b78788-4m59z # not found
kubectl logs k8s-nginx-6fcb9b44f8-766bk # you will see the log output

How about delete

kubectl delete deployment k8s-nginx

Let’s create configuration file for K8s

nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx-main
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-main
  template:
    metadata:
      labels:
        app: nginx-main
    spec:
      containers:
        - name: nginx-main
          image: nginx:stable-alpine3.17
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "256Mi"
              cpu: "50m"
            limits:
              memory: "512Mi"
              cpu: "200m"
kubectl apply -f nginx-deployment.yaml
kubectl get pod
kubectl get replicaset
kubectl get deployment

With this configuration file, you only need to update this file, and run apply command, k8s will take care the rest for you.

Connecting Services to Deployments

image image

nginx-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx-main
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080 # should match with container port
      # nodePort: 30007 # Uncomment if you want to specify the NodePort; otherwise, it's dynamically allocated.
kubectl apply -f nginx-service.yaml
# check status
kubectl describe service nginx-service
kubectl get pod -o wide
kubectl get deployment nginx-deployment -o yaml > nginx-deployment-result.yaml

K8s command cheatsheet

Deployment CRUD Commands

kubeclt create deployment [name]
kubeclt edit deployment [name]
kubeclt delte deployment [name]

Status of different K8s components

kubectl get nodes | pod | services | replicaset | deployment

Debuging pods

kubectl logs pod-name
kubectl exec -it [podname] -- bin/sh

Use Configuration File

kubectl apply -f [filename] # create/update
kubectl delete -f [filename] # delete

Reading K8s configuration file

  1. Metadata
  2. Specification
  3. Status

image

Secret

  • Create secret with command line
kubectl create secret generic mysql-root-password-secret --from-literal=mysql_root_password='yourpassword'

# from text file
kubectl create secret generic mysql-root-password-secret --from-file=mysql_root_password=mysql_root_password.txt
# verify
kubectl get secret mysql-root-password-secret
  • If you wanna define secret in your config file, the value must be base64
# sample
echo -n '123456' | base64
apiVersion: v1
kind: Secret
metadata:
  name: mysql-root-password-secret
  namespace: my-namespace
type: Opaque
data:
  mysql_root_password: MTIzNDU2

Usage

env:
  - name: MYSQL_ROOT_PASSWORD
    valueFrom:
      secretKeyRef:
        name: mysql-root-password-secret
        key: mysql_root_password
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deployment
  labels:
    app: mysql-main
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql-main
  template:
    metadata:
      labels:
        app: mysql-main
    spec:
      containers:
        - name: mysql-main
          image: mysql:8.3.0
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-root-password-secret
                  key: mysql_root_password
          resources:
            requests:
              memory: "1024Mi"
              cpu: "1000m"
            limits:
              memory: "1024Mi"
              cpu: "1000m"

Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-deployment
  labels:
    app: mysql-main
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql-main
  template:
    metadata:
      labels:
        app: mysql-main
    spec:
      containers:
        - name: mysql-main
          image: mysql:8.3.0
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-root-password-secret
                  key: mysql_root_password
          resources:
            requests:
              memory: "1024Mi"
              cpu: "1000m"
            limits:
              memory: "1024Mi"
              cpu: "1000m"
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  selector:
    app: mysql-main
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306

You can define your service with seperated config file, but k8s support us to define it in the same file with your deployment config. Service is something like a proxy between your pod with other service in your cluster

Config Map

How do we connect two services? Eg: your phpmyadmin-service with your mysql-service. Don’t worry ConfigMap live for this reason

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-configmap
data:
  mysql-database-url: mysql-service

Usage, same as secret, but instead of getting value from secret, just get them from configmap

env:
  - name: PMA_HOST
    valueFrom:
      configMapKeyRef:
        name: mysql-configmap
        key: mysql-database-url

mysql-database-url <==> mysql-service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: phpmyadmin
spec:
  selector:
    matchLabels:
      app: phpmyadmin
  template:
    metadata:
      labels:
        app: phpmyadmin
    spec:
      containers:
        - name: phpmyadmin
          image: phpmyadmin:latest
          resources:
            limits:
              memory: "512Mi"
              cpu: "500m"
          ports:
            - containerPort: 80
          env:
            - name: PMA_HOST
              valueFrom:
                configMapKeyRef:
                  name: mysql-configmap
                  key: mysql-database-url
            - name: PMA_USER
              value: root
            - name: PMA_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-root-password-secret
                  key: mysql_root_password
---
apiVersion: v1
kind: Service
metadata:
  name: phpmyadmin-service
spec:
  selector:
    app: phpmyadmin
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      # must be between 30,000-32,767
      nodePort: 30000

Namespaces

Default namespace

image

Group resources with namespace

image

Resolve conflicts: many teams, same application, different services

image

Resource Sharing: Blue/Green Deployment

image

Access Resource Limits on Namespace

You can limit:

  • Access
  • CPU
  • RAM
  • Storage

Resource Quota

image

What you can’t access between namespace

  • Config Map
  • Secret

What you can access between namespace

  • Service: {service}.{namespace}

Some components which can’t be created in a namespace

  • Volume
  • Node
kubectl api-resources --namespaced=false

Practice

kubectl create namespace my-namespace
kubectl get configmap -n=default
kubectl get configmap -n=my-namespace
kubectl apply -f mysql-configmap.yaml -n=my-namespace
kubectl get configmap -n=my-namespace
kubectl get all -n=my-namespace
kubectl delete -f mysql-configmap.yaml -n=my-namespace
kubectl delete namespace my-namespace

You can create a component inside a namespace with this syntax, but it is not covenient, and you can’t keep track on it.

kubectl apply -f mysql-configmap.yaml -n=my-namespace

So it’s better to determine the namespace inside config file like this.

apiVersion: v1
kind: Secret
metadata:
  name: mysql-root-password-secret
  namespace: my-namespace
type: Opaque
data:
  mysql_root_password: MTIzNDU2

Everytime your wanna get information about your components, you must specify your namespace.

kubectl get configmap -n=my-namespace

There is another tool that support you to change your active namespace called “kubectx

On MAC

brew install kubectx

And then you will be provided this command “kubens

# list all namespace
kubens
# change active namespace
kubens your-target-namespace

Real world service deployment with Ingress

image

image

image

Architecture

image

In cloud

image

In bare metal server

image

Practice

Steps

minikube addons enable ingress

image

kubectl get pods -n ingress-nginx

image

Let’s start with assign a domain to dashboard addons

Dashboard

minikube addons list | grep dashboard
# | dashboard                   | minikube | disabled     | Kubernetes                     |
minikube addons enable dashboard
kubectl get ns
kubectl get all -n kubernetes-dashboard

Dashboard Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: k8s-dashboard-ingress
  namespace: kubernetes-dashboard
  labels:
    name: k8s-dashboard-ingress
spec:
  rules:
    - host: dashboard.k8s-phpguru.local
      http:
        paths:
          - pathType: Prefix
            path: "/"
            backend:
              service:
                name: kubernetes-dashboard
                port:
                  number: 80
kubectl apply -f dashboard-ingress.yaml
kubectl get ingress -n kubernetes-dashboard --watch
# then use the ipaddress, assign it with your dns to resolve the domain
# in local we'll modify /etc/hosts
sudo nano /etc/hosts
# then tunnel to keep accessible
minikube tunnel
# then open http://dashboard.k8s-phpguru.local in your browser
## k8s tutorial: because of using Docker Driver -> we must use this local ip address
127.0.0.1 dashboard.k8s-phpguru.local

And you will see this

image

If you have to create ssl

image

Quick test ingress and ingress-dns addons

kubectl create deployment web --image=gcr.io/google-samples/hello-app:1.0
kubectl expose deployment web --port=8080
kubectl apply -f example-ingress.yaml

Helm

image image

Helm Charts - Sharing, Reusing Configuration

  • You may consider it is a package manager for k8s, so you can reuse/share your configuration.

image image

Templating Engine

So instead of having multiple configuration files for similar services, you only need to define a template file and a variable file.

image image

Very useful for CI/CD, the main usecase is deployment your application on different env

image

Helm Charts Structure

image

helm install [chart-name]
# template files will be filled with values from values.yaml

Helm v2

Tiller

image image

But in v3, it had been removed!!!

image

K8s Volumes

Due to the needs of data persistent, k8s provide several components to persist your data

Storage requirements

  • Doesn’t depend on the pod lifecycle
  • Must be available on all nodes
  • Must survive even if cluster crashes

image

Persistent Volume

  • The primary component in k8s system to persist your data
  • Cluster Resource
  • It must be backed by physical storage in local(inside cluster) or in cloud service(network file system).
  • Usually created by admin
  • For Database must use cloud

image

NFS Storage

image

Local Storage

image

image

Persistent Volume Claim

image

  • Abstraction of Persistent Volume
  • Same namespace with Pod
  • Easy to use for users(developers)

image

Flow

image

Example

image

Storage Class

Issue: Imagination, you have a big system, a lot of developers claim for a volume with PVC, so as admin you have to create enough PV to fullfil the needs, it can be extremely time consuming and will get messy very fast.

image

Fortunately, k8s provide us a solution for it, named “Storage Class”. What does it do?

  • Provisions persistent volumes dynamically when PVC claim it
  • It must be backed by a provisioner: internal(k8s)/external(another services)

Example

image

K8s StatefulSet

Stateless application

A stateless process or application, however, does not retain information about the user’s previous interactions. There is no stored knowledge of or reference to past transactions. Each transaction is made as if from scratch for the first time

image

image

Stateful Application

Stateful applications save data to persistent disk storage for use by the server, by clients, and by other applications. An example of a stateful application is a database or key-value store to which data is saved and retrieved by other applications.

What deployment does?

image

Statefulset

image

But why you need to pod identity is not interchangable.

To scale your database, we will use master slave mechanism

image

image

image

So you have to specify which pod is master. So that pod must not be interchangable.

image

Also, you need to use remote storage, because local storage is usually tied to a specific node

image

Conclusion

image

image image image image

K8s Service

image

image

ClusterIP Services

image

Service will select all pods that has the matched selector attribute

image image

Note: the service port can be arbitrary, but the targerPort must match the port that the container is listening at.

If you have multiple ports in your services, you must name it

image

Headless Services

image

image

image

NodePort Services

For testing purpose

image

For external traffic, but it is secured!!!

image

LoadBalancer Services

For Production

image image

Summary Configuration

image

Terminology

Ports

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  type: NodePort
  selector:
    app: example-app
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30000
  • Port (80): Other Pods within the cluster can reach the Service at example-service..svc.cluster.local:80.
  • TargetPort (8080): The Service routes this internal traffic to port 8080 on the selected Pods.
  • NodePort (30000): External traffic can access the Service by targeting http://:30000, which routes to port 8080 on the Pods.