Kubernetes 新一代的雲端作業系統
幫企業 run Application ( 應用系統 ) run 的長長久久、穩穩當當
Containerisation (貨櫃化) has brought a lot of flexibility for developers in terms of managing the deployment of the applications. However, the more granular the application is, the more components it consists of and hence requires some sort of management for those.
One still needs to take care of scheduling the deployment of a certain number of containers to a specific node, managing networking between the containers, following the resource allocation, moving them around as they grow and much more.
Nearly all applications nowadays need to have answers for things like
Google has given a combined solution for that which is Kubernetes, or how it’s shortly called – K8s.
甚麼時候會需要用到隨需擴充?
這張圖中,有3台虛擬電腦
API Server
、Secheduler
、Controller-Manager
和 etcd
API Server
kubectl
、kubeadm
…等API Server
下達命令,例如 : Rancher
Rancher 這家公司還有推出 K3s ( 無肥肉、身材好、精實型的 K8s )
Secheduler
kubelet
Controller-Manager
Scheduler
幫我們把 pod 建立回來,但是不一定會在原來的 node 把 pod 建立回來。 ( Kubernetes 內建的容錯功能 )Sechedular
, 幫忙把 pod 複製出來,也就是隨需擴增。etcd
kubelet
Kube-proxy
( 哭 ㄅ ㄆㄨㄚ seeˇ )
kube-proxy
會透過 Flannel 網路套件,來讓 Pods 跨主機互通有無m1 只要執行
kubelet
,其實也可以扮演 worker 的角色
我們 ( 前端管理者 ) 打 kubectl
這個命令告訴 API Server
,我有一個 pod 要 run, API Server
接收到後,會去找 Scheduler
,請他安排,Secheduler
會去找看哪一台 node 最閒,然後會去通知那台 node 裡面的 kubelet
,叫他去請 Container-Runtime-Engine 幫我們產生 Container 。
K8s 的 pod 在外面工作,我們稱它為 Solution ( 解決方案 ) ,注意 ! 它不能稱為一台電腦,因為 pod 是由一台或多台 Container 組成, Container 是一台電腦,pod 就是很多台電腦堆疊起來的一整個機櫃,舉例 : 應用系統由一個或多個 process 組成,而 pod 就是一個企業所需的應用系統,pod 裡面的 Container 就是 process 和 process 的相依檔。
Dockershim
來跟 Docker Engine
來溝通,建立 Container ,透過 Docker Engine
來做到內部網路,所以速度慢,效率差。Docker
給砍掉了,並且 K8S
自己寫了 CRI-Cotainerd
來跟 Containerd
做溝通,不依賴 Docker Engine
Containerd
討好 K8S ,直接提供 CRI plugin
( Container Runtime Interface, 內建一個介面,根據 CRI 標準寫的程式 ) 給 K8S,讓 K8S 可以直接使用 Containerd
,但因為 Containerd
本身還有給 Docker
平台使用,所以制定 image 的標準也有 Docker
的,跟下面最新的架構比,效率還是差了點。kubelet
的話,效率最優。
Docker
就可以建立和跑 Container 了Docker
並沒有遵循 CRI 的標準意思是有人已經幫你設好了 Kubernetes
quay.io
的 image 管理中心,供使用者 Pull
、Push image
,還會有 CI/CD
,讓程式設計師有健身房可以用,達到敏捷開發。k8s 的 scale out ,自動擴充出分身的 pod ,硬體設備要夠強,不然會有災難。
bypass
跳過 overlay2 檔案系統, 使用 K8S 的 volume 來做儲存,這個 volume 會透過網路連到 NFS 。# 設定 K8S Master 可以執行 Pod
$ kubectl taint node m1 node-role.kubernetes.io/control-plane:NoSchedule-
output :
node/m1 untainted
將 m1 主機安裝成 Kubernetes 的 Master
# 在 Windows 系統的 cmd 視窗, 執行以下命令
$ ssh bigred@<alp.m1 IP>
安裝 Kubernetes 要用的一些管理命令
$ sudo apk update; sudo apk add kubeadm kubelet kubectl --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted
kubeadm
,建置 K8S 會用到的命令kubelet
,將 CRI-O
建立出來的 Container 包成 pod 的程式kubectl
,controls the Kubernetes cluster manager,kubectl-overview 官網介紹連結apk add
命令補充
--repository <REPO>
,指定其他套件下載網址--update-cache
, Alias for '–cache-max-age 1'--allow-untrusted
,安裝帶有不受信任簽名或沒有簽名的套件m1 的 IP
$ echo $IP
192.168.61.4
透過 kubeadm
來設定 Master 主機
$ sudo kubeadm init --service-cidr 10.98.0.0/24 --pod-network-cidr 10.244.0.0/16 --service-dns-domain=k8s.org --apiserver-advertise-address $IP
kubeadm init
,設定 Kubernetes control plane ( m1 主機 ),將它初始化--service-cidr 10.98.0.0/24
,K8S 對外提供服務的 IP 位址,給我們辦公室的同仁們,機房的其他應用系統,甚至 Internet 上面的使用者,來提供這樣的服務,我們設定的是10.98.0.0/24
,所以代表我們 Kubernetes 做出來的服務最多可以有 254 個。--pod-network-cidr 10.244.0.0/16
,pod 的 IP 位址範圍,可以有 254 * 254 = 64516 個 pod。--service-dns-domain=k8s.org
,因為 K8S 內部有 Service Discovery ,所以要設定 Domain Name ,對外的應用系統連接網址使用此設定名稱,在外面做案子的話,就用公司的 Domain Name 來命名。--apiserver-advertise-address $IP
,將 m1 主機的 IP 位址 設定為 Kubernetes Master 的 API Server
的 IP將 Kubelet
設定為系統自動啟動
$ sudo rc-update add kubelet default
Kubelet
,需設定為系統自動啟動 ?檢查 pod 的 image
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
k8s.gcr.io/kube-apiserver v1.24.3 d521dd763e2e 3 weeks ago 131 MB
k8s.gcr.io/kube-proxy v1.24.3 2ae1ba6417cb 3 weeks ago 112 MB
k8s.gcr.io/kube-controller-manager v1.24.3 586c112956df 3 weeks ago 121 MB
k8s.gcr.io/kube-scheduler v1.24.3 3a5aa3a515f5 3 weeks ago 52.3 MB
docker.io/rancher/mirrored-flannelcni-flannel v0.19.0 c4300d1c88c8 4 weeks ago 63.3 MB
docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin v1.1.0 fcecffc7ad4a 2 months ago 8.37 MB
k8s.gcr.io/etcd 3.5.3-0 aebe758cef4c 3 months ago 301 MB
docker.io/rancher/local-path-provisioner v0.0.22 e16d1e3a1066 4 months ago 35.3 MB
k8s.gcr.io/pause 3.7 221177c6082a 5 months ago 718 kB
k8s.gcr.io/coredns/coredns v1.8.6 a4ca41631cc7 10 months ago 47 MB
registry.k8s.io/pause 3.6 6270bb605e12 11 months ago 690 kB
docker.io/rancher/mirrored-flannelcni-flannel:v1.1.0
docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v0.19.0
將 bigred 設成 K8S 管理者
$ mkdir -p $HOME/.kube; sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config; sudo chown $(id -u):$(id -g) $HOME/.kube/config
.kube
的目錄admin.conf
這個 K8S 管理者的設定檔到 使用者家目錄下 .kube
目錄裡面的 config
這個檔案裡面config
的權限設為 當前使用者的權限cp -i
,覆蓋前會提示id -u
,顯示當前使用者的 UIDid -g
,顯示當前使用者群組的 GID設定 K8S Master 可以執行 Pod
$ kubectl taint node m1 node-role.kubernetes.io/control-plane:NoSchedule-
NoSchedule
的 taint ,有了這個 taint 之後, pod 就不會建在 m1 的 node 上,這時候在規則裡面改成 NoSchedule-
, 就能破格。Kubernetes 1.24.4 版還要再執行以下命令
$ kubectl taint node m1 node-role.kubernetes.io/master:NoSchedule-
flannel 網路套件,動用的技術 : Static Route
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
安裝好後,將 m1 主機重新開機
$ sudo reboot
檢查 node 的狀態
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
m1 Ready control-plane 74m v1.24.3
檢查 kube-system 這個 Namespace 裡面 pod 的狀態
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-6d4b75cb6d-grkmp 1/1 Running 1 37m
coredns-6d4b75cb6d-xlgfd 1/1 Running 1 37m
etcd-m1 1/1 Running 1 37m
kube-apiserver-m1 1/1 Running 1 37m
kube-controller-manager-m1 1/1 Running 1 37m
kube-proxy-ftk9d 1/1 Running 1 37m
kube-scheduler-m1 1/1 Running 1 37m
注意!這邊的 Namespace 與 linux 的 namespace 不同
-n
, namespace ,K8S 系統的運作空間coredns
,供外部通訊所使用的 Domain Name Server ( 負責做 Service Discovery )kube-apiserver-m1
,負責接收使用者的命令etcd-m1
,紀錄 K8S 系統的 Metadatakube-scheduler-m1
,負責啟動 Podkube-controller-manager-m1
,負責監控與維護 Podflannel
+ kube-proxy
可以讓 pod 在不同 nodes 之間網路能互通有無看 m1 主機維運系統的 4 個 pod 的 Container 狀態
$ crictl ps -a
output :
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
45c3af598722d a4ca41631cc7ac19ce1be3ebf0314ac5f47af7c711f17066006db82ee3b75b03 9 minutes ago Running coredns 1 1a9c8965ba001 coredns-6d4b75cb6d-xlgfd
54317fe6de819 a4ca41631cc7ac19ce1be3ebf0314ac5f47af7c711f17066006db82ee3b75b03 9 minutes ago Running coredns 1 6581b3ba9f2b8 coredns-6d4b75cb6d-grkmp
06f7e1a7dc5c4 252b2c3ee6c86b9cba63c9bddc2d1638d4152b5713009ff9397498014d06178e 10 minutes ago Running kube-flannel 1 b0b014f1d4cb6 kube-flannel-ds-r5p9q
f32b9416f5600 2ae1ba6417cbcd0b381139277508ddbebd0cf055344b710f7ea16e4da954a302 10 minutes ago Running kube-proxy 1 f5b632754d09e kube-proxy-ftk9d
81fa3b96f83f4 252b2c3ee6c86b9cba63c9bddc2d1638d4152b5713009ff9397498014d06178e 10 minutes ago Exited install-cni 1 b0b014f1d4cb6 kube-flannel-ds-r5p9q
38cdbe521c340 fcecffc7ad4af70c8b436d45688771e0562cbd20f55d98581ba22cf13aad360d 10 minutes ago Exited install-cni-plugin 1 b0b014f1d4cb6 kube-flannel-ds-r5p9q
46ac2b6b44a73 586c112956dfc2de95aef392cbfcbfa2b579c332993079ed4d13108ff2409f2f 10 minutes ago Running kube-controller-manager 1 1132c83fc98fc kube-controller-manager-m1
adbf8a41e4cb8 d521dd763e2e345a72534dd1503df3f5a14645ccb3fb0c0dd672fdd6da8853db 10 minutes ago Running kube-apiserver 1 e2caae9b8b044 kube-apiserver-m1
ee38c42a981d8 aebe758cef4cd05b9f8cee39758227714d02f42ef3088023c1e3cd454f927a2b 10 minutes ago Running etcd 1 dc7ecd13048ef etcd-m1
794fdd119dd8d 3a5aa3a515f5d28b31ac5410cfaa56ddbbec1c4e88cbdf711db9de6bbf6b00b0 10 minutes ago Running kube-scheduler 1 ded0bada61429 kube-scheduler-m1
# ssh 到 w1 主機安裝 kubelet 及 kubeadm 命令
$ ssh w1 'sudo apk add kubeadm kubelet --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted'
# ssh 到 w2 主機安裝 kubelet 及 kubeadm 命令
$ ssh w2 'sudo apk add kubeadm kubelet --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted'
# ssh 到 w1, w2 主機,將 kubelet 設成 Deamon
$ ssh w1 'sudo rc-update add kubelet default'; ssh w2 'sudo rc-update add kubelet default'
kubectl
的原因是因為我們規劃的管理主機在 m1 ,work node 不需要管理其他機器。在 m1 的終端機, 執行以下命令
# 設定 JOIN 環境變數
$ export JOIN=$(echo " sudo `kubeadm token create --print-join-command 2>/dev/null`")
# 將 w1 主機,join 到 m1 的這個 k8s 的 Cluster
$ ssh w1 "$JOIN"; ssh w2 "$JOIN"
# 將 w2 主機,join 到 m1 的這個 k8s 的 Cluster
$ ssh w1 sudo reboot; ssh w2 sudo reboot
描述 : 將 w1 和 w2 的主機,join 到 K8S 系統的命令
$ echo " sudo `kubeadm token create --print-join-command`"
--print-join-command
,不要只打印令牌,而是打印使用令牌加入集群所需的完整 “kubeadm join” 標誌。output :
sudo kubeadm join 192.168.61.4:6443 --token tx06v1.le8z0pagl0ltfycz --discovery-token-ca-cert-hash sha256:0f4c243af9e70715a72f30ba0bc5cc44210f1b8fc66447cde16bad3a8c48d691
在 m1 終端機, 執行以下命令
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
m1 Ready control-plane 76m v1.24.3
w1 Ready <none> 4m21s v1.24.3
w2 Ready <none> 79s v1.24.3
即使此時 w1 和 w2 notready
,也可以貼標籤,因為它是對 etcd
的資料庫作業。
$ kubectl label node w1 node-role.kubernetes.io/worker=; kubectl label node w2 node-role.kubernetes.io/worker=
worker=
, worker 的等號後面還可以放更詳細的說明,例如 : Asia, Taipei… 等檢查是否符合預期
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
m1 Ready control-plane 75m v1.24.3
w1 Ready worker 37m v1.24.3
w2 Ready worker 36m v1.24.3
kubectl run 產生 pod ,pod 名字 nginx
$ kubectl run nginx --image=quay.io/cloudwalker/nginx
pod/nginx created --> ( 高機率騙人,有可能產生出來是掛的 )
kubectl run
,Create and run a particular image in a pod.--image=''
,The image for the container to run.檢查是否符合預期
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 ContainerCreating 0 8s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 52s
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATE
nginx 1/1 Running 0 74s 10.244.1.3 w1 <none> <none>
-o output
-o wide
,可以看到 pod 的 ip 位址,和所在的 node$ curl -s http://10.244.1.3 | grep 'Welcome'
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
# 刪除 pod <pod name>
$ kubectl delete pods nginx
$ kubectl run nginx --image=quay.io/cloudwalker/nginx
pod/nginx created
$ kubectl run a1 --image=quay.io/cloudwalker/alpine
pod/a1 created
## 失敗原因是因為
## quay.io/cloudwalker/alpine 這張光碟片第一個跑的命令是 sh,
## 所以後面給一個虛擬終端機
$ kg pods
NAME READY STATUS RESTARTS AGE
a1 0/1 CrashLoopBackOff 2 (18s ago) 44s
# -it ,給一個個虛擬終端機
# --rm ,pod 停止就刪除 pod
$ kubectl run a1 --rm --image=quay.io/cloudwalker/alpine -it
$ kubectl exec -it a1 -- sh
## 要對 pod 下達命令一定要在 -- 後面
/ # curl http://10.244.1.3
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...中間省略
</html>
/ # exit
系統工程
kubectl run
(命令式 Imperative) - Manage K8s object (POD, Controller, Service) using CLI
kubeclt run
的方法產生kubectl run
(Imperative) 這命令適合做概念性(物件的雛形)驗證 (Proof of Concept;POC)kubeclt run
的方法 對於 K8S 能產生和設定多少物件是有限的kubectl create/apply
(聲明式 Declarative) - By defining K8s objects in yaml file
kubectl create
(Declarative) 與 yaml 設定檔適合建立複雜的企業應用服務檢視 K8S 當前所有 Namespace 的資訊
$ kubectl get ns
NAME STATUS AGE
default Active 10d
kube-flannel Active 10d
kube-node-lease Active 10d
kube-public Active 10d
kube-system Active 10d
default
,預設的 Namespaces 名稱為 default,過去我們產生的物件像是, Deployment, Services 等若沒特別指定 Namespace 都是存放在名稱為 default 的 namespaces 中。kube-public
,是個特殊的 namespace,存放在裡面的物件可被所有的使用者讀取。$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-6d4b75cb6d-tslq8 1/1 Running 5 10d
coredns-6d4b75cb6d-vqz9h 1/1 Running 5 10d
etcd-m1 1/1 Running 5 10d
kube-apiserver-m1 1/1 Running 5 10d
kube-controller-manager-m1 1/1 Running 5 10d
kube-proxy-274g9 1/1 Running 4 10d
kube-proxy-kqlrm 1/1 Running 4 10d
kube-proxy-zw6sb 1/1 Running 5 10d
kube-scheduler-m1 1/1 Running 5 10d
kube-system
,在 Kubernetes 中,較特別的資源都會存放在 kube-system 這個 namespace。若是用 kubectl get all -n kube-system
查看,可以發現 kube-dns 或是 kube-apiserver…等維運系統的 pod 都是存放在該 namepsace 中。$ kubectl create deployment --image quay.io/cloudwalker/nginx demo-nginx
deployment.apps/demo-nginx created
$ kubectl describe deployment demo-nginx | grep Namespace
Namespace: default
$ kubectl create deployment --image quay.io/cloudwalker/nginx demo-nginx
Error from server (AlreadyExists): deployments.apps "demo-nginx" already exists
$ kubectl create deployment --image quay.io/cloudwalker/nginx demo-nginx -n kube-public
# 建立工作目錄
$ mkdir -p ~/wulin/yaml; cd ~/wulin
# 建立新的 namespace
$ kubectl create namespace myoffice
namespace/myoffice created
# 檢查有無建立成功
$ kubectl get namespace
NAME STATUS AGE
default Active 10d
kube-flannel Active 10d
kube-node-lease Active 10d
kube-public Active 10d
kube-system Active 10d
myoffice Active 2s
# 刪除 myoffice namespace
$ kubectl delete namespace myoffice
# 再次建立 myoffice namespace
$ kubeclt create namespace myoffice
# 檢查
$ kubectl get namespace
建立 pod
$ kubectl run n1 --image=quay.io/cloudwalker/alpine -n myoffice -- sleep infinity
kubectl run
,用指定 image 產生 podn1
,pod 的名字-n myoffice
,指定等等的 pod 會建在 myoffice 這個 namespace-- sleep infinity
,--
後面放的是 Container 要執行的程式 ,這裡是 sleep infinity
檢視 pod 的狀態
$ kg pods
NAME READY STATUS RESTARTS AGE
gocgi 1/1 Running 1 20h
mydb 1/1 Running 1 21h
pod1 1/1 Running 1 16h
$ kg pods -n myoffice
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 22m
$ kubectl exec -it n1 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Error from server (NotFound): pods "n1" not found
kubectl exec [POD] [COMMAND]
,這個用法以被棄用,且即將在未來的版本被刪除kubectl exec [POD] -- [COMMAND]
,Command 前面要加 --
Error from server (NotFound): pods "n1" not found
,找不到 n1 這台 pod,因未指定 Namespace 的話,預設會到 default 這個 namespace 裡面找有沒有 n1 這個 podkubectl exec -it n1 -n myoffice -- sh
$ kubectl exec -it -n myoffice n1 -- sh
/ # ping -c 1 www.hinet.net
PING www.hinet.net (163.28.83.113): 56 data bytes
ping: permission denied (are you root?)
# 檢視 NameServer 是誰
/ # cat /etc/resolv.conf
search myoffice.svc.k8s.org svc.k8s.org k8s.org localdomain
nameserver 10.98.0.10
options ndots:5
/ # exit
nameserver 10.98.0.10
,這是 K8S 的 DNS Serverwww.hinet.net
這個名稱丟到網路去問,最後解析出 163.28.83.113
這個 IP 位址ping: permission denied (are you root?)
,權限不足,Linux Capaibility 不給權限用 yaml 檔建立 pod
$ nano ~/wulin/yaml/pod-n2.yaml
apiVersion: v1
kind: Pod
metadata:
name: n2
namespace: myoffice
labels:
name: n2
spec:
hostname: n2
containers:
- image: quay.io/cloudwalker/busybox
command:
- sleep
- "60"
name: n2
kind
,指定要建立 pod 這個物件metadata
,設定重要資訊
name: n2
,pod 名字 : n2namespace
,設定 pod 所在的 namespace : myofficelabels
,pod 上貼一個標籤spec
,設定 pod 內部的結構
hostname: n2
,設定 Container 的 hostname,多個 Container 也會共用同一個 hostnamecontaienrs
,設定 Container 的資訊command
,container 要先執行的命令
command:
這個宣告, 會覆蓋掉 image 的內定命令, 包括 entrypoint 指定的命令name
,設定 Container 在系統上的名稱apply 它
$ kubectl apply -f ~/wulin/yaml/pod-n2.yaml
檢查是否符合預期
$ kubectl get pods -n myoffice
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 61m
n2 1/1 Running 0 14s
$ kubectl config set-context --current --namespace=myoffice
set-context
,進入 K8S 的路口set-context
,入口的意思,進入商業辦公大樓的大門--current
,入口指定預設的入口,因為進入的方法有很多種,我們就走預設的入口--namespace=myoffice
,進到 myoffice 這個 namespace$ kubectl get pods
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 25m
n2 1/1 Running 5 (105s ago) 8m32s
$ cat ~/.kube/config | grep -A 6 contexts:
contexts:
- context:
cluster: kubernetes
namespace: myoffice
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
~/.kube/config
,進入 K8S 這棟大樓的磁卡 (裡面記錄著使用者在 K8S 的名字和識別代號,及擁有多大的權限可以對 K8S 的物件進行訪問…等資訊)contexts
,大樓的入口,後面加 s
是因為可能會有很多入口
context
,大樓的入口cluster: kubernetes
,大樓的名字 : kubernetes
namespace: myoffice
,大樓裡的辦公室名字: myoffice
user: kubernetes-admin
,使用者進入大樓辦公室用的身分名稱$ cat ~/.kube/config | grep users -A1
users:
- name: kubernetes-admin
~/.kube/config
這個檔案得知)在 m1 終端機, 執行以下命令
$ mkdir -p ~/wulin/{sshd,fbs}
$ echo $'FROM quay.io/cloudwalker/alpine
RUN apk update && apk upgrade && apk add --no-cache nano sudo wget curl \
tree elinks bash shadow procps util-linux git coreutils binutils \
findutils grep openssh-server tzdata && \
# 設定時區
cp /usr/share/zoneinfo/Asia/Taipei /etc/localtime && \
ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key && \
echo -e \'Welcome to ALP sshd 6000\\n\' > /etc/motd && \
# 建立管理者帳號 bigred
adduser -s /bin/bash -h /home/bigred -G wheel -D bigred && \
echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
echo -e \'bigred\\nbigred\\n\' | passwd bigred &>/dev/null && \
rm /sbin/reboot && rm /usr/bin/killall
EXPOSE 22
ENTRYPOINT ["/usr/sbin/sshd"]
CMD ["-D"]' > ~/wulin/sshd/Dockerfile
ENTRYPOINT ["/usr/sbin/sshd"]
,強制宣告等下 run 的 Container 等等只能跑 /usr/sbin/sshd
這個命令CMD ["-D"]
,上面命令的參數Container Image 建立與測試
$ sudo podman build -t alp.sshd ~/wulin/sshd/
$ sudo podman images | grep alp.sshd
localhost/alp.sshd latest 01424a71e3d6 3 minutes ago 67.6 MB
$ sudo podman run --name a1 -h a1 -d -p 22100:22 alp.sshd
220ce94e636ce9270604bd31a7dd5d515be6008ab6351295cd10a66da5d978d1
$ ssh bigred@$IP -p 22100
bigred@192.168.61.4 s password: bigred
Welcome to ALP sshd 6000
a1:~$ git version
git version 2.30.2
a1:~$ exit
$ sudo podman rm -f a1
建立 K8S 應用物件方式
kubectl run
(命令式 Imperative) - Manage K8s object (POD, Controller, Service) using CLIkubectl create/apply
(聲明式 Declarative) - By defining K8s objects in yaml file
For creating multiple complex object, declarative approach is preferred as it follows Infrastructure as a Code approach. However, imperative approach is also useful when we want to do quick POC and save yourself from writing yaml files. Let's take a look at some imperative commands.
kubectl run (Imperative) 這命令適合做概念性驗證 (Proof of Concept;POC), kubectl create (Declarative) 與 yaml 設定檔適合建立複雜的企業應用服務
# 建立一個 pod ,image 用的是 alp.sshd
$ kubectl run demo --image=alp.sshd
[註] 從 1.18 這個版本開始, 上面命令只會產生 pod, 不再會產生 deployment object 及 replicaset controller.
# 為何 pod 會建立失敗 ? ErrImagePull ?
$ kg pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo 0/1 ErrImagePull 0 13s 10.244.2.5 w2 <none> <none>
# 因為在我們的工作主機上,沒有這片 image,所以先刪除 pod
# --force ,強制刪除
$ kubectl delete pods demo --force
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "demo" force deleted
# 打包 alp.sshd 這個 image
$ sudo podman save alp.sshd > alp.sshd.tar
# 將打包檔拷貝到 w1 和 w2 主機
$ scp alp.sshd.tar w1:alp.sshd.tar; scp alp.sshd.tar w2:alp.sshd.tar
# 在 w1 和 w2 主機,將打包檔的 image 還原
$ ssh w1 'sudo podman load < alp.sshd.tar'
$ ssh w2 'sudo podman load < alp.sshd.tar'
# 再 run 一次 pod
# --image-pull-policy IfNotPresent ,如果本地端有 image 就不用上網下載了
$ kubectl run demo --image-pull-policy IfNotPresent --image=localhost/alp.sshd
pod/demo created
# 以下命令會失敗的原因是因為, image 前面不加 localhost ,就會上網飆到 docker.io/library 抓 image
$ kubectl run demo --image=alp.sshd
# 其實不加 --image-pull-policy IfNotPresent 也會過
$ kubectl run demo --image=localhost/alp.sshd
# 檢查是否符合預期
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo 1/1 Running 0 3m12s 10.244.2.2 w2 <none> <none>
編輯 sshd yaml 檔
$ nano ~/wulin/sshd/pod-sshd.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-sshd
spec:
containers:
- name: mysshd
image: localhost/alp.sshd
imagePullPolicy: IfNotPresent
command:
- /bin/bash
- -c
- |
adduser -s /bin/bash -h /home/rbean rbean
echo -e 'rbean\nrbean\n' | passwd rbean
/usr/sbin/sshd -D
command
能破格 Dockerfile 設定的 ENTRYPOINT
,讓 Container 跑 command
宣告的命令command
最後還是要宣告 /usr/sbin/sshd -D
,讓 Container 有個 SSH Server
一直在前景執行的命令/bin/bash -c
,c
的意思是 command,合起來就是讓 bash 貝殼程式跑下面的命令|
,這裡的 pipe 意思是會保留以下宣告命令的格式,有空格就空格,有換行就換行,通通保留,如果沒有宣告,貝殼程式會把下面 3 行命令當成一行用 apply 產生 pod
$ kubectl apply -f ~/wulin/sshd/pod-sshd.yaml
檢查 pod 狀態
$ kg po pod-sshd -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-sshd 1/1 Running 0 2m 10.244.1.37 w1 <none> <none>
SSH 連線測試 pod 功能
$ ssh rbean@10.244.1.37
rbean@10.244.1.37s password:
Welcome to ALP sshd 6000
# 建立 zzz 目錄
pod-sshd:~$ mkdir zzz
pod-sshd:~$ ls -ld zzz
drwxr-sr-x 2 rbean rbean 4096 Sep 1 09:49 zzz
pod-sshd:~$ exit
logout
Connection to 10.244.1.37 closed.
刪除 pod
$ kubectl delete pod pod-sshd
pod "pod-sshd" deleted
再次建立 pod
$ kubectl apply -f ~/wulin/sshd/pod-sshd.yaml
pod/pod-sshd created
連進 pod 檢視 zzz 目錄是否還在 ?
$ kubectl get pod pod-sshd -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-sshd 1/1 Running 0 73s 10.244.1.38 w1 <none> <none>
# 用 rbean 帳號連進 pod
$ ssh rbean@10.244.1.38
Warning: Permanently added '10.244.1.38' (RSA) to the list of known hosts.
rbean@10.244.1.38s password:
Welcome to ALP sshd 6000
# 檢視剛剛建立的目錄
pod-sshd:~$ ls -l
total 0
將 pod 所在的 w1 這台機器重開機
$ ssh w1 'sudo reboot'
再次檢視 pod 狀態
$ kubectl get pods pod-sshd -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-sshd 1/1 Running 1 20m 10.244.1.39 w1 <none> <none>
編輯 yaml 檔
$ nano ~/wulin/yaml/podhostport.yaml
apiVersion: v1
kind: Pod
metadata:
name: podhostport
spec:
containers:
- name: wk01
image: quay.io/cloudwalker/alpine.sshd
imagePullPolicy: IfNotPresent
ports:
- containerPort: 22
hostPort: 22101
securityContext:
capabilities:
add: ["AUDIT_WRITE", "SYS_CHROOT", "CAP_NET_RAW"]
command: ["/usr/sbin/sshd"]
args: ["-D"]
nodeSelector:
kubernetes.io/hostname: w1
image:
,Container 的 image 可以換裝成 localhost/alp.sshd
hostport: 22101
,kube-proxy
會在 pod 所在主機開一個 22101 的 port,對應到 pod 裡 Container 開的 22 port,效能優於 kubectl port-forward
nodeselector
,透過 node 上的 labels 來決定 pod 要建在哪台 node 上先建立工作目錄
$ mkdir -p ~/wulin/mydb/
編輯 yaml 檔
$ nano wulin/mydb/pod-mydb.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: mydb
name: mydb
spec:
containers:
- image: localhost/mydb
name: mydb
ports:
- containerPort: 3306
hostPort: 3306
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
產生 pod
$ ka -f wulin/mydb/pod-mydb.yaml
pod/mydb created
$ crio-status config
output :
...
default_capabilities = ["CHOWN", "DAC_OVERRIDE", "FSETID", "FOWNER", "SETGID", "SETUID", "SETPCAP", "NET_BIND_SERVICE", "AUDIT_WRITE", "SYS_CHROOT", "KILL"]
...
NET_BIND_SERVICE
,如果 Container 要開的 port number 小於 1024 ,會需要有這個 CapabilitiesSSH
通訊協定一定會需要 "AUDIT_WRITE
", "SYS_CHROOT
" 這兩個 Capabilities,才能讓 CRI-O 做出來的 Container run openssh 的時候,能夠讓 openssh Server 與外部連接,給別人從外面連進來,如果沒有以上兩個 Capabilities ,即使要用 root 去登入也沒用。設定後要記得重新開機。$ sudo nano /etc/crio/crio.conf
output :
[crio.runtime]
.......
default_capabilities = [
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"SETGID",
"SETUID",
"SETPCAP",
"NET_BIND_SERVICE",
"AUDIT_WRITE",
"SYS_CHROOT",
"KILL"
]
.......
讓 demo 這個 pod 安裝 libcap 套件
$ kubectl exec demo -- sudo apk add libcap
--
,兩個 -
後面接的命令就是要給 pod 執行如果這行安裝失敗,可以進入 pod 的 Container 將 nameserver 的設定檔 (
/etc/resolv.conf
),新增一行nameserver 8.8.8.8
查看 demo 這個 pod 裡面的 Container 裡面有哪些 Linux capabilities
$ kubectl exec demo -- sudo capsh --print
output :
Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_sys_chroot,cap_audit_write=ep
...
# K8S 叢集內連接 demo app,k8s 叢集內部的網路連線全程都會加密
# -it ,給一個虛擬終端機
# -- bash ,跑 bash 貝殼程式
$ kubectl exec -it pods/demo -- bash
bash-5.1# exit
# 檢視 demo 這個 pod 的 IP 位址
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo 1/1 Running 0 26m 10.244.2.2 w2 <none> <none>
# ssh 連進 demo 這個 pod
bigred@m1:~$ ssh bigred@10.244.2.2
Warning: Permanently added '10.244.2.2' (RSA) to the list of known hosts.
bigred@10.244.2.2 s password:
Welcome to ALP sshd 6000
# 離開 pod
demo:~$ exit
# K8S 叢集外連接 demo app
# --address 只能指定 Master 的 IP 位址
$ kubectl port-forward --address $IP pods/demo 22100:22 &
[1] 27780
# 在 Windows 系統的 cmd 視窗, 執行以下命令
$ ssh bigred@<alp.m1 IP> -p 22100
Handling connection for 22100
bigred@192.168.61.4 s password: bigred
Welcome to ALP sshd 6000
# 離開 pod
demo:~$ exit
# 移除 pod
$ kubectl delete pod demo
以上動作運作架構圖
# 建立專案目錄,編輯 yaml 檔
$ cd ~/wulin; nano sshd/pod-sshd.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-sshd
spec:
containers:
- name: mysshd
image: localhost/alp.sshd
imagePullPolicy: IfNotPresent
apiVersion: v1
,這行是跟 API Server 說,我們的 yaml 檔是 v1 ,這行的設定由 K8s 的版本來決定kind: Pod
, 設定 K8s 的物件metadata:
name: pod-sshd ---> 設定 pod 的名字
spec:
containers:
- name: mysshd ---> 設定 Container 的名稱
image: localhost/alp.sshd ---> 設定 Container 用哪片 image
imagePullPolicy: IfNotPresent ---> 如果本地端有這片 image,不用上網下載
imagePullPolicy: Never
,不要上網下載 image# apply yaml 檔,建立 pod
$ kubectl apply -f sshd/pod-sshd.yaml
# 檢查是否符合預期
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo 1/1 Running 0 126m 10.244.2.2 w2 <none> <none>
pod-sshd 1/1 Running 0 13s 10.244.1.3 w1 <none> <none>
10.244.1.3
,看到這個 IP 位址 ,就知道 Flannel 網路套件在做事,將我們的 pod 分配到第一台工作機
$ nano sshd/pod-sshd.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-sshd
spec:
containers:
- name: mysshd
image: localhost/alp.sshd
imagePullPolicy: IfNotPresent
command:
- /bin/bash
- -c
- |
adduser -s /bin/bash -h /home/rbean rbean
echo -e 'rbean\nrbean\n' | passwd rbean
/usr/sbin/sshd -D
command:
,在 K8s 的 yaml 檔中宣告一段等下 Container 要跑的 shell script ,即使 Container 的 image 本身有 ENTRYPOINT ["/usr/sbin/sshd"]
,一樣會被破格,強制要跑命令$ cd ~/wulin/
$ nano fbs/Dockerfile
FROM quay.io/cloudwalker/alpine
RUN apk update && apk upgrade && apk add --no-cache nano sudo wget curl \
tree elinks bash shadow procps util-linux coreutils binutils findutils grep && \
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash && \
# 設定 filebrowser
filebrowser config init --port 4000 --address "" --log "/tmp/fbs" --root="/srv" && \
# 建立管理者帳號
filebrowser users add admin "admin" --perm.admin=true
CMD ["filebrowser"]
curl -fsSL 'URL/get.sh' | bash
,利用 curl 命令將網站上的程式輸出在螢幕,但是 | bash
,會將輸出在螢幕的程式碼丟給重裝貝殼執行,這樣做的好處是不會留下紀錄。
-f
,下載失敗不會輸出錯誤訊息-s
,Silent modefilebrowser config init
,將網站初始化
--port 4000
,網站的 port number : 4000--address ""
,任何 IP 位址的來源都能與我連線--log "/tmp/fbs"
,網站 log 檔的存放檔案 --root="/srv"
,使用者丟檔案到網站後存放的目錄區,可自行設定filebrowser users add
,建立帳號
admin "admin"
,帳號 : admin
,密碼 : admin
--perm.admin=true
,打開管理員的權限build image
$ sudo podman build -t alp.myfbs fbs/
# 檢查是否符合預期
$ sudo podman images | grep fbs
localhost/alp.myfbs latest 24d1b68d43f8 About a minute ago 74.3 MB
手動部署 alp.myfbs Image
# 備份 Image
$ sudo podman save localhost/alp.myfbs > alpine.myfbs.tar
$ scp alpine.myfbs.tar w1:~; scp alpine.myfbs.tar w2:~
$ ssh w1 'sudo podman load < alpine.myfbs.tar'; ssh w2 'sudo podman load < alpine.myfbs.tar'
建立 File Browser Server Pod
# 編輯 yaml 檔
$ nano fbs/pod-fbs.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-fbs
labels:
name: pod-fbs
spec:
containers:
- name: myfbs
image: localhost/alp.myfbs
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4000
用 yaml 檔 建立 pod
$ kubectl apply -f fbs/pod-fbs.yaml
檢查 pod 的 4000 port 有無開啟
# 看 pod 的 IP
$ kg pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-fbs 1/1 Running 0 2m30s 10.244.1.6 w1 <none> <none>
# 網貓測 4000 port
$ nc -w 1 10.244.1.6 4000
$ echo $?
0
$ nano fbs/svc-fbs.yaml
kind: Service
apiVersion: v1
metadata:
name: svc-fbs
spec:
externalIPs:
- 192.168.61.4
selector:
name: pod-fbs
ports:
- port: 8080
targetPort: 4000
selector:
,選擇有貼 pod-fbs 這個標籤的 podexternalIPs:
,設定對外服務的 IP 位址ports:
- port: 8080 ---> 對外的 port number
targetPort: 4000 ---> pod 開的 port number
建立 service/svc-fbs 服務
$ kubectl apply -f fbs/svc-fbs.yaml
檢查是否符合預期
$ kubectl get all -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/pod-fbs 1/1 Running 0 14m 10.244.1.6 w1 <none> <none>
pod/pod-sshd 1/1 Running 0 39m 10.244.2.4 w2 <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.98.0.1 <none> 443/TCP 4h5m <none>
service/svc-fbs ClusterIP 10.98.0.230 192.168.61.4 8080/TCP 83s name=pod-fbs
看 pod 的標籤
$ kubectl get pods --show-labels
AME READY STATUS RESTARTS AGE LABELS
pod-fbs 1/1 Running 0 20m name=pod-fbs
pod-sshd 1/1 Running 0 44m <none>
答 : pod-fbs 的 container 裡 /srv
目錄下
# 進入 pod
$ kubectl exec -it pod/pod-fbs -- bash
# 檢查是否符合預期
bash-5.1# ls -al srv
total 12
drwxr-xr-x 1 root root 4096 Jul 31 06:46 .
dr-xr-xr-x 1 root root 4096 Jul 31 06:23 ..
drwxr-xr-x 3 root root 4096 Jul 31 06:46 ALP3
bash-5.1# touch srv/zzz.txt
bash-5.1# ls -al srv/
total 12
drwxr-xr-x 1 root root 4096 Jul 31 06:51 .
dr-xr-xr-x 1 root root 4096 Jul 31 06:23 ..
drwxr-xr-x 3 root root 4096 Jul 31 06:46 ALP3
-rw-r--r-- 1 root root 0 Jul 31 06:51 zzz.txt
如果刪除 pod ,裡面存放的內容就不見了 ! ! !
$ kubectl delete pod pod-fbs
pod "pod-fbs" deleted
$ kubectl delete svc svc-fbs
service "svc-fbs" deleted
對於 K8s 的 Storage 怎麼處理 ?
編輯 yaml 檔
$ nano fbs/pod-fbs.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-fbs
labels:
name: pod-fbs
spec:
containers:
- name: myfbs
image: localhost/alp.myfbs
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4000
volumeMounts:
- mountPath: /srv
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /tmp
volumeMounts
,設定 Container 的目錄要將哪個目錄掛載到 volumes
的目錄
mountPath
,要掛載的目錄volumes
,下面可以宣告儲存體的設定
hostPath
,volume 的型態
path: /tmp
,這是一個在 Linux 系統的 /tmp
目錄,但注意 ! 這個目錄重開機裡面的內容會消失 (這是 Linux /tmp
目錄的規則)。
apiVersion: v1
kind: Pod
metadata:
name: pod-fbs
labels:
name: pod-fbs
spec:
containers:
- name: myfbs
image: localhost/alp.myfbs
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4000
volumeMounts:
- mountPath: /srv
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /tmp
nodeSelector:
kubernetes.io/hostname: w2
系統工程
CustomResourceDefinitions
,自定義 K8s 的物件Short name Full name
csr certificatesigningrequests
cm configmaps
ds daemonsets
deploy deployments
ep endpoints
ev events
hpa horizontalpodautoscalers
ing ingresses
ns namespaces
pvc persistentvolumeclaims
svc services
pv persistentvolumes
po pods
pdb poddisruptionbudgets
psp podsecuritypolicies
rs replicasets
rc replicationcontrollers
quota resourcequotas
sa serviceaccounts
# 在 Windows 系統的 cmd 視窗, 執行以下命令
$ ssh bigred@<alp.m1 IP>
# 從 1.18 這個版本開始, 下面命令只會產生 pod,
# 不再會產生 deployment object 及 replicaset controller.
# --image= ,等號右邊的格式為 image 網站/帳號/image 名稱
$ kubectl run a1 --image-pull-policy IfNotPresent --image=quay.io/cloudwalker/alpine.derby
pod/a1 created
# STATUS 的 ContainerCreating,
# 代表 Container 正在被 CRI-O 透過 pull 命令到網路上下載 Container 需要的 image
$ kg pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
a1 0/1 ContainerCreating 0 64s <none> w1 <none> <none>
# 把 pod 的 IP 位址丟到一個變數
$ podip=$(kg pods -o wide | grep -e "^a1 " | tr -s ' ' | cut -d ' ' -f6)
# 檢查是否符合預期
$ curl http://$podip:8888
<h1>Welcome to Spring Boot</h1>
$ curl http://$podip:8888/hostname
Hostname : a1
進入a1 pod
$ kubectl exec -it a1 -- bash
--
分隔符號,後面放的是 pod 要執行的程式
連線的動作會經過加密,安全等級不輸 SSH
檢查有無啟動pid 的 namespace
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 2180 1672 ? Ss 06:29 0:00 bash -c /derby/app/startup
root 2 1.5 2.5 3496804 207136 ? Sl 06:29 0:11 java -jar -Dderby.system.home=/derby/db /derby/app/dt-0.0.1-SNAPSHOT.war
root 44 0.0 0.0 2312 1696 pts/0 Ss 06:40 0:00 bash
root 45 0.0 0.0 1624 520 pts/0 R+ 06:41 0:00 ps aux
有!
在 pod a1 裡搞破壞
rm -r /bin/*
bash-4.4# ls -al /bin/
bash: ls: command not found
bash-4.4# exit
exit
command terminated with exit code 127
bigred@m1:~$ kubectl exec -it a1 -- bash
2022-05-31T06:44:14.000679204Z: 2022-05-31T06:44:14.000679086Z: executable file `bash` not found in $PATH: No such file or directory
檢查 node 本機的 /bin
bigred@m1:~$ ls -al /bin/
可以發現沒被破壞
刪除pod a1
$ kubectl delete pod a1
重新建立並執行 a1 pod
$ kubectl run a1 --image=quay.io/cloudwalker/alpine -- sleep 15
當 pod 的狀態呈現 Compelted 時, K8s 會將 pod 浴火重生,讓 pod 再次 runnning
監控 pod a1 的狀態
$ kubectl get pods a1 --watch
NAME READY STATUS RESTARTS AGE
a1 0/1 CrashLoopBackOff 4 (31s ago) 3m24s
a1 1/1 Running 5 (86s ago) 4m19s
a1 0/1 Completed 5 (101s ago) 4m34s
a1 0/1 CrashLoopBackOff 5 (16s ago) 4m49s
...以下省略
當STATUS 出現CrashLoopBackOff
,代表 pod 正在不斷被重啟。
重啟 6 次後, 會將重啟時間拉長, 繼續重啟
刪除 pod a1
$ kubectl delete pod a1
# 建立並執行 pod a1
$ kubectl run a1 --image=quay.io/ict39/alpine.derby
pod/a1 created
# 查看 pod a1 的 ip 位址和所在主機
$ kubectl get pod a1 -o wide | grep a1
a1 1/1 Running 0 36s 10.244.2.10 w2 <none> <none>
# 將 pod a1 所在主機重新開機
$ ssh w2 sudo reboot
# 檢查 pod a1 的狀態
$ kubectl get pod a1 -o wide | grep a1
a1 0/1 ContainerCreating 1 2m23s <none> w2 <none> <none>
# 再次檢查 pod a1 的狀態
$ kubectl get pod a1 -o wide | grep a1
a1 1/1 Running 1 2m35s 10.244.2.11 w2 <none> <none>
# 刪除 pod a1
$ kubectl delete pod a1
結論: pod a1 的 IP 位址會改變, 這是因爲 node 重啟時會產生一個新 Pod
$ kubectl run a1 --restart='Never' --image=quay.io/cloudwalker/alpine -- sleep 15
pod/a1 created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
a1 1/1 Running 0 10s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
a1 0/1 Completed 0 32s
$ kubectl delete pod a1
pod "a1" deleted
先建立 wulin 工作目錄
$ mkdir -p ~/wulin/yaml; cd ~/wulin
宣告 yaml/yml 檔
超級注意!縮編格式一定要對!不然絕對噴 Error 給你看!
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: sharepid
spec:
shareProcessNamespace: true
hostname: xyz
containers:
- name: derby
image: quay.io/cloudwalker/alpine.derby
imagePullPolicy: Always
- name: shell
image: quay.io/cloudwalker/alpine
imagePullPolicy: IfNotPresent
tty: true ' > yaml/sharepid.yml
kind
, 宣告要產生什麼metadata
,會宣告 pod 的名稱spec
, pod 的結構說明
shareProcessNamespace: true
,兩台 Container 用同一個 pid 的 Namespacetty: true
,給一個虛擬終端機,因為 quay.io/cloudwalker/alpine
這個 image 內定執行的命令是 sh ,所以一定要有一台虛擬終端機imagePullPolicy: Always
,代表每次都要下載 imageimagePullPolicy: IfNotPresent
, 代表沒有 image 再下載imagePullPolicy: Never
*.yaml
或是 *.yml
都可以。k8s 標準的運作,透過 yaml 檔作業
$ kubectl create -f yaml/sharepid.yml
$ kubectl get pod/sharepid -o jsonpath='{.spec.containers[*].name}';echo ""
derby shell
相同電腦名稱
$ kubectl exec sharepid -c shell -- hostname; kubectl exec sharepid -c derby -- hostname
xyz
xyz
-c 指定Container
共用 IP 位址
$ kubectl exec sharepid -c shell -- hostname -i; kubectl exec sharepid -c derby -- hostname -i
10.244.1.4
10.244.1.4
登入 sharepid 中的 shell container
$ kubectl exec -it sharepid -c shell -- sh
/ # whoami
root
/ # apk add curl
.......
/ # curl http://localhost:8888/hostname
Hostname : xyz
當我們透過localhost來連接不同的 Container 時,會更有效率,速度接近記憶體的速度。
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 /pause
6 root 0:00 bash -c /derby/app/startup
11 root 0:18 java -jar -Dderby.system.home=/derby/db /derby/app/dt-0.0.1-SNAPSHOT.war
53 root 0:00 /bin/sh
...........
/ # kill -9 1 無法 強制關閉 PID 1
/ # kill -15 1 可以 正常關閉 PID 1
/ # command terminated with exit code 137
K8s 中所有的 pod 一啟動一定會產生 /pause 這台 Container ,裡面跑的程式是 sleep (睡一輩子)站在資安的角度:全球最安全的命令,老師翻成中文:睡夢羅漢
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: twoc
annotations:
kubectl.kubernetes.io/default-container: "shell"
spec:
shareProcessNamespace: false
containers:
- name: derby
image: quay.io/cloudwalker/alpine.derby
imagePullPolicy: Never
- name: shell
image: quay.io/cloudwalker/busybox
command:
- sleep
- "60"
restartPolicy: Never'> yaml/twoc.yml
建立 pod
$ kubectl apply -f yaml/twoc.yml
pod/twoc created
檢查
$ kg pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sharepid 2/2 Running 2 (6m4s ago) 32m 10.244.1.5 w1 <none> <none>
twoc 1/2 ErrImageNeverPull 0 59s 10.244.2.6 w2 <none> <none>
發現 w2 的 node 沒有 derby Container 的 image
修改yaml 檔
$ nano yaml/twoc.yml
取得 K8S 叢集的 DNS IP 位址
$ kubectl -n kube-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.98.0.10 <none> 53/UDP,53/TCP,9153/TCP 24h
$ kubectl describe service kube-dns -n kube-system
.........
IP: 10.98.0.10
IPs: 10.98.0.10
Port: dns 53/UDP
TargetPort: 53/UDP
Endpoints: 10.244.0.6:53,10.244.0.7:53
Port: dns-tcp 53/TCP
TargetPort: 53/TCP
Endpoints: 10.244.0.6:53,10.244.0.7:53
Port: metrics 9153/TCP
.......
$ kubectl get pods -n kube-system -o wide | grep coredns
coredns-78fcd69978-5v6cd 1/1 Running 2 5d3h 10.244.0.6 m1 <none> <none>
coredns-78fcd69978-j4z9r 1/1 Running 2 5d3h 10.244.0.7 m1 <none> <none>
kube-dns
,它的 IP 位址會固定在 <network_id>.10
,只有 network_id 可以自己設,預設尾數是 10 ,開的 port : 53kubectl describe
看 kube-dns
詳細的資訊,可以看到 Endpoints:
有兩個 IP 位址,代表後面有兩個 pod 在運作Selector
來抓到對應的 pod
kube-dns
會提供 名稱解析的服務,讓我們的 pod 得到要上網的 IP 位址。kube-dns
註冊自己的網址和對應的 IP 位址 ( A record )headless
) 的特性 : service 本身不會有 IP 位址,他一樣會去跟 kube-dns
註冊,不過註冊的資訊會有 pod 的名字及 pod 的 IP 位址 (以上是老師的小秘笈)
kube-dns
註冊,註冊的資訊是自己 service 本身的名字及對應 pod 的 IP 位址$ kubectl run d1 -it --image=quay.io/cloudwalker/alpine
If you dont see a command prompt, try pressing enter.
/ # cat /etc/resolv.conf
search default.svc.k8s.io svc.k8s.io k8s.io localdomain
nameserver 10.98.0.10
options ndots:5
/ # ping www.hinet.net
PING www.hinet.net (203.66.32.110): 56 data bytes
/ # exit
DNS for Service
編輯 yaml 檔
$ nano yaml/service-dns.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-nginx
spec:
selector:
matchLabels:
run: k8s-nginx
replicas: 3
template:
metadata:
labels:
run: k8s-nginx
spec:
containers:
- name: k8s-nginx
image: quay.io/cloudwalker/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-cluster
spec:
selector:
run: k8s-nginx
ports:
- name: http
port: 80
protocol: TCP
---
kind: Service
apiVersion: v1
metadata:
name: svc-headless
spec:
selector:
run: k8s-nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
clusterIP: None
---
透過 kubectl create
產生 yaml 檔中的物件(包含 : deployment, svc-cluster, svc-headless)
$ kubectl create -f yaml/service-dns.yaml
檢查
$ kg all
NAME READY STATUS RESTARTS AGE
pod/k8s-nginx-56975f8f86-f49nr 1/1 Running 0 4m40s
pod/k8s-nginx-56975f8f86-hf4zh 1/1 Running 0 4m40s
pod/k8s-nginx-56975f8f86-qk727 1/1 Running 0 4m40s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.98.0.1 <none> 443/TCP 17d
service/svc-cluster ClusterIP 10.98.0.179 <none> 80/TCP 4m40s
service/svc-headless ClusterIP None <none> 80/TCP 4m40s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/k8s-nginx 3/3 3 3 4m40s
NAME DESIRED CURRENT READY AGE
replicaset.apps/k8s-nginx-56975f8f86 3 3 3 4m40s
檢視 Service endpoints,當前的服務用的是相同的 pod
$ kubectl get endpoints svc-cluster
NAME ENDPOINTS AGE
svc-cluster 10.244.1.35:80,10.244.2.35:80,10.244.2.36:80 8m25s
$ kubectl get endpoints svc-headless
NAME ENDPOINTS AGE
svc-headless 10.244.1.35:80,10.244.2.35:80,10.244.2.36:80 8m31s
查詢 Service IP
$ nslookup
> server 10.98.0.10
Default server: 10.98.0.10
Address: 10.98.0.10#53
> svc-headless.default.svc.k8s.org
Server: 10.98.0.10
Address: 10.98.0.10#53
Name: svc-headless.default.svc.k8s.org
Address: 10.244.1.35
Name: svc-headless.default.svc.k8s.org
Address: 10.244.2.35
Name: svc-headless.default.svc.k8s.org
Address: 10.244.2.36
> svc-cluster.default.svc.k8s.org
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: svc-cluster.default.svc.k8s.org
Address: 10.98.0.3
> exit
nslookup
,設定 DNS server
是 10.98.0.10
(Kube-dns) 後,來檢視我們建立的服務 dns 資訊svc-headless.default.svc.k8s.org
服務名稱.服務所在的namespace.svc.k8s.org
svc
,代表是 service 的紀錄k8s.org
,是我們在 init K8S 的時候設定的A record
,由一個 Name
+ 一個 IP Address
組成> kube-dns.kube-system.svc.k8s.org
Server: 10.98.0.10
Address: 10.98.0.10#53
Name: kube-dns.kube-system.svc.k8s.org
Address: 10.98.0.10
$ kubectl run d1 --rm -it --image=quay.io/cloudwalker/alpine
/ # ping svc-cluster
PING svc-cluster (10.98.0.174): 56 data bytes
ping: permission denied (are you root?)
/ # ping svc-headless
PING svc-headless (10.244.1.6): 56 data bytes
ping: permission denied (are you root?)
/ # ping svc-headless
PING svc-headless (10.244.2.10): 56 data bytes
ping: permission denied (are you root?)
/ # ping svc-headless
PING svc-headless (10.244.1.6): 56 data bytes
ping: permission denied (are you root?)
/ # exit
ping svc-cluster
名稱就能解析出 IP 位址 ?$ kubectl run d1 --rm -it --image=quay.io/cloudwalker/alpine
If you don't see a command prompt, try pressing enter.
/ # cat /etc/resolv.conf
search default.svc.k8s.org svc.k8s.org k8s.org mcu.edu.tw
nameserver 10.98.0.10
options ndots:5
search default.svc.k8s.org svc.k8s.org k8s.org mcu.edu.tw
ping svc-cluster
時,會自動幫我們在名稱後面加上 search 後面的字串做收尋,如 : ping svc-cluster.default.svc.k8s.org
找不到時,會在換後面的字串 ping svc-cluster.svc.k8s.org
做收尋,依此類推,一直到最後一個字串,如果都沒有就表示無法解析建立 POD 之間的 DNS 名稱解析
$ nano ~/wulin/yaml/svcfqdn.yaml
apiVersion: v1
kind: Service
metadata:
name: dt
spec:
selector:
name: busybox
clusterIP: None
$ kubectl apply -f ~/wulin/yaml/svcfqdn.yaml
service/dt created
$ kubectl get svc dt
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dt ClusterIP None <none> <none> 23s
新增 Pod 的搜尋網域名
透過 nano ~/wulin/yaml/podfqdn.yaml
來編輯 yaml 檔
apiVersion: v1
kind: Pod
metadata:
name: b1
labels:
name: busybox
spec:
hostname: b1
subdomain: dt
containers:
- image: quay.io/cloudwalker/alpine
command:
- sleep
- "3600"
name: busybox
securityContext:
capabilities:
add: ["CAP_NET_RAW"]
---
apiVersion: v1
kind: Pod
metadata:
name: b2
labels:
name: busybox
spec:
hostname: b2
subdomain: dt
containers:
- image: quay.io/cloudwalker/alpine
imagePullPolicy: Always
command:
- sleep
- "3600"
name: busybox
securityContext:
capabilities:
add: ["CAP_NET_RAW"]
spec:
hostname: b1
subdomain: dt
dt
,就能被解析$ kubectl apply -f ~/wulin/yaml/podfqdn.yaml
$ kubectl exec -it b2 -- sh
/ # ping -c 1 b1.dt.default.svc.k8s.org
PING b1.dt.default.svc.k8s.org (10.244.2.8): 56 data bytes
.........
# 可解析簡易名稱
/ # ping -c 1 b1.dt
PING b1.dt (10.244.2.8): 56 data bytes
.........
/ # apk add nano; nano /etc/resolv.conf
search dt.default.svc.k8s.org default.svc.k8s.org svc.k8s.org localdomain
nameserver 10.98.0.10
options ndots:5
/ # ping -c 1 b1
PING b1 (10.244.2.8): 56 data bytes
.........
/ # exit
$ kubectl delete -f ~/wulin/yaml/podfqdn.yaml
$ nano ~/wulin/yaml/podfqdn.yaml
..............
spec:
hostname: b2
subdomain: dt
containers:
- image: quay.io/cloudwalker/alpine
imagePullPolicy: Always
command:
- sleep
- "3600"
name: busybox
securityContext:
capabilities:
add: ["CAP_NET_RAW"]
dnsConfig:
searches:
- dt.default.svc.k8s.org
$ kubectl apply -f ~/wulin/yaml/podfqdn.yaml
$ kubectl exec -it b2 -- sh
/ # cat /etc/resolv.conf
search default.svc.k8s.org svc.k8s.org k8s.org localdomain dt.default.svc.k8s.org
nameserver 10.98.0.10
options ndots:5
/ # ping -c 1 b1
PING b1 (10.244.2.3): 56 data bytes
64 bytes from 10.244.2.3: seq=0 ttl=62 time=0.590 ms
--- b1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.590/0.590/0.590 ms
/ # exit
建立 Kubernetes Job Controller
$ echo 'apiVersion: batch/v1
kind: Job
metadata:
name: job1
spec:
template:
spec:
containers:
- name: job
image: quay.io/cloudwalker/busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 30s; echo exiting...; date
restartPolicy: Never ' > ~/wulin/yaml/job01.yaml
$ ka -f yaml/job01.yaml
$ kg job
NAME COMPLETIONS DURATION AGE
job1 1/1 44s 5m26s
$ kubectl get pod -l=job-name=job1
NAME READY STATUS RESTARTS AGE
job1-9b8xz 0/1 Completed 0 119m
修改 yaml 檔
apiVersion: batch/v1
kind: Job
metadata:
name: job1
spec:
template:
spec:
containers:
- name: job
image: quay.io/cloudwalker/busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 30s; echo exiting...; date
restartPolicy: Always
apply 它
$ ka -f yaml/job01.yaml
The Job "job1" is invalid: spec.template.spec.restartPolicy: Required value: valid values: "OnFailure", "Never"
檢視 Kubernetes Job Controller
$ echo 'apiVersion: batch/v1
kind: Job
metadata:
name: job2
spec:
activeDeadlineSeconds: 5
template:
spec:
containers:
- name: job2
image: busybox
args:
- /bin/sh
- -c
- date; echo sleeping....; sleep 30s; echo exiting...; date
restartPolicy: Never ' > ~/wulin/yaml/job02.yaml
$ ka -f yaml/job02.yaml
activeDeadlineSeconds: 5
,設定 Job 只能跑 5 秒,時間到會停止系統工程
並切換成 root 使用者
May 27, 2025SUSE Edge 是專為解決在 edge 部署 infrastructure (基礎架構) 和 cloud-native applications (雲原生應用程式) 的獨特挑戰,而設計、緊密整合且經過全面驗證的端對端解決方案。
May 21, 2025https://tysonchen.me/debug-shell-script/
May 21, 2025apiserver 可以視為在 etcd 前面的一个代理(proxy)
May 17, 2025or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up