k8s筆記
購買服務器(一臺master,nodes2臺以上)

安裝集群
連接服務器,改主機名(所有節點)
vim /etc/hosts #所有節點都修改host
以下三個地址都為內網地址

改完每臺服務器相互ping,測試是否改名成功

關閉 SELinux(所有節點)
sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux
確保防火墻關閉(所有節點)
systemctl stop firewalld
systemctl disable firewalld
添加 k8s 安裝源(所有節點)
# 添加 k8s 安裝源
cat <<EOF > kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
mv kubernetes.repo /etc/yum.repos.d/
# 添加 Docker 安裝源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
#安裝所需組件 1.24 以上的版本會初始化報錯
yum install -y kubelet-1.22.4 kubectl-1.22.4 kubeadm-1.22.4 docker-ce
啟動 kubelet、docker,并設置開機啟動(所有節點)
systemctl enable kubelet
systemctl start kubelet
systemctl enable docker
systemctl start docker
修改 docker 配置(所有節點)
# kubernetes 官方推薦 docker 等使用 systemd 作為 cgroupdriver,否則 kubelet 啟動不了
cat <<EOF > daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": ["https://ud6340vz.mirror.aliyuncs.com"]
}
EOF
mv daemon.json /etc/docker/
# 重啟生效
systemctl daemon-reload
systemctl restart docker
用 kubeadm 初始化集群(僅在主節點跑)
# 初始化集群控制臺 Control plane
# 失敗了可以用 kubeadm reset 重置
kubeadm init --image-repository=registry.aliyuncs.com/google_containers
# 記得把 kubeadm join xxx 保存起來
# 忘記了重新獲取:kubeadm token create --print-join-command
# 復制授權文件,以便 kubectl 可以有權限訪問集群
# 如果你其他節點需要訪問集群,需要從主節點復制這個文件過去其他節點
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 在其他機器上創建 ~/.kube/config 文件也能通過 kubectl 訪問到集群
了解 kubeadm init 查看文檔
把工作節點加入集群(只在工作節點跑)
初始化成功 最后一行如下
kubeadm join xxxxxxxxxx --token xxx --discovery-token-ca-cert-hash xxx
安裝網絡插件,否則 node 是 NotReady 狀態(主節點跑)
# 很有可能國內網絡訪問不到這個資源,你可以網上找找國內的源安裝 flannel
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
跑完稍等,查看節點 kubectl get node
,要在主節點查看(其他節點有安裝 kubectl 也可以查看)

部署
命名空間
如果一個集群中部署了多個應用,所有應用都在一起,就不太好管理,也可以導致名字沖突等。
我們可以使用 namespace 把應用劃分到不同的命名空間,跟代碼里的 namespace 是一個概念,只是為了劃分空間。
# 創建命名空間
kubectl create namespace testapp
# 部署應用到指定的命名空間
kubectl apply -f app.yml --namespace testapp
# 查詢
kubectl get pod --namespace kube-system
可以用 kubens 快速切換 namespace
# 切換命名空間
kubens kube-system
# 回到上個命名空間
kubens -
# 切換集群
kubectx minikube

單個部署,管理不方便管理
kubectl run testapp --image=你的docker鏡像地址
pod
# vim app.yaml
#set paste
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
# 定義容器,可以多個
containers:
- name: test # 容器名字
image: xxxxxxxx# 鏡像
Deployment
# vim app.yaml
#set paste
apiVersion: apps/v1
kind: Deployment
metadata:
# 部署名字
name: test
spec:
replicas: 2
# 用來查找關聯的 Pod,所有標簽都匹配才行
selector:
matchLabels:
app: test
# 定義 Pod 相關數據
template:
metadata:
labels:
app: test
spec:
# 定義容器,可以多個
containers:
- name: test # 容器名字
image: xxxxxxxxxxxxx # 鏡像
Deployment 通過 label 關聯起來 Pods

