# Kubernetes
[TOC]


* Kubernetes是google公司做的。
* google 設計K8S就是為了管理大量的container。
> google為什麼要設計Kubernetes?
> 因為有了 contianer 的技術,就可以在一台實體電腦內,瞬間建立一堆 container
> 多個 container 的效能上還能保持在一定的水準上,但虛擬電腦對於實體電腦的資源耗用的遠比 container 多
>
> 當我們實體電腦數量很多,可以瞬間建立非常多的 container,就需要一套系統來做管理,也就是 Kubernetes,
> K8S 可以用於自動部署、維運、擴充和管理多台 container,管理資源或權限分配的狀況。
> 當我們要管理的主機有一定數量才會啟動 K8S。
## k8s功能
* k8s口訣隨需擴充:隨者企業應用系統的需要,它會自動的來擴充container
> 甚麼時候會需要用到隨需擴充?
> - 網站伺服器,遇到多人大量同時連線時會需要,例如 : 演唱會訂票系統, 台鐵在節慶時的訂票系統,疫情上報系統。
- Replication of components:叢集(分身的概念),同一個服務會有好幾個 pod 來提供服務,避免單點損毀的情況,提高可用性。
- Auto-scaling:自動橫向擴充,可以依照暴漲的流量擴充機器。
- Load balancing:系統平衡附載。
- Rolling updates:專門給程式設計師用的,因為程式設計師會經常更新程式。k8s會自動幫我們做進版跟退版(進版:會進到下一個版本,退版:他會回到上一個版本)。
- Logging across components:紀錄pod狀態。
- Monitoring and health checking:維運一堆container,所以要看logging監控每一個container的執行狀態(多少資源跟細項和用電量等等)(系統叫grafana儀錶板)。
- Service discovery:電腦很多,所以會提供很多功能,因此這套系統可以清楚地讓我們知道那些電腦提供哪些服務(由CoreDNS system得知)。
- Authentication:對操作者的身份確認,所有的身分認證都是用憑證(它的安全性跟ssh一樣);k8s裡面的電腦沒有人在打帳號跟密碼,都是憑證在作業。
> 問題:k8s為的是誰?
> 答:pod,pod就是一個application,是一家公司的應用系統,為了要讓我們的應用系統跑得長長久久,穩穩當當,因此k8s有提供這些的功能。
## k8s系統架構

### k8s master
* 要使用k8s一定要有一台master(m1)。
* API SERVER:負責接受前端使用者送過來的命令,然後叫Scheduler去執行,有CLI跟UI兩種模式。API 接受命令的同時會檢查對方的身分,跟他可做甚麼。
- Authentication: 身分認證,你是誰
- Authorization: 授權,你有什麼權限,可以做什麼事情
> CLI:命令模式,例如 : `kubectl` command的方式下達的命令。
> UI(web UI):以圖像介面點選指令,操控一套系統(RANCHER瀏覽器)。
> CLI 跟 UI都可以對API SERVER做下達指令。
* Scheduler:會分配container要在哪一台工作主機做執行,安排的方式不只看記憶體,使用複雜的演算法。找到要跑的主機後他就會通知kubelet這支程式,之後kubelet就會使用docker平台來做出container,但真正產生container的是runc/crun,kubelet負責做出pod。
* Controller-Manager:會負責照顧 node 裡面的 pod ,如果一台 node 裡面的 Container 掛了,它會去請 Scheduler 幫我們把 Container 建立回來,但是不一定會在原來的 node 把 Container 建立回來。他也會做Auto-scaling 自動擴增,隨者需要而擴增;需求如果降低,他就會把分身拿回來。
* 他會監控 node 裡的pod,如果有pod快負荷不了,他會幫pod再啟動一模一樣的分身,並且這個分身pod會跟原本的pod一起做事。
* Cloud Controller Manager: 負責與各家雲端商整合,允許將你的叢集連接到雲端的 API 之上。
* etcd:所有k8s系統架構資訊都會處存在這(pod放在哪個worker,pod叫什麼名稱)。
### Worker node 工作主機。
* k8s最小的執行單位叫做pod。pod就是我們要跑的應用系統。
* pod裡面放的就是container,裡面有幾台container不一定。
* kubelet會命令docker/CRI-O去做container,之後kubelet會把container包起來變成一個pod。
* kube-proxy:他就是負責讓不同工作實體主機pod跟pod網路能夠互通(kube-flannel他是一個daemon,就是他讓pod跟pod網路上可以溝通)。
> 問題:pod是不是一台電腦?
> 答:pod不是一台電腦,他是Solution,機櫃的概念,application的概念,container就是一台電腦,那pod裡面可以有很多container。例如:一個戶政系統裡面她會有很多台電腦去組成這個系統,那pod就是這個戶政系統。
> pod就是一個解決方案,一個應用。
但每個企業規模、機房、需求不同,就算是同個應用系統,也可能含不同數量的container。
## kubernetes Raw block volume CRI 的架構演進圖

* 第一代k8s:就是用docker做出container的,最後他是用runc/crun做出container。
* 第二代k8s:少了docker engine,是用containerd做出container,是用runc/crun做出container。
* 第三代k8s:處理container效能更好了,因為containerd自己內建一個可以呼叫他的介面(CRI Plugin),再透過runc/crun產生container。
* 第四代k8s:CRI-O就是產生container,專屬於k8s使用,效能一定大大提升,因為少了很多層介面,但他也還是一樣用runc/crun產生container。
> 目前台灣大部分的k8s雲端上使用的都是containerd(第三代)
## 公有雲k8s
* 只要你敢叫雲端商就要提供k8s給人家使用
1. Amazon Elastic Kubernetes service CRI: CNI: Amazon VPC CSI:Amazon EBS
2. Google Kubernetes Engine CRI: CNI: CSI:
3. Azure Kubernetes service CRI: CNI: CSI:

* 這整套系統是 microsoft Azure做的,只要有付錢都可以使用(Azure Kubernetes service)
* ingress:後端資料庫都可以使用
* prometheus:可以監控k8s pod的狀況
* CI/CD:專門給程式設計師用的
## 實作
在 Windows 系統的 cmd 視窗, 執行以下命令
```
$ ssh bigred@<alp.m1 IP>
$ sudo apk update; sudo apk add kubeadm kubelet kubectl --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/community/ --allow-untrusted
...........
(1/5) Installing socat (1.7.4.3-r0)
(2/5) Installing kubeadm (1.24.0-r2)
(3/5) Installing kubectl (1.24.0-r2)
(4/5) Installing kubelet (1.24.0-r2)
(5/5) Installing kubelet-openrc (1.24.0-r2)
Executing busybox-1.35.0-r13.trigger
OK: 1435 MiB in 297 packages
```
建立 K8S Master
```
$ echo $IP
192.168.61.4
##init 初始化設定
##--service-cidr k8s提供對外的服務,/24設定他的ip位址總共254個(最多254個服務)。
##--pod-network-cidr pod的ip位址範圍有6萬多個,只要是私有ip就可以
##--service-dns-domain 設定domain名稱,如果公司有domain name ,就設定公司的名稱
##--apiserver-advertise-address $IP 設定對外連接的窗口,對外的窗口一定是透過m1
$ 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
因 Kubelet 是 Daemon 不是 Pod, 需設定為系統自動啟動
$ sudo rc-update add kubelet default
##建立m1需要用到的images
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
k8s.gcr.io/kube-apiserver v1.23.5 3fc1d62d6587 3 weeks ago 137 MB
k8s.gcr.io/kube-proxy v1.23.5 3c53fa8541f9 3 weeks ago 114 MB
k8s.gcr.io/kube-controller-manager v1.23.5 b0c9e5e4dbb1 3 weeks ago 126 MB
k8s.gcr.io/kube-scheduler v1.23.5 884d49d6d8c9 3 weeks ago 54.8 MB
k8s.gcr.io/etcd 3.5.1-0 25f8c7f3da61 5 months ago 294 MB
k8s.gcr.io/coredns/coredns v1.8.6 a4ca41631cc7 6 months ago 47 MB
k8s.gcr.io/pause 3.6 6270bb605e12 7 months ago 690 kB
```
將 bigred 設成 K8S 管理者
```
##cp -i 覆蓋前會提示
##$(id -u) 當前使用者的uid
##$(id -g) 當前使用者的gid
$ mkdir -p $HOME/.kube; sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config; sudo chown $(id -u):$(id -g) $HOME/.kube/config
```
設定 K8S Master 可以執行 Pod,如果worker的pod快稱不住了,那m1這台電腦就會去幫他執行pod。
```
$ kubectl taint node m1 node-role.kubernetes.io/control-plane:NoSchedule-
```
k8s 的每個 node 都會有標記 taint ,而 m1 這台 node 就有 `NoSchedule` 的 taint ,有了這個 taint , pod 就不會建在 m1 的 node 上,這時候在規則裡面改成`NoSchedule-` , 就能破格。
> 問題:為什麼m1主機會有kubelet跟kube-proxy這2個程式?
> 答:因為我們有設定過K8S Master可以執行Pod
## 安裝 Flannel 網路套件(static route)
```
$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
```
[註] 因建立 K8S 設定 POD 的 Subnet 為 10.244.0.0/16, 而 flannel 內定網路 ID 為 10.244.0.0/16, 所以不需修改 kube-flannel.yml
`$ sudo reboot`
## 檢視 Kubernetes 系統
```
##檢視我現在有哪些node
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
m1 Ready control-plane,master 2m4s v1.23.5
##-n namespace ,這裡可以看到k8s的運作系統,裡面都是pod
##coredns 提供外部的dns
$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-78fcd69978-9hz84 1/1 Running 0 18m
coredns-78fcd69978-m7dfp 1/1 Running 0 18m
etcd-m1 1/1 Running 3 42m
kube-apiserver-m1 1/1 Running 3 42m
kube-controller-manager-m1 1/1 Running 3 42m
kube-flannel-ds-267gx 1/1 Running 0 5m37s
kube-proxy-k4ptl 1/1 Running 1 7m37s
kube-scheduler-m1 1/1 Running 3 42m
```
檢視m1目前跑的container
```
$ crictl ps -a
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
0e06d6f655ab7 a4ca41631cc7ac19ce1be3ebf0314ac5f47af7c711f17066006db82ee3b75b03 9 minutes ago Running coredns 1 6403b407c2c51 coredns-6d4b75cb6d-mn9pq
66aeda371eef9 a4ca41631cc7ac19ce1be3ebf0314ac5f47af7c711f17066006db82ee3b75b03 9 minutes ago Running coredns 1 e2250c5108060 coredns-6d4b75cb6d-mbtx7
............
```
## 開始建置 Kubernetes Worker
在 m1 的終端機, 執行以下命令
```
$ ssh w1 'sudo apk add kubeadm kubelet --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/community/ --allow-untrusted'
$ ssh w2 'sudo apk add kubeadm kubelet --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/community/ --allow-untrusted'
$ ssh w1 'sudo rc-update add kubelet default'; ssh w2 'sudo rc-update add kubelet default'
```
## w1, w2 加入 K8S Cluster
在 m1 的終端機, 執行以下命令
```
$ export JOIN=$(echo " sudo `kubeadm token create --print-join-command 2>/dev/null`")
$ echo $JOIN ##因為指令太長所以把這個指令放到這個變數
sudo kubeadm join 192.168.61.4:6443 --token 7q0zlv.lyhnh8xnlie6p6ui --discovery-token-ca-cert-hash sha256:da8ef2a7d20f81fe6ea8a90be944171493a4809f0aeaf5408ec84768b57ceaf6
$ ssh w1 "$JOIN"; ssh w2 "$JOIN"
$ ssh w1 sudo reboot; ssh w2 sudo reboot
```
## 設定 K8S Worker 標籤
在 m1 終端機, 執行以下命令
```
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
m1 Ready control-plane,master 76m v1.23.5
w1 Ready <none> 4m21s v1.23.5
w2 Ready <none> 79s v1.23.5
##貼上w1 w2的標籤,都是叫worker
$ kubectl label node w1 node-role.kubernetes.io/worker=; kubectl label node w2 node-role.kubernetes.io/worker=
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
m1 Ready control-plane,master 78m v1.23.5
w1 Ready worker 6m17s v1.23.5
w2 Ready worker 3m15s v1.23.5
```
## 建立 K8S POD
```
##kubectl run 代表產生pod;pod裡面一定有container,所以後面還是要放產生container的image
$ kubectl run nginx --image=quay.io/cloudwalker/nginx
pod/nginx created ##下面回覆說create但其實未必會成功
$ kubectl get pods ##ContainerCreating代表還在下載image
NAME READY STATUS RESTARTS AGE
nginx 0/1 ContainerCreating 0 8s
$ kubectl get pods ##一定要是Running才代表啟動成功
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 52s
$ kubectl get pods -o wide ##在w1這台機器上面跑
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATE
nginx 1/1 Running 0 74s 10.244.1.3 w1 <none> <none>
$ curl -s http://10.244.1.3 | grep 'Welcome'
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
$ kubectl delete pods nginx
```
再次建立 nginx POD
```
$ kubectl run nginx --image=quay.io/cloudwalker/nginx
$ kg pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 8h 10.244.1.3 w1 <none> <none>
##因為跑的image內定是sh貝殼程式,所以一定要有終端機
$ kubectl run a1 --rm -it --image=quay.io/cloudwalker/alpine
If you don't see a command prompt, try pressing enter.
/ # apk add curl &>/dev/null
/ # curl -s http://10.244.1.3 | grep 'Welcome'
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
/ # exit
Session ended, resume using 'kubectl attach a1 -c a1 -i -t' command when the pod is running
pod "a1" deleted
```

