Skip to main content

Getting started

Instruction

Control plane vs worker nodes

Control Plane:

  • The control plane is installed on your master node
  • Can be both a control plane node and a worker node
  • It houses the API server, scheduler, and controller manager settings

Worker Nodes:

  • This is where the kubelet and kube-proxy are installed
  • You can use the kubeadm join command to join workers to the master node to form the cluster

First Test

New Pod

shell-demo.yaml

apiVersion: v1
kind: Pod
metadata:
  name: shell-demo
spec:
  volumes:
  - name: shared-data
    emptyDir: {}
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  hostNetwork: true
  dnsPolicy: Default

Create a Pod

kubectl apply -f https://k8s.io/examples/application/shell-demo.yaml

Verify that the container is running

kubectl get pod shell-demo

# Get a shell to the running container
kubectl exec -it shell-demo -- /bin/bash
New Deployment

nginx-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Create a Deployment

kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml

# Without a yaml file
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
EOF

Verify

kubectl get deployments
kubectl get pods --show-labels

Concepts

YAML File

myapp.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "100Mi"
      limits:
        memory: "200Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]
# Create a pod by applying a yaml file
kubectl apply -f myapp.yaml

# 建立一個部署用的初始 YAML 格式內容
kubectl create deploy nginx-deployment --image=nginx --dry-run=client -o yaml

Get the complete manifest of the deployed Kubernetes object.

kubectl get deploy nginx -n nginx-demo -o yaml
kubectl get svc <service-name> -n <name-space> -o yaml
kubectl get deployment <deployment-name> -n <name-space> -o yaml
Kubectl Configuration
kubectl version
kubectl cluster-info
kubectl config view
kubectl config view --minify --raw
Namespace & Node
  • Namespace 命名規則:名稱會用於內部的 subdomain,開頭與結尾必須字母數字,不可超過 253 個字元,只能包含小寫字母數字及 hyphen, dot 符號。 
# Create a namespace mem-example
kubectl create namespace mem-example
kubectl get ns

# Check the nodes
kubectl get nodes
kubectl describe node
Pod
# Create a pod memory-demo
kubectl apply -f myapp.yaml

# Check the pods
kubectl get pods
kubectl get all -A
kubectl get pod memory-demo --namespace=mem-example
kubectl get pod memory-demo --output=yaml --namespace=mem-example
kubectl top pod memory-demo --namespace=mem-example
kubectl describe pod memory-demo --namespace=mem-example
# Get a list of pods and the node they run on
kubectl get po -o=custom-columns=NODE:.spec.nodeName,NAME:.metadata.name
# Get the name of the containers of a running pod
kubectl get pod MYPOD -o 'jsonpath={.spec.containers[*].name}'

# Delete a pod
kubectl delete pod memory-demo --namespace=mem-example

# Run Shell in the Pod
kubectl exec -it <pod-name> -n <namespace> -- bash

# Attach the pod
kubectl attach <pod-name> -n <namespace> -i

# Force to delete a pod
kubectl delete pod <pod-name> --force --grace-period=0

# Copy a file from a pod to a local file
kubectl cp <namespace>/<pod>:<file_path> <local_file_path>
Deployment
kubectl get deployments
kubectl rollout status deployment/nginx-deployment
kubectl describe deployment <deployment-name>

# Update a new image
kubectl set image deployment/nginx-deployment nginx=nginx:sometag

# Scale a deployment
kubectl scale deployment deployment --replicas=X

Delete a deployment

NOTE: 刪除 deployment 後,底層的其他相關資源,例如 container, PersistentVolumes, image, kubernetes secrets 仍然存在,必須手動移除。

# Delete the specified deployment
kubectl delete deployment <deployment name> --namespace <namespace name>

# Delete all deployments for the specified namespace
kubectl delete deployment --all --namespace=<namespace name>

# Using yaml file to delete the deployment
kubectl delete -f blog-deployment-1.yaml -f blog-deployment-2.yaml

# Using label selector
kubectl get deployment -l app=my-app
kubectl get all --selector app=[app-label]