部署應用演示
部署一個 nodejs web 應用,源碼地址:Github
# 部署應用
kubectl apply -f app.yaml
# 查看 deployment
kubectl get deployment
# 查看 pod
kubectl get pod -o wide
# 查看 pod 詳情
kubectl describe pod pod-name
# 查看 log
kubectl logs pod-name
# 進入 Pod 容器終端, -c container-name 可以指定進入哪個容器。
kubectl exec -it pod-name -- bash
# 伸縮擴展副本
kubectl scale deployment test-k8s --replicas=5
# 把集群內端口映射到節點
kubectl port-forward pod-name 8090:8080
# 查看歷史
kubectl rollout history deployment test-k8s
# 回到上個版本
kubectl rollout undo deployment test-k8s
# 回到指定版本
kubectl rollout undo deployment test-k8s --to-revision=2
# 刪除部署
kubectl delete deployment test-k8s
Pod 報錯解決
如果你運行 kubectl describe pod/pod-name 發現 Events 中有下面這個錯誤
networkPlugin cni failed to set up pod "test-k8s-68bb74d654-mc6b9_default" network: open /run/flannel/subnet.env: no such file or directory
在每個節點創建文件/run/flannel/subnet.env寫入以下內容,配置后等待一會就好了
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
更多命令
# 查看全部
kubectl get all
# 重新部署
kubectl rollout restart deployment test-k8s
# 命令修改鏡像,--record 表示把這個命令記錄到操作歷史中
kubectl set image deployment test-k8s test-k8s=ccr.ccs.tencentyun.com/k8s-tutorial/test-k8s:v2-with-error --record
# 暫停運行,暫停后,對 deployment 的修改不會立刻生效,恢復后才應用設置
kubectl rollout pause deployment test-k8s
# 恢復
kubectl rollout resume deployment test-k8s
# 輸出到文件
kubectl get deployment test-k8s -o yaml >> app2.yaml
# 刪除全部資源
kubectl delete all --all
更多官網關于 Deployment 的介紹
將 Pod 指定到某個節點運行:nodeselector
限定 CPU、內存總量:文檔
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
工作負載分類
- Deployment
適合無狀態應用,所有pod等價,可替代 - StatefulSet
有狀態的應用,適合數據庫這種類型。 - DaemonSet
在每個節點上跑一個 Pod,可以用來做節點監控、節點日志收集等 - Job & CronJob
Job 用來表達的是一次性的任務,而 CronJob 會根據其時間規劃反復運行。
現存問題
- 每次只能訪問一個 pod,沒有負載均衡自動轉發到不同 pod
- 訪問還需要端口轉發
- Pod 重創后 IP 變了,名字也變了
Service
特性
- Service 通過 label 關聯對應的 Pod
- Servcie 生命周期不跟 Pod 綁定,不會因為 Pod 重創改變 IP
- 提供了負載均衡功能,自動轉發流量到不同 Pod
- 可對集群外部提供訪問端口
- 集群內部可通過服務名字訪問

創建 Service
創建 一個 Service,通過標簽test-k8s跟對應的 Pod 關聯上
service.yaml
apiVersion: v1
kind: Service
metadata:
name: test-k8s
spec:
selector:
app: test-k8s
type: ClusterIP
ports:
- port: 8080 # 本 Service 的端口
targetPort: 8080 # 容器端口
應用配置 kubectl apply -f service.yaml
查看服務 kubectl get svc

查看服務詳情 kubectl describe svc test-k8s,可以發現 Endpoints 是各個 Pod 的 IP,也就是他會把流量轉發到這些節點。

服務的默認類型是ClusterIP,只能在集群內部訪問,我們可以進入到 Pod 里面訪問:
kubectl exec -it pod-name -- bash
curl http://test-k8s:8080
如果要在集群外部訪問,可以通過端口轉發實現(只適合臨時測試用):
kubectl port-forward service/test-k8s 8888:8080
如果你用 minikube,也可以這樣
minikube service test-k8s
對外暴露服務
上面我們是通過端口轉發的方式可以在外面訪問到集群里的服務,如果想要直接把集群服務暴露出來,我們可以使用NodePort 和 Loadbalancer 類型的 Service
apiVersion: v1
kind: Service
metadata:
name: test-k8s
spec:
selector:
app: test-k8s
# 默認 ClusterIP 集群內可訪問,NodePort 節點可訪問,LoadBalancer 負載均衡模式(需要負載均衡器才可用)
type: NodePort
ports:
- port: 8080 # 本 Service 的端口
targetPort: 8080 # 容器端口
nodePort: 31000 # 節點端口,范圍固定 30000 ~ 32767
應用配置 kubectl apply -f service.yaml
在節點上,我們可以 curl http://localhost:31000/hello/k8s`訪問到應用
并且是有負載均衡的,網頁的信息可以看到被轉發到了不同的 Pod
hello k8s
IP lo172.17.0.8, hostname: test-k8s-68bb74d654-962lh
如果你是用 minikube,因為是模擬集群,你的電腦并不是節點,節點是 minikube 模擬出來的,所以你并不能直接在電腦上訪問到服務
Loadbalancer 也可以對外提供服務,這需要一個負載均衡器的支持,因為它需要生成一個新的 IP 對外服務,否則狀態就一直是 pendding,這個很少用了,后面我們會講更高端的 Ingress 來代替它。
多端口
apiVersion: v1
kind: Service
metadata:
name: test-k8s
spec:
selector:
app: test-k8s
type: NodePort
ports:
- port: 8080 # 本 Service 的端口
name: test-k8s # 必須配置
targetPort: 8080 # 容器端口
nodePort: 31000 # 節點端口,范圍固定 30000 ~ 32767
- port: 8090
name: test-other
targetPort: 8090
nodePort: 32000
總結y
ClusterIP
默認的,僅在集群內可用
NodePort
暴露端口到節點,提供了集群外部訪問的入口
端口范圍固定 30000 ~ 32767
LoadBalancer
需要負載均衡器(通常都需要云服務商提供,裸機可以安裝 METALLB 測試)
會額外生成一個 IP 對外服務
K8S 支持的負載均衡器:負載均衡器
Headless
適合數據庫
clusterIp 設置為 None 就變成 Headless 了,不會再分配 IP
數據持久化
介紹
kubernetes 集群不會為你處理數據的存儲,我們可以為數據庫掛載一個磁盤來確保數據的安全。
你可以選擇云存儲、本地磁盤、NFS。
- 本地磁盤:可以掛載某個節點上的目錄,但是這需要限定 pod 在這個節點上運行
- 云存儲:不限定節點,不受集群影響,安全穩定;需要云服務商提供,裸機集群是沒有的。
- NFS:不限定節點,不受集群影響
hostPath 掛載示例
把節點上的一個目錄掛載到 Pod,但是已經不推薦使用了,文檔
配置方式簡單,需要手動指定 Pod 跑在某個固定的節點。
僅供單節點測試使用;不適用于多節點集群。
minikube 提供了 hostPath 存儲,文檔
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
serviceName: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongo
image: mongo:4.4
# IfNotPresent 僅本地沒有鏡像時才遠程拉,Always 永遠都是從遠程拉,Never 永遠只用本地鏡像,本地沒有則報錯
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /data/db # 容器里面的掛載路徑
name: mongo-data # 卷名字,必須跟下面定義的名字一致
volumes:
- name: mongo-data # 卷名字
hostPath:
path: /data/mongo-data # 節點上的路徑
type: DirectoryOrCreate # 指向一個目錄,不存在時自動創建
更高級的抽象