* 再叢集裡面都能夠互相連到。
* 在k8s外面的電腦是無法連到10.244.1.2的。

* windows 10 要可以連到nginx這個pod,一定要先連到vm,然後vm再連SRV,透過我們自己設定的IP(10.98.XX.XX)會在連到pod。
### 問題
```
$ kubectl run a2 --image=quay.io/cloudwalker/alpine ##無法running
先檢查pod狀態
$ kubectl describe pod a2
$ kubectl run a2 -it --image=quay.io/cloudwalker/alpine ##如果IMAGE內定跑的是貝殼程式,那就是一定要給終端機。
```
`$ kubectl exec -it a2 -- sh ##k8s進入到pod裡面的container`
## Kubernetes CRI 基礎運作
* crictl 沒有提供 build, save(備份image) 及 load(還原image) 功能,因此請了豆莢男人來幫他做image的備份與還原,但豆莢男人要能備份image一定讀得到crictl的image。
* crictl + crio 大約等於 podman
```
$ crictl --help | grep -A 20 COMMANDS:
COMMANDS:
attach Attach to a running container
create Create a new container
exec Run a command in a running container
version Display runtime version information
images, image, img List images
inspect Display the status of one or more containers
inspecti Return the status of one or more images
imagefsinfo Return image filesystem info
inspectp Display the status of one or more pods
logs Fetch the logs of a container
........
[註]
$ sudo podman --help | grep -E 'save|load' ##豆莢男人可以備分image跟還原image
load Load image(s) from a tar archive
save Save image(s) to an archive
```
Podman 與 CRIO Image 存儲目錄
* Podman 的 image 存儲目錄設定, 是參考 /etc/containers/storage.conf 這個設定檔, 設定檔內容如下 :
* 可以看到podman跟crictl的image都是存在`/var/lib/containers/storage`這個目錄
```
$ cat /etc/containers/storage.conf | grep -A 15 '\[storage\]'
[storage]
# Default Storage Driver, Must be set for proper operation.
driver = "overlay"
# Temporary storage location
runroot = "/run/containers/storage"
# Primary Read/Write location of container storage
graphroot = "/var/lib/containers/storage"
##檢查 cri-o 存的image位子
$ crio-status config
[crio]
root = "/var/lib/containers/storage" ##crio跟podman存image的位子相同
runroot = "/run/containers/storage"
.......
```
利用podman進行image的備份與還原
```
bigred@m1:~$ crictl images
IMAGE TAG IMAGE ID SIZE
docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin v1.1.0 fcecffc7ad4af 8.37MB
docker.io/rancher/mirrored-flannelcni-flannel v0.18.1 e237e85065092 63.3MB
bigred@m1:~$ sudo podman save docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0 > flannel-plu
gin.tar
##image後面要記得打版本
bigred@m1:~$ sudo podman save docker.io/rancher/mirrored-flannelcni-flannel:v0.18.1 > flannel.tar
bigred@m1:~$ sudo podman load < flannel-plugin.tar
##利用podman把tar還原成image
```
## 更改 k8s image 預設
```
$ sudo cat /etc/containers/registries.conf
......
unqualified-search-registries = ["quay.io"] # 改成 quay.io
......
```
## crun 與 runc 效能比較
* runc crun 大小比較
```
$ ls -lh /usr/bin/crun
-rwxr-xr-x 1 root root 383K Apr 28 06:20 /usr/bin/crun
$ ls -lh /usr/bin/runc
-rwxr-xr-x 1 root root 9.1M Aug 2 18:39 /usr/bin/runc
```
```
$ mkdir -p ~/wulin/rt/rootfs; cd ~/wulin/rt/rootfs; curl -o alpine.tar.gz http://dl-cdn.alpinelinux.org/alpine/v3.14/releases/x86_64/alpine-minirootfs-3.14.2-x86_64.tar.gz; tar xvf alpine.tar.gz ;rm alpine.tar.gz; cd ..
##因為等等要跑container預設都是root
$ sudo chown root:root -R rootfs/
$ runc spec
##{1..100} 代表產生100次,用runc產生container
$ time for i in {1..100}; do echo "exit" | sudo runc run b1 &>/dev/null; done
real 0m2.842s
user 0m1.157s
sys 0m1.297s
##用crun來測
$ time for i in {1..100}; do echo "exit" | sudo crun run b1 &>/dev/null; done
real 0m0.779s
user 0m0.460s
sys 0m0.129s
```
> 結論:crun比runc速度快了3倍,crun是c語言寫的,runc是go語言寫的
## 設定 CRI-O 執行 crun
* 在 m1 執行以下命令
* cri-o內定的命令是crun
* crun是豆莢男人幫他裝進來的
* CRI-O 內定使用 runc 建 Container,但 crun 比較快,所以修改 CRI-O 設定檔來使用 crun,代表最底層 container 的執行從 runc 改成 crun。
* 為什麼有 crun 可以用?因為有安裝 podman,podman 內定使用 crun 建 Container。
```
$ sudo nano /etc/crio/crio.conf
[crio.runtime]
# Overide defaults to not use systemd cgroups.
conmon_cgroup = "pod"
cgroup_manager = "cgroupfs"
default_runtime = "crun"
[crio.runtime.runtimes.crun]
runtime_path = "/usr/bin/crun"
runtime_type = "oci"
runtime_root = ""
[crio.network]
network_dir = "/etc/cni/net.d/"
plugin_dir = "/opt/cni/bin"
$ exit
```
* 在 w1 及 w2 也需執行以上設定
## Flannel CNI

* cni0就是虛擬橋接器,讓同個網段的pod可以互相溝通。
* 不同實體主機的pod要互相溝通,就是利用flannel,他就是一個路由器,讓不同往段的pod可以互聯。
```
$ ifconfig
cni0 Link encap:Ethernet HWaddr 7E:54:2B:28:DC:34
inet addr:10.244.0.1 Bcast:10.244.0.255 Mask:255.255.255.0
.......
eth0 Link encap:Ethernet HWaddr 00:50:56:AB:00:04
inet addr:192.168.61.4 Bcast:0.0.0.0 Mask:255.255.255.0
.......
flannel.1 Link encap:Ethernet HWaddr BE:90:42:CC:9D:70
inet addr:10.244.0.0 Bcast:0.0.0.0 Mask:255.255.255.255
......
$ brctl show
bridge name bridge id STP enabled interfaces
cni0 8000.7e542b28dc34 no veth3a93d696
vetha16fdc4d
```
```
##flanneld 他是一個deamon,這個process就是一個路由器,就是他讓pod可以互通
$ ps aux | grep flannel
root 4421 0.0 0.4 1334924 36900 ? Ssl 14:51 0:03 /opt/bin/flanneld --ip-masq --kube-subnet-mgr
```
## Kubernetes Cluster 核心運作

* CRI(container runtime interface)標準:docker、cri-o,幫K8S建立container的標準
- cri-o的出生為的就是k8s,並且也跟著k8s制定的CRI(container runtime interface)標準。他可以選擇多種 container runtime(runc/crun),相容度高,效能也是最好,因為要溝通的介面最少。
* CNI(container network interface)標準:flanner(靜態路由),幫K8S建立大型的網路標準
- 讓不同 node 中的 pod 網路可以互通有無,還有讓別人可以透過網路連接到我們建立對外的 Service
* CSI(container storage interface)標準:ceph、emc、nfs(network file system,他可以把linux的資料夾透過網路跟別人分享),硬體储存系統
- 其實講的就是 Volume , by pass overlay2 ,讓pod的資料可以永存
> K8S可以根據不同標準做出不同架構的Kubernetes
## Kubernetes Pods (CRI)

* 一個pod不可能同時跨兩個主機(worker)。
* Scale Out(水平擴充):創造分身。
* CRI就是創造POD,所以他要知道硬體的資源的多少,一個POD要多少CPU、記憶體要多少,他都要了解。
## Kubernetes Pods (CNI)

* 在K8S的同一個POD裡面所有的container都可以透過localhost互通有無,container對外溝通都共用同一個ip。
* 不同pod之間裡面的container不可能使用localhost互相溝通。
* 同一個pod裡面的container都是共用同一個ip,同一個網路卡,並且container跟container使用localhost溝通並不會斷線,因為他是透過記憶體的方式溝通。
## Kubernetes Pods (CSI)

* 他複雜程度相當的高,上圖只是其中一個架構
* pv:他配置在每一個pod裡面,永存的储存區塊,透過網路他可以储存在外部的NFS檔案系統。
* NFS(Network File System):共享資料夾的概念,pod可以把重要的資訊都集中存放在這裡,例如:qnap(網路儲存設備)
## Kubernetes Pods (Scheduling)

* pod可以透過標籤,來做出很多的實務應用
## Kubernetes Pods (High Availability)

* 本尊分身,k8s如果主機不夠力了,但硬體也不夠,k8s可以做出硬體的擴充
* 一個pod可以有本尊跟分身,並且同時作業,如果一個pod掛了另外一個還可以繼續作業,達到服務不中斷,簡稱HA。
## Kubernetes Master 容錯
* etcd: 透過多節點的 etcd 實例組成叢集,並利用 Raft 演算法,來選取一個領導者 (Leader) 處理需要叢集共識的所有客戶端的請求 (Request),如下圖所示。另外由於 Raft 演算法關析,還需要注意叢集的故障容許度(Failure Tolerance), 計算故障容許節點數為 (N-1)/2,其中 N 為 K8S Master 數量, 而由 (N/2)+1 計算出 Majority, 只要滿足這數量, 代表 K8S 可以新增 Service, Deployment 等功能。

* 在 Kubernetes Master 裡面的 etcd 儲存整個 K8s 的 meta data , 只要 Master 這台主機掛了, 整個 k8s 再見,所以要做容錯。
* 多台 Master 主機的 etcd 會自動同步。
* 根據 Raft 演算法可以算出如何做到 Master 主機的容錯。
* 現在有3台master主機,如果壞了一台k8s還能正常運作,但是如果再壞了一台k8s就會進入安全模式。
* 如果不符合majority,k8s就會進入安全模式,Application 還可以繼續 run ,但是不能新增、刪除、修改。
> 問題:如果現在公司只有三台電腦給你使用,你要怎麼分配建立k8s系統?
> 答:三台都建成master主機,並且讓這些master主機
> 兼者做woker,讓他們也可以去執行pod
* 當 K8s Master 容錯的 Raft 演算法中只剩下一個 Master 節點時,該節點上運行的 kube-apiserver 將無法對 etcd 進行寫入操作,而該操作是Kubernetes 控制平面的核心部分。因此,在只剩下一個 Master 節點的情況下,kubectl 命令將無法對 Kubernetes 集群進行控制和管理。
* 該節點的 kube-apiserver 將停止對 API 請求的回應,而 kube-controller-manager 和 kube-scheduler 等控制器將無法執行其功能。
## Docker Image 設計與建立
* k8s要run起來,貨櫃化一定要發生,貨櫃要要發生定要有image
在 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
```
## 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
```
## mysql
```
##做image有兩種格式一個是oci 一個是docker
##現在這個WARN是 oci 不支援健康檢查
##健康檢查就是由image做出來的container有沒有正常,我們可以去判讀
$ sudo podman build -t mydb .
WARN[0000] HEALTHCHECK is not supported for OCI image format and will be ignored. Must use `docker` format
```
## 第一次接觸 Kubernetes App
### 建立 K8S 應用物件方式
1. kubectl run (命令式 Imperative) - Manage K8s object (POD, Controller, Service...) using CLI
- kubectl run 對於產生k8s的物件有限,並不是所有物件都能透過他來產生
2. kubectl create/apply (聲明式 Declarative) - By defining K8s objects in yaml file
- 因為很容易有人為錯誤,因此使用yaml可以有效地降低人為錯誤
- yaml全名:Yet Another Markup Language(另一種標記語言)
## Using Namespace
* k8s namespace 跟 linux namespace是不同的東西。
* k8s就像是一個辦公室,裡面可以放很多物件。
Namespaces 有以下幾個特點
1. 在同一個 Kubernetes Cluster 中,每個 Namespaces 的名稱都是要獨特的,不能重複
2. 當一個 Namespaces 被刪除時,在該 Namespace 裡的所有物件也會被刪除
3. 可以透過 Resource Quotas 限制一個 Namespaces 所可以存取的資源
## Assigning Pods to Namespace
```
$ mkdir -p ~/wulin/yaml; cd ~/wulin
$ kubectl create namespace myoffice
namespace/myoffice created
$ kubectl get namespace
NAME STATUS AGE
default Active 2d
kube-flannel Active 2d
kube-node-lease Active 2d
kube-public Active 2d
kube-system Active 2d
myoffice Active 17s
```
* 在myoffice這個namespace裡面建立一個pod叫做n1,container的image是`quay.io/cloudwalker/alpine` ,`-- sleep infinity` 指定這個 pod 裡的 container 執行的 process 是 sleep infinity
* `--` 後面放的是pod要執行的命令
```
$ kubectl run n1 --image=quay.io/cloudwalker/alpine -n myoffice -- sleep infinity
pod/n1 created
$ kubectl get pods -n myoffice
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 13s
$ kubectl exec -it n1 sh -n myoffice
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # exit
$ 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?)
/ # exit
```
```
$ 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 :這個錯誤訊息是在default這個namespace找不到n1這個pod,因為我們建的pod是在myoffice這個namespace
```
```
$ kubectl run n1 --image=quay.io/cloudwalker/alpine -n myoffice -- sleep infinity
pod/n1 created
$ kubectl get pods -n myoffice
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 13s
$ kubectl exec -it n1 sh -n myoffice
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # exit
$ kubectl exec -it -n myoffice n1 -- sh
##這邊ping沒通是因為被限制住了,並不是網路沒通,網路是有通的(因為有抓到ip)
/ # ping -c 1 www.hinet.net
PING www.hinet.net (163.28.83.113): 56 data bytes
ping: permission denied (are you root?)
/ # exit
```
```
$ echo 'apiVersion: v1
kind: Pod
metadata: ##專門放重要資訊
name: n2 ##pod名稱
namespace: myoffice ##pod在myoffice這個namespace產
labels: ##貼上標籤,指定n2
name: n2
spec: ##規格,pod內部結構
hostname: n2 ##container主機名稱叫n2
containers:
- image: quay.io/cloudwalker/busybox ##container是由這個image做出來的
command:
- sleep ##等等要run的程式
- "60" ##container名稱叫n2
name: n2 ' > ~/wulin/yaml/pod-n2.yaml
$ kubectl apply -f ~/wulin/yaml/pod-n2.yaml
```
* 切換 Namespace
```
$ kubectl get pods -n myoffice
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 17m
n2 1/1 Running 0 26s
##set-context 入口的意思,進入商業辦公大樓的大門
##--current 入口指定預設的入口,因為進入的方法有很多種,我們就走預設的入口
##--namespace=myoffice 進到myoffice這個namespace
$ kubectl config set-context --current --namespace=myoffice
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 25m
n2 1/1 Running 5 (105s ago) 8m32s
##~/.kube/config 裡面放的是通行證,bigred帳號裡面有這個檔案,裡面放的就是bigred自己進入k8s的通行證
##一進到k8s商業大樓會先到櫃檯確認你有沒有邀請函,確認好後確認你的識別證,在從指定的商業大樓入口進入,最後到你能夠進入的辦公室
$ cat ~/.kube/config
.......
server: https://192.168.61.4:6443 ##換證件(憑證)的櫃檯,也是地址
name: kubernetes
contexts:
- context:
cluster: kubernetes ##商業大樓
namespace: myoffice ##辦公室名字
user: kubernetes-admin ##這個憑證的使用者是kubernetes-admin,bigred在k8s運做使用者就是他
name: kubernetes-admin@kubernetes ##k8s大樓入口名字
current-context: kubernetes-admin@kubernetes
```
## 建立 Demo App
```
$ kubectl run demo --image=alp.sshd
[註] 從 1.18 這個版本開始, 上面命令只會產生 pod, 不再會產生 deployment object 及 replicaset controller.
##這邊會噴錯是因為demo這個pod是建在w1上的,但是w1上沒image,所以噴說找不到image
$ 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>
nginx 1/1 Running 0 9h 10.244.1.3 w1 <none> <none>
##刪除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
```
把image備份到其他worker
```
##把image存到一個打包檔(image備分)
$ sudo podman save alp.sshd > alp.sshd.tar
$ scp alp.sshd.tar w1:alp.sshd.tar; scp alp.sshd.tar w2:alp.sshd.tar
$ ssh w1 'sudo podman load < alp.sshd.tar'
$ ssh w2 'sudo podman load < alp.sshd.tar'
##--image-pull-policy IfNotPresent 如果在本機找不到image就到網路上下載
##kubectl run 內定就是IfNotPresent
##如果image沒打完整他就會上網下載,但如果網路上沒有那就會失敗
$ kubectl run demo --image-pull-policy IfNotPresent --image=localhost/alp.sshd
pod/demo created
$ 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>
```