kubectl delete deployment -l app=my-app
kubectl delete all --selector app=[app-label]

# Force-delete a hung deployment
kubectl delete deployment <deployment-name> --grace-period=0 --force
kubectl delete replicaset -l app=<label>
kubectl delete pods -l app=<label>

Deployment with health-check

  • initialDelaySeconds: 

    設定當 service 剛啟動時,要延遲幾秒再開始做 health check

  • periodSeconds: 代表每隔幾秒訪問一次,預設值為 10秒
  • timeoutSeconds: 連線愈時
  • successThreshold: 可以設置訪問幾次就代表目前 service 還正常運行
  • failureThreshold: 連線失敗的次數
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-deployment
...
    spec:
      containers:
      - name: webapp
        image: zxcvbnius/docker-demo
        ports:
        - name: webapp-port
          containerPort: 3000
        livenessProbe:
          httpGet:
            path: /
            port: webapp-port
          initialDelaySeconds: 15
          periodSeconds: 15
          timeoutSeconds: 30  
          successThreshold: 1
          failureThreshold: 3

Secret
  • 變數的值以 yaml 格式輸出時,會以 base64 編碼顯示。
# Create a secret as environment variables
kubectl create secret generic [secret-name] \
  --from-literal=APP_API_KEY='your-api-key' \
  --from-literal=EXTERNAL_SERVICE_API_KEY='your-external-key' \
  -n [your-namespace]

Pull Docker Images from Private Docker Registries

# Create
# Usage: kubectl create secret docker-registry [secret-name] \
#  --docker-server:[address] \
#  --docker-username=[username] \
#  --docker-password=[password] \
#  -n [namespace]

# For Docker Hub Registry
kubectl create secret docker-registry dockerhub-pull-secret \
  --docker-server=docker.io \
  --docker-username=yourusername \
  --docker-password='Your-Personal-Key' \
  -n my-devops-prod

Check the secret

kubectl get secret [secret-name] -n [namespace] -o yaml

# get the value of a secret (if you have the base64 command available)
kubectl -n mynamespace get secret MYSECRET -o 'jsonpath={.data.DB_PASSWORD}' | base64 -d 

字串轉 base64

# String to Base64
echo -n 'ThisIsSecretStrings' | base64

# Base64 to String
echo -n 'VGhpc0lzU2VjcmV0U3RyaW5ncw==' | base64 -d

secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  db-password: cm9vdHBhc3M=

deployment.yaml

apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: mysql-server
...
    spec:
      containers:
      - name: mysql
        image: mysql:5.7
        args:
          - "--ignore-db-dir=lost+found"
        ports:
        - name: mysql-port
          containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: db-password
        volumeMounts:
...

PVC

PersistentVolumeClaim

kubectl get pvc -n <namespace>

kubectl delete pvc <pvc-name> -n <namespace>

掛載 node 端的本地磁碟

apiVersion: v1
kind: PersistentVolume
metadata:
  name: ollama-pv
  labels:
    type: local
spec:
  storageClassName: localvol-ollama
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/k8svol/ollama"

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ollama-pvc
  namespace: ollama
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
  storageClassName: localvol-ollama

Label

常用 Labels:

  • app: myapp
  • release: stable, release: qa
  • env: dev, env: prod
  • tier: backend, tier: frontend
  • depart: enginerring, depart: marketing
# 動態新增標籤
kubectl label pods <pod-name> <label-key>=<label-value>

# Show labels
kubectl get pods  --show-labels -n <namespace>

# Remove an existed label 'app=XXXX'
kubectl label pods <pod-name> app-

Annotations: 其他註解資訊

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: webserver
    tier: backend   
  annotations:
    version: latest
    release_date: 2017/12/28
    contact: zxcvbnius@gmail.com
spec:
  containers:
...
Service
  • nodePort range: 3000 ~ 32767
# Check service
kubectl get svc -n <name-space>

# Create a service to expose the port of a pod
kubectl expose pod <pod-name> --port=<pod-port> --name=<service-name> -n <name-space>