Storage Class (SC)
將存儲卷劃分為不同的種類,例如:SSD,普通磁盤,本地磁盤,按需使用。文檔
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
type: io1
iopsPerGB: "10"
fsType: ext4
Persistent Volume (PV)
描述卷的具體信息,例如磁盤大小,訪問模式。文檔,類型,Local 示例
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodata
spec:
capacity:
storage: 2Gi
volumeMode: Filesystem # Filesystem(文件系統) Block(塊)
accessModes:
- ReadWriteOnce # 卷可以被一個節點以讀寫方式掛載
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /root/data
nodeAffinity:
required:
# 通過 hostname 限定在某個節點創建存儲卷
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node2
Persistent Volume Claim (PVC)
對存儲需求的一個申明,可以理解為一個申請單,系統根據這個申請單去找一個合適的 PV
還可以根據 PVC 自動創建 PV。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodata
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-storage"
resources:
requests:
storage: 2Gi
為什么要這么多層抽象
- 更好的分工,運維人員負責提供好存儲,開發人員不需要關注磁盤細節,只需要寫一個申請單。
- 方便云服務商提供不同類型的,配置細節不需要開發者關注,只需要一個申請單。
- 動態創建,開發人員寫好申請單后,供應商可以根據需求自動創建所需存儲卷。
騰訊云示例

本地磁盤示例
不支持動態創建,需要提前創建好
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
image: mongo:5.0
imagePullPolicy: IfNotPresent
name: mongo
volumeMounts:
- mountPath: /data/db
name: mongo-data
volumes:
- name: mongo-data
persistentVolumeClaim:
claimName: mongodata
---
apiVersion: v1
kind: Service
metadata:
name: mongodb
spec:
clusterIP: None
ports:
- port: 27017
protocol: TCP
targetPort: 27017
selector:
app: mongodb
type: ClusterIP
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodata
spec:
capacity:
storage: 2Gi
volumeMode: Filesystem # Filesystem(文件系統) Block(塊)
accessModes:
- ReadWriteOnce # 卷可以被一個節點以讀寫方式掛載
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /root/data
nodeAffinity:
required:
# 通過 hostname 限定在某個節點創建存儲卷
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node2
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodata
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-storage"
resources:
requests:
storage: 2Gi
ConfigMap 、 Secret
ConfigMap
數據庫連接地址,這種可能根據部署環境變化的,我們不應該寫死在代碼里。
Kubernetes 為我們提供了 ConfigMap,可以方便的配置一些變量。文檔
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mongo-config
data:
mongoHost: mongodb-0.mongodb
# 應用
kubectl apply -f configmap.yaml
# 查看
kubectl get configmap mongo-config -o yaml