* 如果發現本地端沒有image那他就會到docker.io去下載,因為image是我們自己做的,所以docker.io不可能會有。
## 檢視 Linux capabilities
* linux的capabilities主要是在規範container可以做甚麼事,container被規範等同pod被規範
* 是cri-o規範container可以有什麼能力
```
##NET_BIND_SERVICE container裡面的程式,如果他要有對外上網的能力,一定要有這個capabilities
##"AUDIT_WRITE", "SYS_CHROOT" 如果要使用ssh連線一定要有這兩個capabilities
$ crio-status config
.....
[crio.runtime]
seccomp_use_default_when_empty = true
no_pivot = false
selinux = false
log_to_journald = false
drop_infra_ctr = true
read_only = false
hooks_dir = ["/usr/share/containers/oci/hooks.d"]
default_capabilities = ["CHOWN", "DAC_OVERRIDE", "FSETID", "FOWNER", "SETGID", "SETUID", "SETPCAP", "NET_BIND_SERVICE", "AUDIT_WRITE", "SYS_CHROOT", "KILL"]
.......
```
## 設定 Linux capabilities
* 要在 K8S Cluster 中所有主機的 crio.conf 增加 "AUDIT_WRITE", "SYS_CHROOT" 這二個 capabilities, 才可連接 Container 中的 OpenSSH Server, 設定後要記得重新開機
```
##cri-o內定的capabilities是沒有"AUDIT_WRITE","SYS_CHROOT",這兩個的capabilities,所以如果要使用ssh連線一定要加他
$ sudo nano /etc/crio/crio.conf
[crio.runtime]
.......
default_capabilities = [
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"SETGID",
"SETUID",
"SETPCAP",
"NET_BIND_SERVICE",
"AUDIT_WRITE",
"SYS_CHROOT",
"KILL"
]
.......
```
在k8s對pod下命令後面規定格式一定是打 `--`
```
##安裝 libcap 讓他處理capabilities
##如果這邊安裝失敗,有可能是dns的問題,進到pod裡面的container再增加8.8.8.8這個dns
$ kubectl exec demo -- sudo apk add libcap
......
(1/1) Installing libcap (2.46-r0)
Executing busybox-1.32.1-r8.trigger
OK: 61 MiB in 74 packages
##檢視container現在有什麼capabilities
$ kubectl exec demo -- sudo capsh --print
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
..........
```
## 連接與移除 Demo App

K8S 叢集內連接 demo app
```
##這裡一定是網路連進去的(跨主機連線),這裡傳送的封包全程加密,tls(網路加密)
$ kubectl exec -it pods/demo -- bash
bash-5.1# exit
##也可以使用ssh連進去,因為有給她ssh的兩個capabilities
$ ssh bigred@10.244.2.5
```
K8S 叢集外連接 demo app
```
##在這個pod開啟一個對外port
$ kubectl port-forward --address $IP pods/demo 22100:22 &
[1] 27780
[註] --address 只能指定 Master 的 IP 位址
```
在 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
demo:~$ exit
```
移除 pod
`$ kubectl delete pod demo`
## YAML File
```
##apiVersion 他會送給api server,api server看到這個格式的版本是v1,他會問自己認不認識這個版本
##kind: Pod 我要產生什麼物件,現在是要產生pod
##name: pod-sshd pod名稱
##pod裡面只有一個container,名稱叫mysshd
##imagePullPolicy: IfNotPresent image如果本機沒有就上網下載
$ 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
##-f file 後面一定要給yaml檔
$ kubectl apply -f sshd/pod-sshd.yaml
$ kg pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-sshd 1/1 Running 0 10s 10.244.2.5 w2 <none> <none>
```
連進pod
```
$ ssh bigred@10.244.2.5
Warning: Permanently added '10.244.2.5' (RSA) to the list of known hosts.
bigred@10.244.2.5's password: bigred
Welcome to ALP sshd 6000
pod-sshd:~$ exit
$ kubectl delete -f sshd/pod-sshd.yaml
```
再次編寫yaml
```
##command 可以把image裡面內定的程式換掉,換成我container要執行的一段程式
##alp.sshd這個image裡面有宣告ENTRYPOINT ["/usr/sbin/sshd"],但是在k8s這裡卻又可以在宣告我要跑的命令
##在k8s的yaml可以宣告等等container要跑什麼命令,就算有宣告ENTRYPOINT也沒有用
##-c 是給貝殼程式用的,跟貝殼程式講後面有命令要去執行
##| 帶表我下面要跑的一段命令,有|帶表下面輸入什麼文字,格式都會保留下來。
##/usr/sbin/sshd -D 啟動openssh server,-D 在前景執行
$ 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
$ kubectl apply -f sshd/pod-sshd.yaml
```
> 問題:在k8s系統裡,在pod產生資料,如果沒有動用volume,那pod重啟(浴火重生)資料就會不見了。
## access Kubernetes Pods from outside

* pod在node主機上開一個對應的port
* hostPort是由kube-proxy做的
```
##containerPort: 22 ,openssh 開22port
##hostPort: 22101 ,在node主機上再開一個port,對應container 22port。
##指定在w1產生
$ 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
```
## 建立 File Browser Server
```
$ cd ~/wulin/
##--address "" 代表不管誰從哪裡連都可以
##--port 4000 網站開一個4000port
##--root="/srv" 網站存的內容都會存在這個目錄
##curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash 到網站下載bash script,並且用水管丟到bash直接執行,執行結果就會幫你把filebrowser命令下載下來
##--address "" 允許internet上面任何人可以來連她
##--perm.admin=true 管理權限,並且權限最大
$ 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"]
$ sudo podman build -t alp.myfbs fbs/
$ sudo podman images | grep fbs
localhost/alp.myfbs latest 24d1b68d43f8 About a minute ago 74.3 MB
```
備份 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'
```
編寫fbs yaml檔
```
##containerPort port剛剛image宣告4000,所以這裡也要宣告4000
##labels: 幫pod貼上標籤
$ 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: Never
ports:
- containerPort: 4000
$ kubectl apply -f fbs/pod-fbs.yaml
##檢測網站有沒有架起來
$ nc -w 1 10.244.1.6 4000;echo $?
```
```
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-fbs 1/1 Running 0 13s 10.244.0.41 w1 <none> <none>
##service 就是選到有對應標籤的 pod
##kind: Service 要讓外面的人可以使用,要再產生k8s service
##selector 選擇器,選擇有貼name: pod-fbs這個標籤的pod
##port: 8080 這是這個服務對外的port
##targetPort: 4000 目標端口,這是pod的port
##externalIPs: 外面連進來要輸入的ip,是m1,並且port也是8080
##就算pod浴火重生ip換了service還是會抓到他
$ 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
$ kubectl apply -f fbs/svc-fbs.yaml
```
檢視所有k8s所有物件資訊
```
$ kg all
NAME READY STATUS RESTARTS AGE
pod/pod-fbs 1/1 Running 0 61s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.98.0.1 <none> 443/TCP 5h26m
service/svc-fbs ClusterIP 10.98.0.67 192.168.61.4 8080/TCP 19s
```
## 設定 hostPath PV
```
##volumes: 建立一個永存區
##path: /tmp 储存的資料都放在worker這個目錄區
##mountPath: /srv 把container裡面的srv目錄區存的資料,都掛載到node主機的 /tmp裡面
##volumeMounts: 創造volume,並且把它掛載到/srv
$ 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: Never
ports:
- containerPort: 4000
volumeMounts:
- mountPath: /srv
name: test-volume
volumes:
- name: test-volume
hostPath:
# directory location on host
path: /tmp
```
> 問題:如果這時把pod刪除,在重建一次pod,那他原本存的資料還會再嗎?
> 答:不一定,因為資料是存在worker,因為如果pod不是重建在原本的那台worker,那她就會找不到資料。
處理辦法:
方法一:資料在w2產生,如果需要使用w2上的資料的pod,他以後重建都只會在w2
方法二:pod使用nfs做資料永存
## 認識 K8S 叢集物件

