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.
Replication
Advanced Concepts
Master Node and Worker Node in Kubernetes
Overview
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
Kubetl
- CLI Tool to interact with K8s API Server
You can use kubectl to interact with Cloud K8s Nodes
Let’s install them
Install kubectl
If in our computer, Docker Desktop had been installed already, just enable K8s
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
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
When it’s ready
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
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
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
One Pod is creating while the other one is running, it will be removed soon
What about replicaset
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
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
- Metadata
- Specification
- Status
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
Group resources with namespace
Resolve conflicts: many teams, same application, different services
Resource Sharing: Blue/Green Deployment
Access Resource Limits on Namespace
You can limit:
- Access
- CPU
- RAM
- Storage
Resource Quota
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
Architecture
In cloud
In bare metal server
Practice
Steps
-
Install ingress controller
-
Setup ingress rules
minikube addons enable ingress
kubectl get pods -n ingress-nginx
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
If you have to create ssl
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
Helm Charts - Sharing, Reusing Configuration
- You may consider it is a package manager for k8s, so you can reuse/share your configuration.
Templating Engine
So instead of having multiple configuration files for similar services, you only need to define a template file and a variable file.
Very useful for CI/CD, the main usecase is deployment your application on different env
Helm Charts Structure
helm install [chart-name]
# template files will be filled with values from values.yaml
Helm v2
Tiller
But in v3, it had been removed!!!
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
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
NFS Storage
Local Storage
Persistent Volume Claim
- Abstraction of Persistent Volume
- Same namespace with Pod
- Easy to use for users(developers)
Flow
Example
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.
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
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
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?
Statefulset
But why you need to pod identity is not interchangable.
To scale your database, we will use master slave mechanism
So you have to specify which pod is master. So that pod must not be interchangable.
Also, you need to use remote storage, because local storage is usually tied to a specific node
Conclusion
K8s Service
ClusterIP Services
Service will select all pods that has the matched selector attribute
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
Headless Services
NodePort Services
For testing purpose
For external traffic, but it is secured!!!
LoadBalancer Services
For Production
Summary Configuration
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.