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 logLogs
kubectl logs -f deployment/<deployment-name> -n <name-space>
kubectl logs -f pod/<pod-name> -n <name-space>
kubectl logs -l app=xyz
環境變數
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