* 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
```
## 建立 Kubernetes POD

## 認識 Kubernetes Pod
* Pods are not intended to live long.
- pod 很容易領便當
* k8s 內定如果node機器重開,都會浴火重生(原本的被刪掉,再重新建立一個pod)
* This group of containers would share storage, Linux namespaces, cgroups, IP addresses.
- 同一個群組的pod,他們共用storage,cgroups,共用同一組ip
## Single Container Pod 建立與連接
```
$ kubectl run a1 --image-pull-policy IfNotPresent --image=quay.io/cloudwalker/alpine.derby
pod/a1 created
[註] 從 1.18 這個版本開始, 上面命令只會產生 pod, 不再會產生 deployment object 及 replicaset controller.
$ kg pods | grep a1
a1 1/1 Running 0 41s
##這個變數直接抓到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
```
## Single Container Pod 自動重新建立
```
##sleep 15 檢查15秒後pod會不會重建
$ kubectl run a1 --image=quay.io/cloudwalker/alpine -- sleep 15
pod/a1 created
##--watch 可以一路監控pod的狀態
$ kubectl get pods a1 --watch
NAME READY STATUS RESTARTS AGE
a1 1/1 Running 1 (18s ago) 43s
a1 0/1 Completed 1 (19s ago) 44s
a1 0/1 CrashLoopBackOff 1 (16s ago) 59s
a1 1/1 Running 2 (20s ago) 63s
a1 0/1 Completed 2 (35s ago) 78s
a1 0/1 CrashLoopBackOff 2 (15s ago) 92s
^C
* 重啟 6 次後, 會將重啟時間拉長, 繼續重啟. 這個 POD 有重建, 可是 IP 位址沒有變
$ kubectl delete pod a1
deployment.extensions "a1" deleted
```
## K8S Node 系統重新啟動
```
$ kubectl run a1 --image=quay.io/ict39/alpine.derby
pod/a1 created
$ kubectl get pod a1 -o wide | grep a1
a1 1/1 Running 0 36s 10.244.2.10 w2 <none> <none>
$ ssh w2 sudo reboot
$ kubectl get pod a1 -o wide | grep a1
a1 0/1 ContainerCreating 1 2m23s <none> w2 <none> <none>
$ kubectl get pod a1 -o wide | grep a1
a1 1/1 Running 1 2m35s 10.244.2.11 w2 <none> <none>
* IP 位址會改變, 這是因爲產生一個新 Pod
$ kubectl delete pod a1
```
## Single Container Pod 永不重啟
```
##--restart='Never' 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
```
## 佈署 Deployment Object

1. Deploy object
- 他是一個控制的資訊區塊,存在etcd,這個控制區塊裡面會記錄他等一下要控制的replicaset。
- replicaset這個功能會幫我們產生等等要執行的pod,並且他可以同時產生大量的pod。
2. Replicaset controller
- 如果覺得pod不夠了,他就會去擴增pod(本尊跟分身,代表pod一定是一樣的。
- pod如果附載太重,狀態的部分可以改成離線狀態,處理完手上的事情再回來。
- 主要目的是服務流量分散。
- ReplicaSet 有自我療傷的功能,如果pod被刪除,他會把pod在產生回來。
3. Deployment 管理 ReplicaSet , ReplicaSet 管理 Pods。
* 撰寫 Depolyment Object 部署檔案
```
##replicas: 2 你等等要產生兩個pod
##template: 代表pod的宣告
##這個pod的標籤是app: deppod
##這個pod只有一個container
##tty: true 虛擬終端機,因為image內定執行命令是貝殼程式
##selector 選擇要對應的標籤
##pod的名字是由replicaset決定的
$ echo 'apiVersion: apps/v1
kind: Deployment
metadata:
name: depobj
labels:
app: deppod
spec:
replicas: 2
selector:
matchLabels:
app: deppod
template:
metadata:
labels:
app: deppod
spec:
containers:
- name: myalpine
image: quay.io/ict39/alpine
imagePullPolicy: IfNotPresent
tty: true '> ~/wulin/yaml/depobj.yaml
```
* 建立與檢視 Depolyment Object
```
$ kubectl apply -f ~/wulin/yaml/depobj.yaml
deployment.apps/depobj created
$ kubectl get all --selector app=deppod
NAME READY STATUS RESTARTS AGE
pod/depobj-54dc9ff76b-57fpj 1/1 Running 0 7s
pod/depobj-54dc9ff76b-l22pq 1/1 Running 0 7s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/depobj 2/2 2 2 7s
NAME DESIRED CURRENT READY AGE
replicaset.apps/depobj-54dc9ff76b 2 2 2 7s
```
* 測試 ReplicaSets Controller
```
##只要pod不見資料不會不見,因為我們一定會啟動pv(hostpath)
$ kubectl delete pods --selector app=deppod
pod "depobj-54dc9ff76b-57fpj" deleted
pod "depobj-54dc9ff76b-l22pq" deleted
ReplicaSets Controller 會一直執行 reconciliation loop 程序, 確保 Deployment Object 的 actual state 與 desired state 一致. 上面命令刪除二個 POD, 這時 reconciliation loop 程序會再生成二個新的 POD, 由以下命令得知
$ kubectl get pods --selector app=deppod
NAME READY STATUS RESTARTS AGE
depobj-54dc9ff76b-bkrhx 1/1 Running 0 44s
depobj-54dc9ff76b-m6zmq 1/1 Running 0 44s
```
* 修改 Depolyment Object 部署檔案
```
##修改同一個depobj.yaml檔,並沒有刪除deployment
##由此可知,我們可以動態的修改yaml
$ echo 'apiVersion: apps/v1
kind: Deployment
metadata:
name: depobj
labels:
app: deppod
spec:
replicas: 1
selector:
matchLabels:
app: deppod
template:
metadata:
labels:
app: deppod
spec:
containers:
- name: myderby
image: quay.io/ict39/alpine.derby
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8888 '> ~/wulin/yaml/depobj.yaml
```
* 部署修改後 Depolyment Object 部署檔
```
$ kubectl apply -f ~/wulin/yaml/depobj.yaml
$ kubectl get all --selector app=deppod
NAME READY STATUS RESTARTS AGE
pod/depobj-584bfd5f59-mhthm 1/1 Terminating 0 2m18s
pod/depobj-584bfd5f59-vwc7w 1/1 Terminating 0 2m18s
pod/depobj-8447fdd678-rrnqk 1/1 Running 0 25s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/depobj 1/1 1 1 2m41s
##7cb97745c,這是前一個replicaset,他要管的對象已經不在了,但他還殘留在這裡
NAME DESIRED CURRENT READY AGE
replicaset.apps/depobj-7cb97745c 0 0 0 2m41s
replicaset.apps/depobj-8447fdd678 1 1 1 25s
##手動刪除replicaset
$ kd replicaset.apps/depobj-7cb97745c
replicaset.apps "depobj-7cb97745c" deleted
```
* Updating a Deployment(進版)
```
##把nginx版本改為1.21版
$ kubectl set image deployment.v1.apps/depng nginx=quay.io/cloudwalker/nginx:1.21 --record
deployment.apps/depng image updated
$ kubectl rollout status deployment.v1.apps/depng
Waiting for deployment "depng" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "depng" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "depng" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "depng" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "depng" rollout to finish: 1 old replicas are pending termination...
deployment "depng" successfully rolled out
$ kubectl describe deployments depng
Name: dep1
............
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: quay.io/cloudwalker/nginx:1.21
Port: 80/TCP
Host Port: 0/TCP
```
* Rolling Back a Deployment(退版,會退回到你進版前的版本)
```
$ kubectl rollout undo deployment.v1.apps/depng --to-revision=1
$ kubectl describe deployment depng
........
Containers:
nginx:
Image: quay.io/cloudwalker/nginx:1.20
Port: 80/TCP
Host Port: 0/TCP
```
* Scaling a Deployment
- 手動scale pod 數量
```
$ kubectl scale deployment.v1.apps/depng --replicas=3
$ kubectl get deployment depng
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
dep1 3 3 3 3 46m
$ kubectl get pods | grep depng
dep1-66f7f56f56-2rf8p 1/1 Running 0 39s
dep1-66f7f56f56-bggcn 1/1 Running 0 71s
dep1-66f7f56f56-mn589 1/1 Running 0 69s
$ kubectl delete deployment depng
```
## 安裝 Metrics Server
```
$ wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
##- --kubelet-insecure-tls加這行是告訴kubelet不要安全模式,告訴kubelet封包不要加密。
$ nano components.yaml
..........
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls # 新增這行
image: k8s.gcr.io/metrics-server/metrics-server:v0.6.1
imagePullPolicy: IfNotPresent
$ kubectl apply -f components.yaml
```
* 檢視 Worker Node 資源使用狀態
```
##cpu 的m,單位是milliCPU,1 core = 1000m
##Metrics Server 每15秒做一次檢測
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
m1 84m 4% 603Mi 7%
w1 26m 1% 496Mi 6%
w2 27m 1% 459Mi 5%
```
## Rsource Requests & Limits
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: podres
namespace: default
spec:
containers:
- name: podres
image: quay.io/cloudwalker/alpine
command: ["/usr/bin/yes"]
resources:
requests:
cpu: 100m
memory: 640M
limits:
cpu: 500m'> ~/wulin/yaml/resource.yaml
$ kubectl apply -f ~/wulin/yaml/resource.yaml
```
```
$ kg all -o wide | grep podres
pod/podres 1/1 Running 0 4m32s 10.244.1.8 w1 <none> <none>
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
m1 71m 3% 621Mi 7%
w1 550m 27% 509Mi 6%
w2 19m 0% 463Mi 5%
$ kd pod/podres
```
## Horizontal Pod Autoscaler