# Create s service to expose a random port of a pod/deploy
kubectl expose pod <pod-name> --type=NodePort --name=<service-name> -n <name-space>
kubectl export deploy <deploy-name> --type=NodePort --name=<service-name> -n <name-space>

# Forward a port from a service to a local port
kubectl port-forward svc/<service-name> <local-port>:<remote-port>
ConfigMap
  • 一個 ConfigMap 物件可以存入整個 configuration
    像是 webserver config file, Nginx config file
  • 無需修改 container 程式碼,可以替換不同環境的 Config
  • 統一存放所有的 configuration
  • ConfigMap v.s Secret: 我們可以將機密的資料存在 Secret 中,且 Secret 會將這些值經過 Base64 加密,機密的資料像是 API 或是 database 的密碼;而將非機密但屬於部署面的資料放在 ConfigMap,好比資料庫的 port number 或是 Redis 的 config file。
# Create from a file
# kubectl create configmap <configmap-name> --from-file=/path/to/file
kubectl create configmap redis-config --from-file=my-redis.conf
kubectl create configmap nginx-conf --from-file=./my-nginx.conf

kubectl get configmap
kubectl describe configmap <configmap-name>

# Create with a command
# Usgae: kubectl create configmap <configmap-name> --from-literal=<key>=<value>
kubectl create configmap mysql-host --from-literal=ip=127.0.0.1

Apply to pod

apiVersion: v1
kind: Pod
metadata:
  name: apiserver
  labels:
    app: webserver
    tier: backend
spec:
  containers:
  - name: nodejs-app
    image: zxcvbnius/docker-demo
    ports:
    - containerPort: 3000
  - name: nginx
    image: nginx:1.13
    ports:
    - containerPort: 80
    volumeMounts:
    - name: nginx-conf-volume
      mountPath: /etc/nginx/conf.d
  volumes:
  - name: nginx-conf-volume
    configMap:
      name: nginx-conf
      items:
      - key: my-nginx.conf
        path: my-nginx.conf
ResourceQuota

對特定 namespace 限制 CPU/RAM 使用 

apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota-cpu-mem
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

限制物件使用,例如 secrets, services

apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quotas
  namespace: hellospace
spec:
  hard:
    services: "2"
    services.loadbalancers: "1"
    secrets: "1"
    configmaps: "1"
    replicationcontrollers: "10"

Apply to the namespace

kubectl apply -f quota-cpu-mem.yaml -n <namespace>

Check the resourcequota

kubectl get resourcequotas -n <namespace>

kubectl describe resourcequotas <resourcequotas-name> -n <namespace>
Node
# List nodes
kubectl get nodes
kubectl get nodes,pods,deploy -o wide

# Node 維護
kubectl drain <node-name>
kubectl drain <node-name> --force

# Node 恢復
kubectl uncordon <node-name>

Monitor the log
kubectl logs -f deployment/<deployment-name> -n <name-space>
kubectl logs -f pod/<pod-name> -n <name-space>
環境變數
kubectl get deployment your-app-name -n your-namespace-prod -o yaml | grep EXTERNAL_SERVICE_URL

kubectl set env <resource>/<resource-name> --list

Networking

Inbound Rules for K3s Nodes
Protocol Port Source Destination Description
TCP 2379-2380 Servers Servers Required only for HA with embedded etcd
TCP 6443 Agents Servers K3s supervisor and Kubernetes API Server
UDP 8472 All nodes All nodes Required only for Flannel VXLAN
TCP 10250 All nodes All nodes Kubelet metrics
UDP 51820 All nodes All nodes Required only for Flannel Wireguard with IPv4
UDP 51821 All nodes All nodes Required only for Flannel Wireguard with IPv6
TCP 5001 All nodes All nodes Required only for embedded distributed registry (Spegel)
TCP 6443 All nodes All nodes Required only for embedded distributed registry (Spegel)

Typically, all outbound traffic is allowed.

Network access to other pods
  • Different Namespace: http://<service-name>.<namespace>:<port> 
  • Same Namespace: http://<service-name>:<port> 
Network access within the same pod
  • http://localhost:<port> 
  • 每個 container 有不一樣的 port