Secret
一些重要數據,例如密碼、TOKEN,我們可以放到 secret 中。文檔,配置證書
注意,數據要進行 Base64 編碼。Base64 工具
secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mongo-secret
# Opaque 用戶定義的任意數據,更多類型介紹 https://kubernetes.io/zh/docs/concepts/configuration/secret/#secret-types
type: Opaque
data:
# 數據要 base64。https://tools.fun/base64.html
mongo-username: bW9uZ291c2Vy
mongo-password: bW9uZ29wYXNz
# 應用
kubectl apply -f secret.yaml
# 查看
kubectl get secret mongo-secret -o yaml

使用方法
作為環境變量使用
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
replicas: 3
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongo
image: mongo:4.4
# IfNotPresent 僅本地沒有鏡像時才遠程拉,Always 永遠都是從遠程拉,Never 永遠只用本地鏡像,本地沒有則報錯
imagePullPolicy: IfNotPresent
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongo-secret
key: mongo-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongo-secret
key: mongo-password
# Secret 的所有數據定義為容器的環境變量,Secret 中的鍵名稱為 Pod 中的環境變量名稱
# envFrom:
# - secretRef:
# name: mongo-secret
掛載為文件(更適合證書文件)
掛載后,會在容器中對應路徑生成文件,一個 key 一個文件,內容就是 value,文檔
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
k8s軟件庫-Helm
介紹
Helm類似 npm,pip,docker hub, 可以理解為是一個軟件庫,可以方便快速的為我們的集群安裝一些第三方軟件。
使用 Helm 我們可以非常方便的就搭建出來 MongoDB / MySQL 副本集群,YAML 文件別人都給我們寫好了,直接使用。官網,應用中心
安裝 Helm
安裝 文檔
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
安裝 MongoDB 示例
# 安裝
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-mongo bitnami/mongodb
# 指定密碼和架構
helm install my-mongo bitnami/mongodb --set architecture="replicaset",auth.rootPassword="mongopass"
# 刪除
helm ls
heml delete my-mongo
# 查看密碼
kubectl get secret my-mongo-mongodb -o json
kubectl get secret my-mongo-mongodb -o yaml > secret.yaml
# 臨時運行一個包含 mongo client 的 debian 系統
kubectl run mongodb-client --rm --tty -i --restart='Never' --image docker.io/bitnami/mongodb:4.4.10-debian-10-r20 --command -- bash
# 進去 mongodb
mongo --host "my-mongo-mongodb" -u root -p mongopass
# 也可以轉發集群里的端口到宿主機訪問 mongodb
kubectl port-forward svc/my-mongo-mongodb 27017:27018
CLB
介紹
Ingress 為外部訪問集群提供了一個 統一 入口,避免了對外暴露集群端口;
功能類似 Nginx,可以根據域名、路徑把請求轉發到不同的 Service。
可以配置 https
跟 LoadBalancer 有什么區別?
LoadBalancer 需要對外暴露端口,不安全;
無法根據域名、路徑轉發流量到不同 Service,多個 Service 則需要開多個 LoadBalancer;
功能單一,無法配置 https

使用
要使用 Ingress,需要一個負載均衡器 + Ingress Controller
如果是裸機(bare metal) 搭建的集群,你需要自己安裝一個負載均衡插件,可以安裝 METALLB
如果是云服務商,會自動給你配置,否則你的外部 IP 會是 “pending” 狀態,無法使用。
文檔:Ingress
Minikube 中部署 Ingress Controller:nginx
Helm 安裝: Nginx
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-example
spec:
ingressClassName: nginx
rules:
- host: tools.fun
http:
paths:
- path: /easydoc
pathType: Prefix
backend:
service:
name: service1
port:
number: 4200
- path: /svnbucket
pathType: Prefix
backend:
service:
name: service2
port:
number: 8080
騰訊云配置 Ingress 演示

擴展
kubernetes 可以管理大量的容器化應用,方便的進行伸縮擴展集群,隨時回退版本。
kubernetes 需要云廠商的支持才是完整的,好在當前各大云廠商都已經提供了 k8s 集群服務,生態很完善,非常方便。
我們自己搭建的叫裸機,用來做測試、學習很好,可以把自己淘汰的電腦用起來搭建出一個集群玩玩。
WEB 可視化管理集群
如果你覺得命令行管理集群太麻煩,你可以用 Helm 快速搭建一個 kubernetes-dashboard,這樣你就有了一個 WEB 界面,可以可視化的進行一些操作和管理。
如果是 minikube 更加簡單,一個命令minikube dashboard就好了。
數據庫更好的做法
數據庫這種有狀態的應用,更好的做法是直接使用云廠商提供的數據庫,運行會更加穩定,也有完善的數據備份。
用腳本搭建集群
Github 上有用戶已經把裸機搭建需要做的工作寫成了腳本,一個腳本就幫你初始化好集群工作:kainstall
公網搭建 K8S 集群









網友提供的:參考文檔

浙公網安備 33010602011771號