* hpa會監控pod的資源使用率,如果使用率到了規定量,它就自動幫你的pod autoscaler。
* hpa他可以做到pod如果不夠力就會產生相同的pod,但是非常講究硬體資源,硬體資源不夠產生出來的pod就會pending
* 建立 Horizontal Pod Autoscaler
```
$ mkdir ~/wulin/hpa; cd ~/wulin/hpa
##limits 這邊限制這個pod cpu 最多使用1 core
$ nano hpa-dep.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: hpa-dep
spec:
replicas: 2
selector:
matchLabels:
app: hpa.pod
template:
metadata:
labels:
app: hpa.pod
spec:
containers:
- name: alp
image: quay.io/cloudwalker/alpine
imagePullPolicy: IfNotPresent
tty: true
resources:
limits:
cpu: 1
```
> [註]
> 單位 m 指的是 milli-cores,每 1000m = 1 vCore
> 設定可以用 m 或分數,例如:
> 設定 0.5 = 500m
> 設定 300m = 0.3
```
##HorizontalPodAutoscaler(hpa)
##scaleTargetRef 宣告hpa來監控Deployment裡面的pod
##targetCPUUtilizationPercentage: 30 cpu如果超出30%就產生pod
##minReplicas: 2 最少兩個pod
##maxReplicas: 5 最多到五個pod
$ nano hpa-sp.yml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: hpa-sp
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hpa-dep
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 30
```
> [註] 1. 設定 30% 代表 HPA 將會維持 Pod 的平均 CPU 使用率為 30 %, 只要超出 30%, 即會自動擴展
* 建立與檢測 Horizontal Pod Autoscaler
```
$ kubectl apply -f .
deployment.apps/hpa-dep created
horizontalpodautoscaler.autoscaling/hpa-sp created
##REPLICAS 目前只有兩個pod
$ kg hpa --watch
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-sp Deployment/hpa-dep <unknown>/30% 2 5 2 30s
hpa-sp Deployment/hpa-dep 0%/30% 2 5 2 30s
```
```
開啟新的 命令提示字元 視窗
$ ssh bigred@192.168.61.4
$ kg pod
NAME READY STATUS RESTARTS AGE
hpa-dep-7c4798d956-5kv8t 1/1 Running 0 24s
hpa-dep-7c4798d956-b97hr 1/1 Running 0 24s
##讓其中一個pod去做cpu壓力測試,是否會自動做出擴充
$ kubectl exec hpa-dep-765c997fc8-wvc72 -- timeout 240 yes >/dev/null &
```
* 擴充3個pod
```
回到 監視 HPA 視窗, 檢視資訊
##自動擴充的pod未必會成功,如果你的硬體資源不夠,那生出來的pod也都只會pending,所以要能做到隨需擴增,硬體設備也要夠力
$ kg hpa --watch
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-sp Deployment/hpa-dep <unknown>/40% 2 5 2 37s
hpa-sp Deployment/hpa-dep 18%/30% 2 5 2 45s
hpa-sp Deployment/hpa-dep 39%/30% 2 5 2 60s
hpa-sp Deployment/hpa-dep 38%/30% 2 5 3 75s
hpa-sp Deployment/hpa-dep 32%/30% 2 5 3 90s
hpa-sp Deployment/hpa-dep 28%/30% 2 5 3 105s
```
### bobowas 系統
- 我們產生的 Deployment 事實上是一種 Contoller manager ,他會產生 ReplicaSet ( 也是 Controller )
- 目前 goweb 的 pod 由 Deployment object 做出來的,pod 會共用 w1 主機的 /opt/www 目錄
- 還有建立 HPA 監控 pod 使用的運算資源,當 pod 使用的 CPU 超過設定的平均(20%)時,pod 會擴充出來,最多 5 個。
- 但是我們 pod 當初在建立的時候,CPU 限制是 400m,pod 所在的 w1 node CPU 最多只能用 2000m ,
- 扣掉系統運作要用的 CPU (假設 200m),再扣掉 fbs 那台 pod (假設用掉 300m),
- 實際上只剩 1500m 的 CPU 可以用,但是 pod 的上限卻設定 5 個,只要擴充到 4 個,CPU 就會不夠用了,在狀態上會顯示 panding
## Liveness and Readiness Probes
* Liveness and Readiness這兩個探針都是針對pod
共有三種檢測方式,分別是
1. ExecAction: Executes a specified command inside the Container. The diagnostic is considered successful if the command exits with a status code of 0.
- 利用作業系統命令來判斷你是否還活者?
2. TCPSocketAction: Performs a TCP check against the Container’s IP address on a specified port. The diagnostic is considered successful if the port is open.
- 利用tcp網路的方式來確認你是否是健康的,例如:nc...
3. HTTPGetAction: Performs an HTTP Get request against the Container’s IP address on a specified port and path. The diagnostic is considered successful if the response has a status code greater than or equal to 200 and less than 400.
- 走http通訊協定,例如curl命令來判讀
* 一旦liveness要執行的命令發生錯誤,那他就會讓pod浴火重生
```
##pod裡面的container,先產生一個檔案,睡20秒,刪除這個檔案,再睡300秒
##livenessProbe
##initialDelaySeconds pod建立好後等5秒
##5秒到了之後去檢查/tmp/healthy這個檔案
##periodSeconds: 5 每5秒做一次這個動作
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: liveness-exec
spec:
containers:
- name: liveness
image: k8s.gcr.io/busybox
command: [/bin/sh]
args:
- -c
- touch /tmp/healthy; sleep 20; rm -rf /tmp/healthy; sleep 300
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5 '> ~/wulin/yaml/exec-liveness.yml
```
## Using Namespace
* Namespaces 有以下幾個特點
1. 在同一個 Kubernetes Cluster 中,每個 Namespaces 的名稱都是要獨特的
2. 當一個 Namespaces 被刪除時,在該 Namespace 裡的所有物件也會被刪除
3. 可以透過 Resource Quotas 限制一個 Namespaces 所可以存取的資源
4. namespace對網路的控管是可以做到防火牆的功能
* 檢視 K8S 內建 Namespace
```
$ kubectl get namespace
NAME STATUS AGE
default Active 3h45m
kube-node-lease Active 3h45m
kube-public Active 3h45m
kube-system Active 3h45m
```
* default
- 預設的 Namespaces 名稱為 default,過去我們產生的物件像是, Deployment, Services 等若沒特別指定 Namespace 都是存放在名稱為 default 的 namespaces 中。
* kube-system
- 在 Kubernetes 中,較特別的資源都會存放在 kube-system 這個 namespace。若是用 kubectl get all -n kube-system 查看,可以發現先前介紹的 kube-dns 或是 heapster 都是存放在該 namepsace 中。
* kube-public
- kube-public 也是個特殊的 namespace,存放在裡面的物件可被所有的使用者讀取。
```
$ 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
```
* pod要在myring這個namespace產生
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: n1
namespace: myring
labels:
name: n1
spec:
hostname: n1
containers:
- image: quay.io/cloudwalker/busybox
command:
- sleep
- "3600"
name: n1 ' > ~/wulin/yaml/pod-n1.yaml
```
* 建立myring namespace
```
$ kubectl create -f ~/wulin/yaml/pod-n1.yaml
Error from server (NotFound): error when creating "pod-n1.yaml": namespaces "myring" not found
$ kubectl create namespace myring
namespace/myring created
$ kubectl create -f pod-n1.yaml
pod/n1 created
$ kubectl get pod
No resources found in default namespace.
$ kubectl get pod -n myring
NAME READY STATUS RESTARTS AGE
n1 1/1 Running 0 33s
```
## K8S Namespace LimitRange
* 對namespace做記憶體限制,裡面產生的pod也會被限制。
```
$ kubectl create namespace memlimit
##在memlimit這個namespace宣告預設的記憶體限制
$ echo 'apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
spec:
limits:
- default:
memory: 512Mi
defaultRequest:
memory: 256Mi
max:
memory: 2Gi
min:
memory: 256Mi
type: Container '> ~/wulin/yaml/limitrange.yaml
$ kubectl apply -f ~/wulin/yaml/limitrange.yaml -n memlimit
```
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: memlimit-pod
spec:
containers:
- name: memlimit-pod-nginx
image: k8s.gcr.io/nginx ' > ~/wulin/yaml/memlimit-pod.yaml
$ kubectl apply -f ~/wulin/yaml/memlimit-pod.yaml -n memlimit
pod/memlimit-pod created
```
```
##memory: 512Mi 在namespace預設有宣告在這裏面建的pod記憶體最高512Mi
##requests 要求記憶體要多大
$ kubectl get pods memlimit-pod -n memlimit --output=yaml | grep -A 10 ' containers:'
containers:
- image: nginx
imagePullPolicy: Always
name: memlimit-pod-nginx
resources:
limits:
memory: 512Mi
requests:
memory: 256Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
```
> 結論:如果先建pod在給限制,那先建的pod並不會被namespace後加的規則給限制
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: memlimit-pod1
spec:
containers:
- name: memlimit-pod-nginx
image: k8s.gcr.io/nginx
resources:
limits:
memory: "2Gi" ' > ~/wulin/yaml/memlimit-pod1.yaml
以下命令會執行成功
$ kubectl apply -f ~/wulin/yaml/memlimit-pod1.yaml -n memlimit
```
* [註] LimitRange 只會針對一個 POD 產生限制, 並不是限制所有 POD 的總和
```
$ kubectl get pods memlimit-pod1 -n memlimit --output=yaml | grep -A 10 ' containers:'
containers:
- image: k8s.gcr.io/nginx
imagePullPolicy: Always
name: memlimit-pod-nginx
resources:
limits:
memory: 2Gi
requests:
memory: 2Gi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
```
## K8S Namespace Resource Quota
* 多物件寫在同一個yaml要使用`---`
```
##requests.cpu: "1" 1000m
##configmaps: "2" k8s物件最多幾個
##secrets: "10" 秘密最多10個
$ nano ~/wulin/yaml/nsrslimit.yaml
apiVersion: v1
kind: Namespace
metadata:
name: mynsrs
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: mynsrs
spec:
hard:
requests.cpu: "1"
requests.memory: 256Mi
requests.nvidia.com/gpu: 1
limits.cpu: "2"
limits.memory: 1Gi
limits.nvidia.com/gpu: 2
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-quota
namespace: mynsrs
spec:
hard:
configmaps: "2"
persistentvolumeclaims: "2"
replicationcontrollers: "2"
secrets: "10"
services: "10"
services.loadbalancers: "2"
```
* mynsrs這個namespace有ResourceQuota(總量控管),pod裡面一定要有詳細說明才能在這個namespace建立
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: nsrs-pod
spec:
containers:
- name: nsrs-pod-nginx
image: k8s.gcr.io/nginx ' > ~/wulin/yaml/nsrs-pod.yaml
$ kubectl apply -f ~/wulin/yaml/nsrs-pod.yaml -n mynsrs
Error from server (Forbidden): error when creating "nsrs-pod.yaml": pods "nsrs-pod" is forbidden: failed quota: compute-quota: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
```
* 當一個 Namespace 有分配 ResourceQuota 和對應的 Namespace 時,所有在該 Namespace 內使用元件時必須都要詳細指明用量,不然不給用!
在yaml檔有提供詳細的資源要求,因此可以在這裡建立pod
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: nsrs-pod
spec:
containers:
- name: nsrs-pod-nginx
image: k8s.gcr.io/nginx
resources:
requests:
cpu: 1.0
memory: 256Mi
limits:
cpu: 2.0
memory: 512Mi ' > ~/wulin/yaml/nsrs-pod.yaml
$ kubectl apply -f ~/wulin/yaml/nsrs-pod.yaml -n mynsrs
```
* resourcequotas看還有多少資源可以使用是要看limits的部分
* limits.cpu 的部分已經用完了,別人不能再用了
```
$ kg pod -n mynsrs
NAME READY STATUS RESTARTS AGE
nsrs-pod 1/1 Running 0 36s
$ kubectl get resourcequotas -n mynsrs
NAME AGE REQUEST LIMIT
compute-quota 54m requests.cpu: 1/1, requests.memory: 256Mi/256Mi, requests.nvidia.com/gpu: 0/1 limits.cpu: 2/2, limits.memory: 512Mi/1Gi, limits.nvidia.com/gpu: 0/2
object-quota 54m configmaps: 1/2, persistentvolumeclaims: 0/2, replicationcontrollers: 0/2, secrets: 1/10, services: 0/10, services.loadbalancers: 0/2
```
* limits.cpu: 2/2, limits.memory: 512Mi/1Gi 這二個項目顯示所有 Pod 最大值的總和
在產生一個pod,但因為在mynsrs這個namespace資源不夠了,因此無法產生pod
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: nsrs-pod1
spec:
containers:
- name: nsrs-pod-nginx
image: k8s.gcr.io/nginx
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: 1000m
memory: 256Mi ' > ~/wulin/yaml/nsrs-pod1.yaml
$ kubectl apply -f ~/wulin/yaml/nsrs-pod1.yaml -n mynsrs
Error from server (Forbidden): error when creating "nsrs-pod1.yaml": pods "nsrs-pod1" is forbidden: exceeded quota: compute-quota, requested: limits.cpu=2,requests.cpu=1,requests.memory=256Mi, used: limits.cpu=2,requests.cpu=1,requests.memory=256Mi, limited: limits.cpu=2,requests.cpu=1,requests.memory=256Mi
```
## Kubernetes Volume

* pod如果浴火重生,那在container做的資料都會不見。
* emptyDir 他的生命週期跟pod一樣,pod如果被刪除,那檔案也會跟者被刪除,如果是pod裡面的container掛了,但pod沒事,資料還會存在。
* Persistent volume(pv) 只要pod的資料要永存,那就會需要使用pv
## emptyDIR

* emptydir,這個目錄就是存在在linux,系統會自動幫我們產生的(名字很長不是人記得)。
* 同一個pod裡的多個container共用同一個linux目錄區,來達到資訊共享。
```
##產生一個volumes,名字是html,是emptyDir,pod在資料就會還在,pod不再資料就會不見
##第一個container 1st,把資料存在/usr/share/nginx/html,並且掛載到html
##第二個container 2nd,把資料產生並且存到/html/index.html這個檔案,把這個目錄掛到html
##由此可知這兩個container共用同一個目錄
$ nano ~/wulin/yaml/sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
name: sidecar
spec:
volumes:
- name: html
emptyDir: {}
containers:
- name: 1st
image: quay.io/cloudwalker/nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
- name: 2nd
image: quay.io/cloudwalker/alpine
volumeMounts:
- name: html
mountPath: /html
command: ["/bin/sh", "-c"]
args:
- while true; do
date >> /html/index.html;
sleep 1;
done
```
```
$ ka -f ~/wulin/yaml/sidecar.yaml
##到 1st 這個 container 看首頁內容
## -c 指定哪一個 container,這裡指定 1st container
## -- 要執行的指令
## 看/usr/share/nginx/html/index.html 這個網站首頁末三行的內容
$ kubectl exec sidecar -c 1st -- /bin/cat /usr/share/nginx/html/index.html | tail -n 3
Thu Dec 16 13:20:14 UTC 2021
Thu Dec 16 13:20:15 UTC 2021
Thu Dec 16 13:20:16 UTC 2021
```
```
$ kg pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sidecar 2/2 Running 0 12m 10.244.0.6 m1 <none> <none>
$ kubectl get pod sidecar --template={{.status.podIP}}; echo
10.244.0.6
$ podip=$(kubectl get pod sidecar --template={{.status.podIP}})
$ curl -s http://$podip | head -n 5
Thu Dec 16 13:06:28 UTC 2021
Thu Dec 16 13:06:29 UTC 2021
Thu Dec 16 13:06:30 UTC 2021
Thu Dec 16 13:06:31 UTC 2021
Thu Dec 16 13:06:32 UTC 2021
```
## hostPath Volume 應用範例
在 m1 終端機執行
```
##/opt/hostpath這個目錄區會在alpine主機上產生
##/opt/hostpath這個目錄會掛載到container的/usr/share/nginx/html這個目錄裡
$ nano ~/wulin/yaml/pod-hp.yaml
kind: Pod
apiVersion: v1
metadata:
name: pod-hp
spec:
containers:
- name: pod-hp
image: quay.io/cloudwalker/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: hp-volume
volumes:
- name: hp-volume
hostPath:
path: /opt/hostpath
```
* 進到pod裡面並且產生資料
```
$ kubectl create -f ~/wulin/yaml/pod-hp.yaml (不會產生 PV Object)
$ kubectl get pod pod-hp -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hp 1/1 Running 0 98s 10.244.2.20 w1 <none> <none>
$ ssh w1 ls -ald /opt/hostpath
drwxr-xr-x 2 root root 4096 Nov 19 21:06 /opt/hostpath
$ kubectl exec -it pod-hp -- sh
# echo "<h1>let me go</h1>" > /usr/share/nginx/html/index.html
# exit
$ curl http://10.244.2.20
<h1>let me go</h1>
```
* 檢查主機上的目錄
```
$ ssh w1 ls -al /opt/hostpath
total 12
drwxr-xr-x 2 root root 4096 May 25 07:41 .
drwxr-xr-x 5 root root 4096 May 25 07:40 ..
-rw-r--r-- 1 root root 19 May 25 07:41 index.html
刪除 pod-hp POD
$ kubectl delete pod pod-hp
pod "pod-hp" deleted
再次檢視 /opt/hostpath 目錄
$ ssh w1 ls -al /opt/hostpath
total 12
drwxr-xr-x 2 root root 4096 6月 2 08:07 .
drwxr-xr-x 5 root root 4096 5月 30 06:22 ..
-rw-r--r-- 1 root root 19 6月 2 08:07 index.html
```
## 認識 Persistent Volumes
* 管理 storage 一直都是一個不簡單的課題,為了讓 developer 可以更專注在開發上,k8s 中提出了兩個概念(resource object),分別是 Persistent Volume(PV) & Persistent Volume Claim(PVC),透過這兩個概念將連結 storage 的過程抽象化,讓使用者不需要了解 storage 在底層是如何運作的,只要了解如何使用即可,大幅降低使用者操作 storage 的困難度。
* Persistent Volume(PV) 由 storage 管理者負責產生,在 k8s 中就是一種可用的 storage resource,同樣也是一種 volume plugin,但有自己獨立的 lifecycle,且包含的就是實際與 storage 連結的實作細節。
* Persistent Volume Claim(PVC) 則是來自使用者的 storage request,就跟 pod 一樣都是要消耗特定的資源,PVC 消耗 PV 資源(pod 消耗 node 資源),PVC 指定特定 size or access mode 的 PV(pod 可以指定 CPU, memory … etc)。
* 由於 PVC 可以允許使用者自行指定所需要 PV 的相關屬性,因此除了 size, access mode 外,可能還會有 performance 的需求。而為了滿足不同的使用目的,cluster administrator 就必須要準備好不同的 PV 來處理不同 PVC 的需求;若是 PVC 數量不多且需求單純,手動產生 PV 可能還可以接受,但若是 PVC 的數量眾多且需求多變,那可能就需要 [StorageClass] 的協助了!

### Local Persistent Volumes
* PV 管理者(SRE工程師負責)根據需求配置 Persistent Volume 後 ,使用者可以透過 PVC 的方式取得 PV 的資源進行操作 ,最後在掛載到 Pod 使用 。
* pv 名字叫 local,代表一個處存體
* pv 不願意跟 pod 的 yaml 再一起,一定要獨立的一個 yaml 檔
* local pv ,一定指定要在哪一台機器
* 永存的目錄區管理者要先建好
```
##處存空間10Gi
##ReadWriteOnce 只是參考用的,因為超過10Gi也不管你
##local:就是pv的名字
##pv不會真的去限制你使用的容量大小,她只是有這個想法而已
##path: "/opt/local" 我們要自己要產生這個目錄
##m1 指定在m1機器上跑pv
##path: "/opt/local" 真正處存空間的限制是linux(Quota)對這個目錄做10Gi的大小限制以及ReadWriteOnce權限的設定
$ echo 'kind: PersistentVolume
apiVersion: v1
metadata:
name: pv-local
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
local:
path: "/opt/local"
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- m1 ' > ~/wulin/yaml/pv-local.yaml
```
[註] 只要是 Local Volume 一定要透過 Persistent Volume 定義檔來使用, 在定義檔中, 還一定要宣告 nodeAffinity
```
$ kubectl create -f ~/wulin/yaml/pv-local.yaml
persistentvolume/pv-local created
##Available 目前沒有人使用這個pv
##Retain 使用者不見了資料還會保存
$ kubectl get pv pv-local
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-local 10Gi RWO Retain Available 102s
```
### 建立 Local PersistentVolumeClaim
```
##pvc 並沒有訂定要對應到哪個 pv,pvc 會去尋找目前有哪個 pv 可使用,然後綁在一起
##storage: 3Gi 處存空間要大於3Gi
$ echo 'kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-local
spec:
accessModes:
- ReadWriteOnce
storageClassName: ""
resources:
requests:
storage: 3Gi ' > ~/wulin/yaml/pvc-local.yaml
$ kubectl create -f ~/wulin/yaml/pvc-local.yaml
```
[註] 建立 PVC 會立即搜尋可用的 PV, 然後建立連接
* 檢視 Local PVC 狀態
```
$ kubectl get pv pv-local
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-volume 10Gi RWO Retain Bound default/pv-claim web 4h16m
$ kubectl get pvc pvc-local
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-local Bound pv-local 10Gi RWO 56s
```
[註] "kubectl get pvc" 這個命令, 無法顯示 PVC 所需的 Storage Size.
### 建立使用 pvc-local 的第一個 Pod
```
產生這個pod,這個pod一定會在pv指定的機器上產生
$ echo 'kind: Pod
apiVersion: v1
metadata:
name: pod-pvc1
spec:
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: pvc-local
containers:
- name: pod-pvc1
image: quay.io/cloudwalker/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pv-storage ' > ~/wulin/yaml/pod-pvc1.yaml
```
### 移除 PVC-local 然後再重新建立
```
$ kubectl delete pvc pvc-local
persistentvolumeclaim "pvc-local" deleted
##pv 顯示 Released,表示釋放
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-local 10Gi RWO Retain Released default/pvc-local 7m52s
##刪除pvc,然後再產生同一個pvc
$ kubectl create -f ~/wulin/yaml/pvc-local.yaml
persistentvolumeclaim/pvc-local created
##pvc刪除後會顯示Released
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-local 10Gi RWO Retain Released default/pvc-local 8m23s
##產生的pvc會Pending
##雖然是用同一個yaml產生的,但對於pv不是原本的pvc,所以不會Bound在一起,因此要做心理治療
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-local Pending
```
### 移除 PVC 然後重新掛載 PV
```
##k8s宣告的編輯器是nano
$ export KUBE_EDITOR="nano"
##把claimRef 這段到uid的內容都刪除,讓她忘了前任
$ kubectl edit pv pv-local
.......
capacity:
storage: 10Gi
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: pvc-local
namespace: default
resourceVersion: "2335811"
uid: f1e0e01e-890c-11e9-9065-0007324d1e94
local:
path: /opt/local
.........
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-local 10Gi RWO Retain Bound default/pvc-local 31m
```

* pv只要有滿足pvc那他們都可以做連接,雖然正常情況下一定都是1:1。
* access mode(形同虛設):參考就好
* ReadWriteOnce(RWO)
- the volume can be mounted as read-write by a single node. ReadWriteOnce access mode still can allow multiple pods to access the volume when the pods are running on the same node.
- 只能在單一節點上掛載
* ReadOnlyMany(ROX)
- the volume can be mounted as read-only by many nodes. pod的掛上來不能新增資料,可以在不同節點上掛載pod
* ReadWriteMany(RWX)
- the volume can be mounted as read-write by many nodes. 多個pod可以使用相同的空間,pod可以在不同的node連到同一個pvc
> dd if=/dev/zero of=test bs=1M count=10
> 產生10m大小的test檔案
## K8S Multi-Tenancy 管理套件

- Linux 系統可以讓多人同時協同作業,使用者登入後,工作目錄會在自己的家目錄。
- 在 K8S 的系統中,也能夠實現多使用者同時進入 K8S 的叢集做操作,登入 K8S 時,靠的是使用者家目錄下`~/.kube/config` 這個放憑證的檔案。
- 這個憑證會決定使用者在 K8S 可以進入哪個 Namespace ,可以"建立"還是"刪除"還是"查詢"物件...等。
* 下載 K8S Multi-Tenancy 管理套件
```
$ cd; wget http://www.oc99.org/zip/k8suser.zip; unzip k8suser.zip; cd k8suser
$ dir
drwx--S--- 2 bigred bigred 4.0K Apr 8 21:50 .
drwxr-sr-x 3 bigred bigred 4.0K Apr 8 22:39 ..
-rw-r--r-- 1 bigred bigred 328 Apr 8 22:03 clusterbind.yaml
-rw-r--r-- 1 bigred bigred 507 Apr 8 22:03 clusterole.yaml
-rw-r--r-- 1 bigred bigred 269 Nov 1 01:14 context.temp
-rw-r--r-- 1 bigred bigred 277 Oct 30 07:09 csr.yaml
-rwxr-xr-x 1 bigred bigred 74 Apr 6 16:00 deluser.sh
-rwxr-xr-x 1 bigred bigred 975 Apr 8 21:57 mkcontext.sh
-rwxr-xr-x 1 bigred bigred 1.1K Apr 6 16:01 mkubeuser.sh
-rw-r--r-- 1 bigred bigred 986 Apr 7 17:30 role.temp
-rw-r--r-- 1 bigred bigred 214 Apr 7 17:20 role.yaml
-rw-r--r-- 1 bigred bigred 302 Oct 27 10:20 rolebind.yaml
```
### 建立 K8S User 及 憑証
```
$ ./mkubeuser.sh bigboss
K8S bigboss created
$ dir kuser/
total 40K
drwxr-sr-x 2 bigred bigred 4.0K Sep 11 19:20 .
drwx--S--- 3 bigred bigred 4.0K Sep 11 15:32 ..
-rw-r--r-- 1 bigred bigred 1.1K Sep 11 19:20 bigboss.crt ##k8s根據你申請的憑證,會給你一個認證的憑證檔,檔案會存在這,又稱(k8s身份證)
-rw-r--r-- 1 bigred bigred 911 Sep 11 19:20 bigboss.csr ##bigboss私鑰(是靠.key產生的),姓名,服務單位等個資
-rw------- 1 bigred bigred 1.7K Sep 11 19:20 bigboss.key ##bigboss的私鑰
$ kubectl config view | grep -A 10 -e '^users'
users:
- name: bigboss
user:
client-certificate: /home/bigred/k8suser/kuser/bigboss.crt
client-key: /home/bigred/k8suser/kuser/bigboss.key
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
```
### ~/.kube/config 憑證檔
```
$ cat ~/.kube/config ##k8s入口的憑證檔
......
server: https://192.168.61.4:6443 ## k8s入口位置
name: kubernetes ##k8s叢集名稱
contexts:
- context:
cluster: kubernetes ##進到kubernetes叢集
user: kubernetes-admin ##這個入口只有這個使用者可以進來
name: kubernetes-admin@kubernetes ##入口名稱
current-context: kubernetes-admin@kubernetes ##使用哪個入口
- name: kubernetes-admin
user:
##k8s認證過的憑證檔
client-certificate-data:
......
##bigred進去k8s的私鑰
client-key-data:
......
```
## 認識 Role-based access control(RBAC)
* 設定這個user可以做什麼,不可以做什麼。
> 1. Subjects(是誰): The set of users and processes that want to access the Kubernetes API.
> 2. Resources(你可以使用哪些物件): The set of Kubernetes API Objects available in the cluster. Examples include Pods, Deployments, Services, Nodes, and PersistentVolumes, among others.
> 3. Verbs(你可以做什麼動作): The set of operations that can be executed to the resources above. Different verbs are available (examples: get, watch, create, delete, etc.), but ultimately all of them are Create, Read, Update or Delete (CRUD) operations.
```
$ kubectl api-versions | grep rbac
rbac.authorization.k8s.io/v1
$ kubectl create namespace finance
##這個規規定,使用者只能看讀pod,和service
$ echo $'kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: finance
name: finance-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods", "services"]
verbs: ["get", "watch", "list"] ' > role.yaml
$ kubectl create -f role.yaml
role.rbac.authorization.k8s.io/finance-reader created
$ kubectl get roles --namespace=finance
NAME CREATED AT
finance-reader 2020-10-21T13:48:50Z
```
```
## rolebinding 負責把使用者跟role綁在一起
$ echo $'kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: finance-read-access
namespace: finance
subjects:
- kind: User
name: bigboss
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role #this must be Role or ClusterRole
name: finance-reader
apiGroup: rbac.authorization.k8s.io ' | kubectl apply -f -
$ kubectl get rolebindings --namespace=finance
NAME AGE
finance-read-access 11s
```
> [註] 務必到這網址 https://kubernetes.io/docs/reference/kubectl/#resource-types, 檢視 K8S 所有 Resource 的 運作資訊
```
$ echo $'apiVersion: v1
kind: Pod
metadata:
name: helloworld
namespace: finance
labels:
app: helloworld
spec:
containers:
- image: k8s.gcr.io/nginx
name: nginx
ports:
- containerPort: 80 ' > rbacpod.yaml
$ ka -f rbacpod.yaml
$ kubectl get pods --namespace=finance --as=bigboss
NAME READY STATUS RESTARTS AGE
helloworld 1/1 Running 0 12m
$ kubectl get pods --as=bigboss
Error from server (Forbidden): pods is forbidden: User "bigboss" cannot list resource "pods" in API group "" in the namespace "default"
```
### Create Cluster Role
```
##針對整個k8s
$ echo $'kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
# "namespace" omitted since ClusterRoles are not namespaced
name: cluster-pods-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"] ' | kubectl create -f -
$ kubectl get clusterroles | grep cluster-node-reader
cluster-node-reader 2020-10-15T11:05:54
```
* Cluster Role Binding
```
$ echo $'kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-cluster-pods
subjects:
- kind: User
name: bigboss
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-pods-reader
apiGroup: rbac.authorization.k8s.io ' | kubectl create -f -
$ kubectl get clusterrolebindings | grep read-cluster-nodes
read-cluster-nodes ClusterRole/cluster-node-reader 66s
$ kubectl get pods --as=bigboss
No resources found in default namespace
```
## 切換 K8S Namespace
```
執行以下命令, 可知目前使用的 K8S Context 的 namespace 沒有設定
##NAMESPACE 沒有寫資料就代表現在的namespace是default
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kubernetes-admin@kubernetes kubernetes kubernetes-admin
切換到 finance namespace
$ kubectl config set-context $(kubectl config current-context) --namespace=finance
Context "kubernetes-admin@kubernetes" modified.
只會顯示 finance namespace 中的 POD
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
helloworld 1/1 Running 0 13h
因內定為 finance namespace, 所以 bigboss 可以讀取
$ kubectl get pods --as bigboss
NAME READY STATUS RESTARTS AGE
helloworld 1/1 Running 0 13h
```
## 建立 K8S Context
* 建立與切換 K8S Context
```
建立 finance-context Context
##finance-context 自己取的入口名稱
##--cluster=kubernetes 進到的叢集是 kubernetes
##--namespace=finance 進到的 namespace 是 finance
##--user=bigboss 只能是 bigboss 進來
$ kubectl config set-context finance-context --cluster=kubernetes --namespace=finance --user=bigboss
$ kubectl config view | grep -A 10 contexts:
contexts:
- context:
cluster: kubernetes
namespace: finance
user: bigboss
name: finance-context
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
切換至 finance-context
$ kubectl config use-context finance-context
```
```
$ kubectl get nodes
Error from server (Forbidden): nodes is forbidden: User "bigboss" cannot list resource "nodes" in API group "" at the cluster scope
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
helloworld 1/1 Running 1 58m
$ kubectl get pods -n kube-system
coredns-78fcd69978-5v6cd 1/1 Running 3 9h
coredns-78fcd69978-j4z9r 1/1 Running 3 9h
etcd-m1 1/1 Running 4 9h
kube-apiserver-m1 1/1 Running 4 9h
kube-controller-manager-m1 1/1 Running 4 9h
..............
切回至 kubernetes-admin@kubernetes context
$ kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
```
## K8S Multi-Tenancy 實作
* 建立 K8S User 及 憑証
```
$ ./mkubeuser.sh rbean
K8S rbean created
$ dir kuser/
total 40K
drwxr-sr-x 2 bigred bigred 4.0K Nov 6 21:19 .
drwx--S--- 3 bigred bigred 4.0K Nov 6 01:05 ..
-rw-r--r-- 1 bigred bigred 1.1K Nov 6 21:19 rbean.crt
-rw-r--r-- 1 bigred bigred 907 Nov 6 21:19 rbean.csr
-rw------- 1 bigred bigred 1.7K Nov 6 21:19 rbean.key
$ kubectl config view | grep -A 10 -e '^users'
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
- name: rbean
user:
client-certificate: /home/bigred/k8suser/kuser/rbean.crt
client-key: /home/bigred/k8suser/kuser/rbean.key
```
```
##幫rbean設定他的入口
$ ./mkcontext.sh rbean
namespace/rbean created
- context:
cluster: default
namespace: rbean
user: rbean
name: rbean-context
role.rbac.authorization.k8s.io/rbean-role created
rolebinding.rbac.authorization.k8s.io/rbean-rbind created
clusterrole.rbac.authorization.k8s.io/rbean-clusterole created
clusterrolebinding.rbac.authorization.k8s.io/rbean-clusterbind created
$ dir kuser
total 28K
drwxr-sr-x 2 bigred bigred 4.0K Oct 30 01:13 .
drwx--S--- 3 bigred bigred 4.0K Oct 30 01:13 ..
-rw-r--r-- 1 bigred bigred 5.5K Oct 30 01:13 rbean.conf ##rbean專屬的入口的憑證檔
-rw-r--r-- 1 bigred bigred 1.1K Oct 30 01:13 rbean.crt # 公鑰
-rw-r--r-- 1 bigred bigred 907 Oct 30 01:13 rbean.csr
-rw------- 1 bigred bigred 1.7K Oct 30 01:13 rbean.key # 私鑰
```
### 檢視 K8S User Context
```
$ cat kuser/rbean.conf
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F
........
server: https://192.168.61.4:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
namespace: rbean
user: rbean
name: rbean-context
current-context: rbean-context
........
kind: Config
preferences: {}
users:
- name: rbean
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCakNDQWU2Z0F..........
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0........
[註] "client-certificate-data:" 存放 公鑰 的憑証檔, "client-key-data:" 存放使用者的 私鑰
```
```
$ kubectl get role -n rbean
NAME CREATED AT
rbean-role 2022-04-08T14:50:29Z
$ kubectl get clusterrole -n rbean | grep rbean
rbean-clusterole 2022-04-08T14:50:29Z
##rbean對自己的namespace有所有的權限
# *.* 代表所有物件有所有權限
# pv pvc 要單獨拉出來做授權
$ kubectl describe role rbean-role -n rbean
Name: rbean-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
*.* [] [] [*]
persistentvolumeclaims.* [] [] [*]
persistentvolumes.* [] [] [*]
```
```
# rbean 在整個叢集有哪些事可以做
$ kubectl describe clusterrole rbean-clusterole -n rbean
Name: rbean-clusterole
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
csidrivers.storage.k8s.io [] [] [*]
csinodes.storage.k8s.io [] [] [*]
storageclasses.storage.k8s.io [] [] [*]
volumeattachments.storage.k8s.io [] [] [*]
nodes [] [] [create delete deletecollection get list patch update watch]
persistentvolumes [] [] [create delete deletecollection get list patch update watch]
```
### 建立 Linux User 及 設定 K8S 憑証
```
$ echo -e "rbean\nrbean" | sudo adduser rbean
##把利用老師程式做出來的rbean入口憑證檔拷貝到rbean自己的家目錄
$ sudo mkdir /home/rbean/.kube; sudo cp ~/k8suser/kuser/rbean.conf /home/rbean/.kube/config; sudo chown -R rbean:rbean /home/rbean/.kube
$ dir /home/rbean/.kube
total 16K
drwxr-sr-x 2 rbean rbean 4.0K Apr 7 10:00 .
drwxr-sr-x 3 rbean rbean 4.0K Apr 7 10:00 ..
-rw-r--r-- 1 rbean rbean 5.5K Apr 7 10:00 config
$ exit
```
### 檢視 K8S User 憑証
```
在 Windows 系統的 cmd 視窗, 執行以下命令
$ ssh rbean@<alp.m1 IP>
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.61.4:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
namespace: rbean
user: rbean
name: rbean-context
current-context: rbean-context
kind: Config
preferences: {}
users:
- name: rbean
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
```
```
$ kg all
No resources found in rbean namespace.
$ kg pv
No resources found
$ kg sc
No resources found
$ kubectl run a1 --rm -it --image=quay.io/cloudwalker/alpine
If you don't see a command prompt, try pressing enter.
/ # ping -c 1 www.hinet.net
PING www.hinet.net (163.28.83.113): 56 data bytes
ping: permission denied (are you root?)
/ # exit
$ exi
```
## K8S Namespace Resource Quota
在 bigred 終端機, 執行以下命令
```
$ echo 'apiVersion: v1
kind: ResourceQuota
metadata:
name: rbean-quota
namespace: rbean
spec:
hard:
requests.cpu: "1"
requests.memory: 256Mi
limits.cpu: "2"
limits.memory: 1Gi
pods: "4"
persistentvolumeclaims: "2"
services: "3"
services.loadbalancers: "2" '> nsrq.yaml
$ ka -f nsrq.yaml
```
在 rbean 終端機, 執行以下命令
```
$ kubectl describe quota
Name: rbean-quota
Namespace: rbean
Resource Used Hard
-------- ---- ----
limits.cpu 0 2
limits.memory 0 1Gi
persistentvolumeclaims 0 2
pods 0 4
requests.cpu 0 1
requests.memory 0 256Mi
services 0 3
services.loadbalancers 0 2
```
在 rbean 終端機, 執行以下命令
```
$ kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: testpod1
spec:
containers:
- name: quota-test
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'echo Pod is Running ; sleep 5000']
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "200m"
memory: "512Mi"
restartPolicy: Never
EOF
```
```
$ kg all
NAME READY STATUS RESTARTS AGE
pod/testpod1 1/1 Running 0 23s
$ kubectl describe quota
Name: rbean-quota
Namespace: rbean
Resource Used Hard
-------- ---- ----
limits.cpu 200m 2
limits.memory 512Mi 1Gi
persistentvolumeclaims 0 2
pods 1 4
requests.cpu 100m 1
requests.memory 256Mi 256Mi
services 0 3
services.loadbalancers 0 2
```
## bobo 網站應用系統
```
在 bigred 終端機, 執行以下命令
$ wget http://www.oc99.org/zip/bobowas.zip; unzip bobowas.zip
佈署 alp.mysql, alp.goweb 及 alp.myfbs image
$ cd ~/bobowas/k8simg/
$ ./go-k8s-images.sh
[images build]
alp.myfbs image ok
alp.mysql image ok
alp.goweb image ok
[images deploy]
w1: localhost/alp.myfbs image ok
w2: localhost/alp.myfbs image ok
w1: localhost/alp.mysql image ok
w2: localhost/alp.mysql image ok
w1: localhost/alp.goweb image ok
w2: localhost/alp.goweb image ok
```
* 在 rbean 終端機, 執行以下命令
```
$ cd /home/bigred/bobowas/k8sobj/
$ ./go-k8s-object.sh
bobo.fbs(w1) pod created
bobo.mysql(w2) pod created
bobo.goweb(w1) pod created
bobo.gosvc service created
$ kg all
NAME READY STATUS RESTARTS AGE
pod/bobo.fbs 1/1 Running 0 15s
pod/bobo.goweb 1/1 Running 0 15s
pod/bobo.mysql 1/1 Running 0 15s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/svc-gosrv ClusterIP 10.98.0.237 192.168.61.4 80/TCP 15s
```
## K8S Namespace Resource Quota
在 bigred 終端機, 執行以下命令
```
##replicationcontrollers: "2" 對replicas做限制最多2個
$ nano ~/bobowas/k8sobj/nsrq.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: rbean
spec:
hard:
requests.cpu: "1"
requests.memory: 256Mi
requests.nvidia.com/gpu: 1
limits.cpu: "2"
limits.memory: 1Gi
limits.nvidia.com/gpu: 2
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-quota
namespace: rbean
spec:
hard:
pods: "3"
configmaps: "2"
persistentvolumeclaims: "2"
replicationcontrollers: "2"
secrets: "10"
services: "10"
services.loadbalancers: "2"
$ ka -f nsrq.yaml
```
## Kubernetes公共建設
### Local Path Provisioner
* 他可以幫你自動建pv,並且建出來的pv對應到的pvc一定是1:1,不會多出而外的空間。
* Local Path 所支援的 accessmode 是 ReadWriteOnce,其他的都不支援,因此在建立 pod 的時候會 Pending
安裝 Local Path Provisioner
```!
$ wget -O - https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.22/deploy/local-path-storage.yaml | kubectl apply -f -
```
```!
##檢查storageclass
##就是因為Delete所以在刪除pvc的同時pv和mount的檔案路徑也會一起刪除,不過這是可以改的
$ kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path rancher.io/local-path Delete WaitForFirstConsumer false 54s
```
### 建立 PersistentVolumeClaim
```
##storageClassName: local-path 就是這個可以自動產生pv
$ echo 'apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: html-storage
spec:
storageClassName: local-path
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Mi' > pvc-nginx.yaml
$ kubectl apply -f pvc-nginx.yaml
persistentvolumeclaim/html-storage created
```
* 檢視 PV 與 PVC
```
##沒人使用他就會先pending
$ kubectl get pvc html-storage
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
html-storage Pending local-path 29s
$ kubectl get pv
No resources found
```
* 建立 Pod 使用 PVC
```
$ echo 'apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: quay.io/flysangel/nginx
volumeMounts:
- name: html-storage
mountPath: /usr/share/nginx/html
volumes:
- name: html-storage
persistentVolumeClaim:
claimName: html-storage' > pod-nginx.yaml
```
* 建立pod後pvc就會bound,因為要建pod才會有pv
```
$ kubectl apply -f pod-nginx.yaml
pod/nginx created
$ kubectl get pod nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 16m 10.244.1.21 w1 <none> <none>
```
* 檢視 PV 與 PVC
```
$ kubectl get pvc html-storage
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
html-storage Bound pvc-a1c106eb-19bb-46b5-b179-a056216878e7 3Mi RWO local-path 19m
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-a1c106eb-19bb-46b5-b179-a056216878e7 3Mi RWO Delete Bound default/html-storage local-path 5m19s
```
```
$ ssh w1 ls -al /opt/local-path-provisioner
total 12
drwxr-xr-x 3 root root 4096 Jul 13 02:19 .
drwxr-xr-x 4 root root 4096 Jul 13 02:18 ..
drwxrwxrwx 2 root root 4096 Jul 13 02:19 pvc-ef65e1a6-c509-4075-8c11-b8ef715078d7_default_html-storage
```
## 刪除 Pod 檢查 PV PVC
* 刪除pvc,pv還有mount的檔案目錄也會一起被刪除
```
$ kubectl delete -f pod-nginx.yaml
pod "nginx" deleted
$ kubectl delete -f pvc-nginx.yaml
persistentvolumeclaim "html-storage" deleted
$ kubectl get pv
No resources found
$ ssh w1 ls -al /opt/local-path-provisioner
total 8
drwxr-xr-x 2 root root 4096 May 29 12:18 .
drwxr-xr-x 5 root root 4096 May 29 11:55 ..
```
> 只要是使用 `storageClassName: local-path` 這個公共建設,那 pvc 的`accessmode`只會支援`ReadWriteOnce`,除此之外的都會 Pending。
## MetalLB 附載平衡
> Kubernetes does not offer an implementation of network load balancers (Services of type LoadBalancer) for bare-metal clusters
> 我們自己裝的原生k8s,`service`沒有`LoadBalancer`這個type,我們裝了MetalLB就是希望有這個功能。

* 三大雲內鍵就有`LoadBalancer`
* user的流量可以透過`LoadBalancer`把流量平衡負載在多台主機上。

* 我們自己裝了MetalLB只是讓我們的service有`LoadBalancer`這個type而已,並沒有附載平衡的功能。
### MetalLB 安裝
```!
$ wget -qO - https://raw.githubusercontent.com/metallb/metallb/v0.14.0/config/manifests/metallb-native.yaml | kubectl apply -f -
```
```
##注意addresses要換自己環境的ip
##這些ip可以不是k8s的ip,只要是k8s連得到的都可以使用
##這些ip pool就等同於externalip,變成可以對外的ip變成從220~230
##externalip只能是k8s叢集的ip
$ echo '
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: mlb1
namespace: metallb-system
spec:
addresses:
- 192.168.11.220-192.168.11.230
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: mlb1
namespace: metallb-system' | kubectl apply -f -
```
### MetalLB 檢查
```
$ kubectl get pod -n metallb-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
controller-7476b58756-rhchw 1/1 Running 0 11m 10.244.1.54 w1 <none> <none>
speaker-779hp 1/1 Running 0 11m 192.168.61.7 w2 <none> <none>
speaker-hx8tp 1/1 Running 0 11m 192.168.61.6 w1 <none> <none>
speaker-vxdnp 1/1 Running 0 11m 192.168.61.4 m1 <none> <none>
```
### MetalLB 測試
* 建立deployment
```
$ echo '
apiVersion: apps/v1
kind: Deployment
metadata:
name: s1.dep
spec:
replicas: 2
selector:
matchLabels:
app: s1.dep
template:
metadata:
labels:
app: s1.dep
spec:
containers:
- name: app
image: quay.io/flysangel/image:app.golang' | kubectl apply -f -
deployment.apps/s1.dep created
```
* 建立service
* service的type是nodeport,就是在各個node上都開一個port
```
$ echo '
apiVersion: v1
kind: Service
metadata:
name: s1
annotations:
metallb.universe.tf/address-pool: mlb1
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: s1.dep
type: LoadBalancer' | kubectl apply -f -
service/s1 created
```
* 檢查service
* 這邊的`192.168.61.220`就會是ip pool的第一個ip,他就是externalip,對外給別人連線
```
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
s1.dep-5949f6d856-khqp8 1/1 Running 0 3s
s1.dep-5949f6d856-59w6k 1/1 Running 0 3s
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.98.0.1 <none> 443/TCP 9d
service/s1 LoadBalancer 10.98.0.137 192.168.61.220 80:32323/TCP 30s
```
```
##-w 輸出完後就換幫你換行
$ curl -w "\n" http://192.168.61.220
{"message":"Hello Golang"}
$ curl -w "\n" http://192.168.61.220/hostname
{"message":"s1.dep-55f7bf48df-zcbcp"}
$ curl -w "\n" http://192.168.61.220/hostname
{"message":"s1.dep-55f7bf48df-b588s"}
```
* `00:50:56:AB:00:07`這個`macaddress`是w2
```
$ sudo arping -c 4 192.168.61.220
ARPING 192.168.61.220 from 192.168.61.4 eth0
Unicast reply from 192.168.61.220 [00:50:56:AB:00:07] 0.873ms
Unicast reply from 192.168.61.220 [00:50:56:AB:00:07] 1.253ms
Unicast reply from 192.168.61.220 [00:50:56:AB:00:07] 0.813ms
Unicast reply from 192.168.61.220 [00:50:56:AB:00:07] 0.871ms
Sent 4 probes (1 broadcast(s))
Received 4 response(s)
```
> 總結:把w2關機之後再去arping,會發現`macaddress`會變成w1,服務還是有通,雖然metallb不會幫我們做附載平衡,但她會幫我們做到單點主機損毀,並且幫我們換到別台主機上。
> 請檢查 `00:50:56:AB:00:07` 此 MAC 為哪台主機?
> 將該台主機關機,五分鐘後重新測試命令,請問 MAC 換至哪台主機?
> 為甚麼是五分鐘後測試?
### 移除測試
```
$ kubectl delete service s1
service "s1" deleted
$ kubectl delete deployment s1.dep
deployment.apps "s1.dep" deleted
```
## Ingress controller

* `ingress`做入口的網站管理,只要切到不同的id上面,那連到的就會是不同服務。
### Ingress Nginx 安裝
```
$ wget -qO - https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml | sed 's|name: nginx|name: ig1|g' | kubectl apply -f -
##ingress就是使用service的externalip
$ kubectl get service -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.98.0.47 192.168.61.220 80:31727/TCP,443:30789/TCP 23s
ingress-nginx-controller-admission ClusterIP 10.98.0.17 <none> 443/TCP 23s
##ingressclass就是給別人用的
$ kubectl get ingressclass
NAME CONTROLLER PARAMETERS AGE
ig1 k8s.io/ingress-nginx <none> 28s
```
### Ingress Nginx 測試
```
$ echo '
apiVersion: v1
kind: Service
metadata:
name: app1
spec:
selector:
app: app1
ports:
- port: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: app1
labels:
app: app1
spec:
containers:
- name: app1
image: quay.io/flysangel/image:app.golang
---
apiVersion: v1
kind: Service
metadata:
name: app2
spec:
selector:
app: app2
ports:
- port: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: app2
labels:
app: app2
spec:
containers:
- name: app2
image: quay.io/flysangel/image:app.golang' | kubectl apply -f -
```
```
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
app1 1/1 Running 0 21s
app2 1/1 Running 0 21s
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
app1 ClusterIP 10.99.0.163 <none> 8080/TCP 42s
app2 ClusterIP 10.99.0.25 <none> 8080/TCP 42s
```
* 建立ingress
```
##ig1連到這個ingress
##test.k8s.org我們自己定義的網址
##pod的port是8080
$ echo '
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ig1
spec:
ingressClassName: ig1
rules:
- host: test.k8s.org
http:
paths:
- path: /app1
pathType: Prefix
backend:
service:
name: app1
port:
number: 8080
- path: /app2
pathType: Prefix
backend:
service:
name: app2
port:
number: 8080' | kubectl apply -f -
```
```
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
ig1 ig1 test.k8s.org 192.168.61.220 80 27s
$ curl -w '\n' --resolve test.k8s.org:80:192.168.61.220 http://test.k8s.org/app1
{"message":"app1"}
$ curl -w '\n' --resolve test.k8s.org:80:192.168.61.220 http://test.k8s.org/app2
{"message":"app2"}
```
> 結論:k8s叢集變複雜後我們一定會記不住有那些pod,有哪些ip,透過ingress,輸入記得住的名字,再透過輸入不同的id會到不同的service,不同的service就代表不同的pod,不同的服務。
## StatefulSet?
* StatefulSet 在 v1.9 版後正式支援,是在 Kubernetes 中用來建構 stateful application 的 resource(API) object。
* 在一般的觀念裡,container 相當合適作為 stateless application 之用(例如:api service),但由於 stateful application 的需求眾多(例如:官網範例中的 ZooKeeper & Kafka 應用),因此 Kubernetes 就額外增加了一些管理維運的機制,讓 pod 也開始適合承載 stateful application。
* 基本上 StatefulSet 中在 pod 的管理上都是與 Deployment 相同,基於相同的 container spec 來進行;而其中的差別在於 StatefulSet controller 會為每一個 pod 產生一個固定的識別資訊,不會因為 pod reschedule 後有變動
### 什麼時候需要使用 StatefulSet?
1. 需要穩定 & 唯一的網路識別 (pod reschedule 後的 pod name & hostname 都不會變動)
2. 需要穩定的 persistent storage (pod reschedule 後還是能存取到相同的資料,基本上用 PVC 就可以解決)
3. 佈署 & scale out 的時後,每個 pod 的產生都是有其順序且逐一慢慢完成的
4. 進行更新操作時,也是與上面的需求相同
## 建立 StatefulSet
```
$ echo 'apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
serviceName: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: quay.io/cooloo9871/nginx
volumeMounts:
- name: html-storage
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: html-storage
spec:
storageClassName: local-path
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi' > statefulset-nginx.yaml
```
## 驗證 nginx 首頁
```
$ kubectl apply -f statefulset-nginx.yaml
statefulset.apps/nginx created
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-0 1/1 Running 0 83s 10.244.2.32 w2 <none> <none>
nginx-1 1/1 Running 0 73s 10.244.1.75 w1 <none> <none>
nginx-2 1/1 Running 0 64s 10.244.0.30 m1 <none> <none>
```
* 驗證 nginx 首頁
```
逐一檢查 3 個 nginx 網頁
$ curl 10.244.2.32
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>
```
```
逐一新增 3 個 nginx 網頁
$ kubectl exec -it nginx-0 -- bash -c "echo 'My nginx-0' > /usr/share/nginx/html/index.html"
$ curl 10.244.2.32
My nginx-0
$ curl 10.244.1.75
My nginx-1
$ curl 10.244.0.30
My nginx-2
```
## 認識 StatefulSet

### 建立 Service StatefulSet
```
$ echo 'apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
clusterIP: None
selector:
app: nginx' > svc-nginx.yaml
$ kubectl apply -f svc-nginx.yaml
service/nginx created
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.98.0.1 <none> 443/TCP 13d
nginx ClusterIP None <none> <none> 14s
```
* 觀察 Service StatefulSet
```
$ kubectl get service -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.98.0.10 <none> 53/UDP,53/TCP,9153/TCP 9h
$ nslookup
> server 10.98.0.10
Default server: 10.98.0.10
Address: 10.98.0.10#53
> 10.244.2.32
32.2.244.10.in-addr.arpa name = nginx-0.nginx.default.svc.k8s.org.
> 10.244.1.75
75.1.244.10.in-addr.arpa name = nginx-1.nginx.default.svc.k8s.org.
> 10.244.0.30
30.0.244.10.in-addr.arpa name = nginx-2.nginx.default.svc.k8s.org.
```
* 刪除 StatefulSet
```
$ kubectl delete -f svc-nginx.yaml
service "nginx" deleted
$ kubectl delete -f statefulset-nginx.yaml
statefulset.apps "nginx" deleted
$ kubectl delete pvc html-storage-nginx-0 html-storage-nginx-1 html-storage-nginx-2
persistentvolumeclaim "html-storage-nginx-0" deleted
persistentvolumeclaim "html-storage-nginx-1" deleted
persistentvolumeclaim "html-storage-nginx-2" deleted
```
## k8s 名稱解析
```
[]bigred@m1:~$ ks -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-565d847f94-2wvmc 1/1 Running 1 97m 10.244.0.5 m1 <none> <none>
coredns-565d847f94-f7qbs 1/1 Running 1 97m 10.244.0.4 m1 <none> <none>
etcd-m1 1/1 Running 2 97m 192.168.61.4 m1 <none> <none>
kube-apiserver-m1 1/1 Running 2 97m 192.168.61.4 m1 <none> <none>
kube-controller-manager-m1 1/1 Running 2 97m 192.168.61.4 m1 <none> <none>
kube-proxy-dbldc 1/1 Running 4 92m 192.168.61.6 w1 <none> <none>
kube-proxy-jz5sn 1/1 Running 2 97m 192.168.61.4 m1 <none> <none>
kube-proxy-kfs7w 1/1 Running 3 92m 192.168.61.7 w2 <none> <none>
kube-scheduler-m1 1/1 Running 2 97m 192.168.61.4 m1 <none> <none>
# 可以發現 endpoints 的 ip 就是 coredns 的 ip ,讓 k8s 有名稱解析的功能
[]bigred@m1:~$ kubectl get endpoints -n kube-system
NAME ENDPOINTS AGE
kube-dns 10.244.0.4:53,10.244.0.5:53,10.244.0.4:53 + 3 more... 98m
```
## imagePullSecrets
* 建立 Container Image
```
$ mkdir -p wulin/{img/alp.plus,yaml}
$ nano ~/wulin/img/alp.plus/Dockerfile
FROM quay.io/cloudwalker/alpine
RUN apk update && apk add bash curl tree nano && apk upgrade
CMD ["/bin/sh"]
$ sudo podman build -t alp.plus ~/wulin/img/alp.plus/
$ sudo podman tag localhost/alp.plus quay.io/cooloo9871/alp.plus
$ sudo podman images | grep alp.plus
quay.io/cooloo9871/alp.plus latest 4ceba006c81b 2 minutes ago 24.9 MB
localhost/alp.plus latest 4ceba006c81b 2 minutes ago 24.9 MB
```
* 上傳 Container Image
```
$ sudo podman login quay.io/cooloo9871
Username: cooloo9871
Password:
Login Succeeded!
$ sudo podman push quay.io/cooloo9871/alp.plus
Getting image source signatures
Copying blob b2d5eeeaba3a done
Copying blob 97818d372f27 done
Copying config 4ceba006c8 done
Writing manifest to image destination
Storing signatures
```
* 建立 K8S imagePullSecrets
* 自己 push 的 image 要把他變成公開才能做下載,但現在不用了,只需要做以下設定
```
$ sudo cat /run/containers/0/auth.json
{
"auths": {
"quay.io/cooloo9871": {
"auth": "Yacx29vssabcdmR5NjU2MBJUDEPK=" # quay.io 的密碼檔案
}
}
```
```
複製 auth.json 到家目錄並變更擁有者
$ sudo cp /run/containers/0/auth.json ~; sudo chown bigred:bigred ~/auth.json
建立 K8S Secret
# generic 格式
# regcred 是 secret 的名稱
# dockerconfigjson 使用docker格式
$ kubectl create secret generic regcred \
--from-file=.dockerconfigjson=/home/bigred/auth.json \
--type=kubernetes.io/dockerconfigjson
$ kubectl get secret
NAME TYPE DATA AGE
regcred kubernetes.io/dockerconfigjson 1 33s
```
* 建立 imagePullSecrets Pod
```
$ sudo podman logout quay.io/cooloo9871
Removed login credentials for quay.io/cooloo9871
$ sudo cat /run/containers/0/auth.json
{
"auths": {}
}
$ nano ~/wulin/yaml/pod-alp-plus.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-alp-plus
spec:
containers:
- name: pod-alp-plus
image: quay.io/cooloo9871/alp.plus
imagePullPolicy: Always
tty: true
imagePullSecrets: # 使用 secret
- name: regcred
$ ka -f ~/wulin/yaml/pod-alp-plus.yaml
```
* 連接 imagePullSecrets Pod
```
$ kubectl exec -it pod/pod-alp-plus -- bash
bash-5.1# curl -s http://example.com | head -n 1
<!doctype html>
bash-5.1# exit
$ kubectl delete pods pod-alp-plus
pod "pod-alp-plus" deleted
```
###### tags: `K8S`