Try   HackMD

雲原生世代最佳舵手 Kubernetes

Kubernetes-Oerview

目錄


Kubernetes 新一代的雲端作業系統

  • 管理實體電腦或虛擬電腦裡面的多台 Container
  • 由 Google 公司設計

單純使用 Podman/Docker 會面臨的挑戰

  1. 單點毀損
    Podman/Docker 啟動的 App Container 只會在一個機器上,如果該主機掛了,這個 Application 怎麼辦?
    我們希望 Application 擁有容錯率,他能夠在另外一台 Podman/Docker 主機啟動一樣的 App Contianer,但 Podman/Docker 沒辦法自動做到這件事,只能靠工程師手動在另外一台機器上去做部署。
  2. 高可用性(High Availability)
    High Availability 的意思是,同一個 Applcation 在不同機器上同時都在運作,可以想成本尊跟分身的概念,如果一台電腦故障了,另外一台電腦上的 App 還是能持續提供服務。
  3. 自動橫向擴充
    假設當前的 Application 只有一個本尊跟分身,但萬一今天下午發生了一些事件,譬如應用系統是一個售票網站,在下午三點開賣,一堆流量灌進來,這時候我們就會希望有一個系統來會自動幫我們做到橫向擴充,英文專有名詞叫 Auto-scaling,也就是自動多長幾個分身出來一起提供服務。
  4. Rolling updates
    為了要讓企業的 Application 能達到長長久久穩穩當當,就還會需要 Rolling updates,因為任何一個企業開發的應用系統,一定會有第一代、第二代、第三代,會新增功能或刪除功能,也就會產生不同的版本,這時候就會需要系統要可以自動幫我們做進版跟退版。
  5. 資料儲存(Storage)
    Application 在運作的時候,一定會有資料的產生,所以還要幫 app 設計資料永存的解決方案,要讓 app 的資料能夠存的穩穩當當。

Kubernetes 最高戰略目標

幫企業 run Application ( 應用系統 ) run 的長長久久、穩穩當當

Kubernetes 的能力介紹

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

  • Replication of components (Pod)
    • 同樣的 pod 可以複製很多台出來 (本尊與分身的概念,分身也會做事)
    • 叢集,同一個服務會有好幾個 pod 來提供服務,避免單點損毀的情況,提高可用性 (HA) 與容錯
  • Auto-scaling
    • 自動橫向擴充,可以依照暴漲的流量擴充 pod
    • 一開始只做一個本尊和一個分身的 pod (兩個相同的應用系統再跑),當偵測到同一時間有大量連線時,會自動長出一樣的應用系統出來一起作業,連線恢復正常時,多的分身會消失
  • Load balancing
    • 平衡負載,讓每個 pod 都能平均分工
  • Replication of components + Auto-scaling + Load balancing 能達到隨需擴增
  • Rolling updates
    • 滾動式更新,專門給程式設計師使用,因增加或修改功能會更新程式版本,也就是進版或退版
    • 當 Application 部屬到測試環境的 K8S Cluster 時,可檢查程式碼有無死邏輯,當服務發現有異常時,程式會退版,確認都無問題後,再部屬到 Production 環境
    • Example: 藍綠部屬、金絲雀部屬
  • Logging across components
    • 每台 pod 運作的 log 檔都會記錄下來
    • grafana ( 發音 : 寡發哪 ) : 透過儀錶板來監控所有的 Container
  • Monitoring and health checking
    • 透過 log 來監控 pod 的狀態和對 pod 進行健康檢查
  • 由於在整個 K8S Cluster 上面會跑多個應用系統,所以需要 Logging across components + Monitoring and health checking 來幫助我們維運
  • Service discovery (DNS Server)
    • DNS Server 的功能 : 將人看得懂的網站網址 (URL),變成 IP 位址
    • 由 CoreDNS System 得知,你的 Service 會對到哪一個 pod,會將 Service 的 Domain name 轉成綁在 Service 上的 pod 的 IP 位址。
    • Domain name wiki link
  • Authentication
    • 身分認證,透過憑證 ( 公鑰 and 私鑰 ) 作業
    • 在 K8s 中沒人在打帳號密碼,全靠憑證作業。

Google has given a combined solution for that which is Kubernetes, or how it’s shortly called – K8s.

甚麼時候會需要用到隨需擴充?

  • 網站伺服器,遇到多人大量同時連線時會需要,例如 : 演唱會訂票系統, 台鐵在節慶時的訂票系統
  • 擴充的方式 : 金絲雀 ( 之後會學的 )

雲原生作業系統 - Kubernetes 大架構

這張圖中,有3台虛擬電腦

  • kubernetes master 的 hostname : m1
  • Worker node 的 hostname : w1 和 w2
    • 可以視 Application 的複雜程度新增 Worker node 主機
  • kubernetes 最小的運作單位為 pod ( 豆莢 )
    • pod 由一台或以上的 Container 組成
    • 同一個 pod 裡的多台 Container 會共用同一張網路卡,網路卡對內透過記憶體溝通(localhost),對外有同一個 Pod 的 IP 位址 。
    • 一個 pod 裡面 3 台 Container 跑的 Application(舉例,Container 1 跑 nginx 的網站; Container 2 跑 MYSQL 資料庫; Container 3 跑 log Server )= > 可以理解為 3 個 Container 跑企業用的 Application 在一個 pod 裡面。

Master node

  • Master node 裡一定會有 4 個 pod 分別是 API ServerSechedulerController-Manageretcd
  • API Server
    • 負責接受前端管理者透過 CLI ( Command Line Interface, 命令模式 ) 下達的命令,例如 : kubectlkubeadm
    • 也接受 Web UI ,在瀏覽器的網頁上點點選選,對 API Server 下達命令,例如 : Rancher

      Rancher 這家公司還有推出 K3s ( 無肥肉、身材好、精實型的 K8s )

    • Authentication 身分認證,你是誰
    • Authorization 授權,你有什麼權限,可以做什麼事情
  • Secheduler
    • 負責安排 pod ( 企業的 application ) 要在哪一台工作主機上 run
    • 安排的方式不只看記憶體,會有更複雜的演算法,溝通的對象為該工作主機的 kubelet
  • Controller-Manager
    • 會負責照顧 node 裡面的 pod ,如果一台 node 裡面的 pod 掛了,它會去請 Scheduler 幫我們把 pod 建立回來,但是不一定會在原來的 node 把 pod 建立回來。 ( Kubernetes 內建的容錯功能 )
    • 會監控 pod 的狀態,如果發現很多 connection 連到 pod ,它就會請 Sechedular, 幫忙把 pod 複製出來,也就是隨需擴增。
  • etcd
    • 一個資料庫。
    • 存放整個 K8S 系統運作的重要資料,e.g., node 裡面會存/跑什麼 application, pod 叫什麼名字 等。

Worker node

  • kubelet
    • 在背景執行的程序
    • 把 Container-Runtime-Engine (Podman, Docker, CRI-O) 做出來的一個或多個 Container 包成一個 pod
    • 實際上 Podman, Docker, CRI-O 會透過 runc/crun (有人根據 OCI 組織制定的 Runtime spec 寫出來的程式) 來將 Container 建立出來。
    • Docker 用的是 runc 來建立 Container,Podman 用的是 crun ,CRI-O 預設是用 runc
    • runc/crun 在建立 Container 時,一定會需要 bundle ( runtime 設定檔 + rootfs 檔案系統目錄 )
  • Kube-proxy ( 哭 ㄅ ㄆㄨㄚ seeˇ )
    • 一個 pod
    • 讓不同 node 中 pod 的網路能互相連接
    • 在我們的環境中,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 的相依檔。


Kubernetes CRI 的架構演進圖

  • 最上面是第一代的流程,因為 K8S 不紅,所以自己寫了一個 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 的,跟下面最新的架構比,效率還是差了點。
  • 目前:使用 CRI-O 這個專案,特別為 K8S 量身訂做,直接翻譯 kubelet 的話,效率最優。
    • 而且直接對 runc 溝通,不用再經過好多流程才產生 container
    • CRI-O 是 open source 的專案,老師目前使用最下面的架構,kubernetes 會直接透過 CRI-O 來跟 rnc 溝通產生 Container
  • 結論
    • K8S 不需要 Docker 就可以建立和跑 Container 了
    • 目前 Google Cloud 、 AWS 和 Microsoft Azure 三大公有雲使用的是第三代的架構,Containerd 直接和 kubernetes 做溝通請 runc 產生 Container。
    • Docker 並沒有遵循 CRI 的標準

選擇 Kubernetes Cluster

  • Cluster 叢集
  • kubernetes Cluster 就是 kubernetes 會用到的電腦,代表 K8S 是由多台電腦組成的作業系統

Managed Kubernetes Services

意思是有人已經幫你設好了 Kubernetes

  • 雲端商的 Kubernetes (資料安全上會有問題)
    • Amazon Elastic Kubernetes Service | Amazon 做出來的 K8S ,簡稱 : EKS/Amazon EKS
    • Google Kubernetes Engine | Google 做出來的 K8S ,簡稱 : GKE
    • Azure Kubernetes Service | Microsoft 做出來的 K8S ,簡稱 : AKS
    • 服務供應商也會有像是 quay.io 的 image 管理中心,供使用者 PullPush image,還會有 CI/CD ,讓程式設計師有健身房可以用,達到敏捷開發。
    • 不同雲端供應商都追加了不同的系統功能,但其核心都是 Kubernetes Cluster
  • K8S 商軟(distribution (“distro”) of Kubernetes)
    • 由大公司幫我們做好 K8S Cluster ,我們只需要付錢錢,就可已買回來自己管理
    • RedHat OpenShift | RedHat 做的 Kubernetes
    • VMware Tanzu | VMware 做的 Kubernetes
    • Rancher | K8S (前端用的是 Web UI,價格較親民) + K3S(比 K8S 更輕量,可用於物聯網。)
    • 還有很多 K8S 商軟
  • Self-Hosting Kubernetes
    • 自己建置 Kubernetes。

Kubernetes Cluster 核心運作

Open Interfaces in Kubernetes

  • CRI Container Runtime Interface , Kubernetes 建置 Container 的標準
    • 選用 CRI-O 的原因是因為,CRI-O 為了 K8S 而活,效能最好,支援多種 OCI Runtime , 不只有 runC/Crun ,還可以選用其他的,相容度很高
  • CNI Container Network Interface , 網路的標準
    • 讓不同 node 中的 pod 網路可以互通有無,還有讓別人可以透過網路連接到我們建立對外的 Service ( Routing Table and NAT and Iptables )
    • 例如 : Flannel 網路套件
  • CSI Container Storage Interface , 儲存系統的標準,
    • 其實講的就是 Volume , bypass overlay2,讓 pod 的資料可以達到永存。e.g., Rook、ceph、NFS (Net Work Filesystem)
  • 陳老師建置出來的 K8S CRI 用的是 CRI-O 這個專案,CNI 用的是 Flannel 網路套件,CSI 用的是 NFS
    以上3種標準會衍生出很多不同的專案,但這些專案都是根據這3種標準做出來的
    Kuberentes 可以根據不同標準做出不同架構的 Kubernetes

k8s 的 scale out ,自動擴充出分身的 pod ,硬體設備要夠強,不然會有災難。


Kubernetes Pods (CRI 的規矩)

  • pod 不會存在兩個 node 或以上,只會在一個 node 上
  • pods 的總記憶體用量為所在的 node 記憶體的總量乘上 80%,保留 20% 給 node 主機作業要使用。

Kubernetes Pods (CNI)

  • 不同 pod 裡的 Container 之間的溝通無法使用 localhost
  • 一個 pod 會有一個 IP 位址和一片網路卡,裡面的多個 Container 會共用 pod 的網路卡和 IP 位址
  • pod 對外有 IP 位址,對內多個 Container 是用 localhost 這個名稱來互通有無 (裡面是記憶體作業)

Kubernetes Pods (CSI)

  • 上圖為 NFS 專案的架構,CSI 還有很多種專案
  • Container 使用的 overlay2 檔案系統,如果大量的新增、修改和刪除檔案,檔案系統會掛點,所以他不適合儲存大量的資料。
  • pod 可以透過 bypass 跳過 overlay2 檔案系統, 使用 K8S 的 volume 來做儲存,這個 volume 會透過網路連到 NFS 。
  • NFS(共享資料夾) 是一台機器(網路儲存設備,e.g., qnap),把一堆資料夾,透過網路分享給不同的 pod 來使用,如果 NFS 使用的是 linux 系統,那麼 pod 就會用 ext4 檔案系統做儲存
  • pod 的 volume 可以連到我們的 big data 的平台 > ROOK
    • ROOK ,類似 Hadoop ,他的底層運作和 Hadoop 很像

Kubernetes Pods (Scheduling)

  • Scheduler 專門安排 pod 在哪一個 node run,靠的是 Labels (標籤)
  • 我們的 pod 可以貼 Label ( 標籤 ) ,標籤儲存在資料庫裡面
  • Pod 可指定如何佈署到帶有特定 Labels 的 Node

Kubernetes Pods (High Availability)

  • pod 可以有很多本尊和分身,當他們同時在運作時,只要資料的儲存系統做的好( 像是透過 ROOK ),多個 pod 可以並行運作,可以解決突然在短時間大量連線,造成系統當機的狀況。
  • 結論:Pod 可輕易做到高可用性 ( HA )

Kubernetes Master 容錯

  • 在 Kubernetes Master 裡面的 etcd 儲存整個 K8S 的 meta data , 只要 Master 這台主機掛了, 整個 K8S 再見,所以要做容錯
  • 多台 Master 主機的 etcd 會自動同步
  • 根據 Raft 演算法可以算出如何做到 Master 主機的容錯。
    • etcd: 計算故障容許節點數為 (N-1)/2,其中 N 為 K8S Master 數量, 而由 (N/2)+1 計算出 Majority, 只要滿足這數量, 代表 K8S 可以新增 Service, Deployment 等功能。
  • 當一個 K8S Cluster 只存在 1 台 或 2 台 Master 無法做到容錯。
  • 3 台 Master 可以允許壞一台 Master 主機, 如果再壞一台,會進入鎖死狀態,代表 Application 可以繼續 run ,但無法新增刪除修改資料。
  • 如果要壞 2 台 Matser ,必須要有 5 台 Master 主機
  • 如果只有 3 台實體主機,要如何做到 K8S 的容錯 ?
    將 3 台實體主機都做成 Master 主機,透過以下命令
# 設定 K8S Master 可以執行 Pod
$ kubectl taint node m1 node-role.kubernetes.io/control-plane:NoSchedule-

output : 
node/m1 untainted
  • 讓 master 主機也能 run pod ,但如果要這樣做,一開始在規劃主機的硬體規格時,要盡量規劃強一點。

Kubernetes-Cluster-build

目錄


將 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,安裝帶有不受信任簽名或沒有簽名的套件
  • 因為手工建立 K8S 的關係,所以可以用到最新的版本

建立 K8S Master

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。
  • 以上兩個設定其實只要是私有 IP 就 ok 。
  • --service-dns-domain=k8s.org,因為 K8S 內部有 Service Discovery ,所以要設定 Domain Name ,對外的應用系統連接網址使用此設定名稱,在外面做案子的話,就用公司的 Domain Name 來命名。
  • --apiserver-advertise-address $IP,將 m1 主機的 IP 位址 設定為 Kubernetes Master 的 API Server 的 IP
  • Kubernetes Service Discovery 介紹連結

Kubelet 設定為系統自動啟動

$ sudo rc-update add kubelet default
  • 為何 Kubelet,需設定為系統自動啟動 ?
    因為它是一個 Daemon ,需要在背景一直 running 來等待 Scheduler 來找他。

檢查 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
  • 以上兩片 image 是陳老師先幫我們準備好的

設定 K8S Master

將 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 ,顯示當前使用者的 UID
  • id -g ,顯示當前使用者群組的 GID

設定 K8S Master 可以執行 Pod

$ kubectl taint node m1 node-role.kubernetes.io/control-plane:NoSchedule-
  • K8s 的每個 node 都會有標記 taint ,而 m1 這台 node 在 K8S 初始化的時候會被標記 NoSchedule 的 taint ,有了這個 taint 之後, pod 就不會建在 m1 的 node 上,這時候在規則裡面改成 NoSchedule- , 就能破格。

Kubernetes 1.24.4 版還要再執行以下命令

$ kubectl taint node m1 node-role.kubernetes.io/master:NoSchedule-

安裝 Flannel 網路套件

flannel 網路套件,動用的技術 : Static Route

$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
  • 因建立 K8S 設定 POD 的 Subnet 為 10.244.0.0/16, 而 flannel 內定網路 ID 為 10.244.0.0/16, 所以不需修改 kube-flannel.yml

安裝好後,將 m1 主機重新開機

$ sudo reboot

檢視 Kubernetes 系統狀態

檢查 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 系統的 Metadata
  • kube-scheduler-m1,負責啟動 Pod
  • kube-controller-manager-m1,負責監控與維護 Pod
  • flannel + 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

w1, w2 安裝 kubelet 及 kubeadm 命令

# 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'
  • work node 不裝 kubectl 的原因是因為我們規劃的管理主機在 m1 ,work node 不需要管理其他機器。

w1, w2 加入 K8S Cluster

在 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
JOIN 變數解析

描述 : 將 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

設定 K8S Worker 標籤

在 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=
  • 對 w1 和 w2 node 的 role 貼上 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

Kubernetes 檢測

建立 K8S POD

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 
  • READY 的 1/1
  • 分子 -> 幾台正在 running 的 Container ; 分母 1 個 pod 有幾台 Container
  • -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
  • 兩個 pod 之間可以互通有無
  • 工作主機 和 master 主機也可以的到 nginx 那台 pod


tags: 系統工程

Kubernetes-First-Steps

目錄


建立 K8S 應用物件方式

  • kubectl run (命令式 Imperative) - Manage K8s object (POD, Controller, Service) using CLI
    • 透過以上命令式建立 pod 能做到的功能是有限的。
    • e.g., 像要讓 pod 到指定 node 去 run 是做不到的; K8S 還有很多物件是無法用 kubeclt run 的方法產生
    • kubectl run (Imperative) 這命令適合做概念性(物件的雛形)驗證 (Proof of Concept;POC)
    • 結論 : kubeclt run 的方法 對於 K8S 能產生和設定多少物件是有限的
  • kubectl create/apply (聲明式 Declarative) - By defining K8s objects in yaml file
    • K8S 產生物件的正規做法,但有個必要條件,須先設計 yaml 檔,才能產生對應的物件
    • kubectl create (Declarative) 與 yaml 設定檔適合建立複雜的企業應用服務
  • "Imperative" is a command - like "create 42 widgets".
  • "Declarative" is a statement of the desired end result - like "I want 42 widgets to exist".
  • 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.

Using Namespace

  • 在 K8S 裡的 Namespace ,可以想像成大樓裡的辦公室,裡面可以有很多人 (Container),辦公室中還可以有很多桌子、椅子和影印機等物件(pod, deployment, Service等)
  • Partition Cluster resources.
  • Kubernetes Namespaces can be used to divide a cluster into logical partitions allowing a single large Kubernetes cluster to be used by multiple users and teams, or a single user with multiple applications. Each user, team, or application running in a Namespace, is isolated from every other user, team, or application in other Namespaces and they operate as if they are the sole user of the cluster (note that Namespaces do not provide network segmentation).
  • Namespaces 有以下幾個特點
    • 在同一個 Kubernetes Cluster 中,每個 Namespaces 的名稱都是要獨特的
    • 當一個 Namespaces 被刪除時,在該 Namespace 裡的所有物件也會被刪除
      • 注意!只會刪除 K8S 產生的物件,如果 K8S 的物件有產生檔案或目錄是不會被刪除的。
    • 可以透過 Resource Quotas 限制一個 Namespaces 所可以存取的資源
      • 不只限制可用的運算資源,還能限制 K8S 的物件
      • 有防火牆的機制,可以允許或禁止 pod 連到另一個 Namespace 的 pod ,或是讓一個 pod 可以上網
  • 結論 : K8S 的物件一定要指定一個 Namespace 給它 run。開發應用系統必定會建立專案目錄, 設計 K8S Application 需在 Namespace, 來運作

檢視 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 中。
  • Namespace 參考文章連結 : https://ithelp.ithome.com.tw/m/articles/10197186

K8S default 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

Assigning Pods to Namespace

# 建立工作目錄
$ 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 產生 pod
  • n1,pod 的名字
  • -n myoffice,指定等等的 pod 會建在 myoffice 這個 namespace
  • -- sleep infinity--後面放的是 Container 要執行的程式 ,這裡是 sleep infinity
    • forever sleeping ( never awake until escape )

檢視 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
  • 這裡看到的是在 default namespace 的 pod 資訊
$ kg pods -n myoffice
NAME   READY   STATUS    RESTARTS   AGE
n1     1/1     Running   0          22m
  • 這裡看到的是 myoffice 這個 namespace 裡面 pod 資訊
$ 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 這個 pod
  • 用法須更正為:
    • kubectl 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 Server
  • 它會幫我們把 www.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 名字 : n2
    • namespace,設定 pod 所在的 namespace : myoffice
    • labels,pod 上貼一個標籤
  • spec,設定 pod 內部的結構
    • hostname: n2,設定 Container 的 hostname,多個 Container 也會共用同一個 hostname
    • contaienrs,設定 Container 的資訊
    • command,container 要先執行的命令
      • yaml 檔中的 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

切換 Namespace

$ 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
  • 在 K8S 系統中,我們 bigred 的帳號,是 kubernetes-admin (由 ~/.kube/config 這個檔案得知)

OpenSSH 實務應用

Container 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
  • 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

第一次接觸 Kubernetes App

建立 K8S 應用物件方式

  1. kubectl run (命令式 Imperative) - Manage K8s object (POD, Controller, Service) using CLI
  2. kubectl create/apply (聲明式 Declarative) - By defining K8s objects in yaml file
    • yaml ,全名 : Yet Another Markup Language ( 因為不用再看到 xml 檔了 )

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 設定檔適合建立複雜的企業應用服務

建立 Demo App

# 建立一個 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>

建立 OpenSSH Pod

編輯 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
  • 在 K8S yaml 檔的 command 能破格 Dockerfile 設定的 ENTRYPOINT,讓 Container 跑 command 宣告的命令
  • 但是 Container run 的程式還是要在前景執行
  • 所以 command 最後還是要宣告 /usr/sbin/sshd -D,讓 Container 有個 SSH Server 一直在前景執行的命令
  • /bin/bash -cc 的意思是 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 只要沒有動用 volume ,當 pod 被刪除時,資料通通消失

將 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>
  • 發現 pod 重新建立,Pod_IP 變了,資料也沒啦 !
  • 在 K8S 的系統中,只要 node 主機重開,它上面 run 的 pod 都會浴火重生(重建),pod 沒有 volume 的話,資料通通再見 !

Access Kubernetes Pods from outside

編輯 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: 22101kube-proxy 會在 pod 所在主機開一個 22101 的 port,對應到 pod 裡 Container 開的 22 port,效能優於 kubectl port-forward
  • hostport 示意圖

  • nodeselector,透過 node 上的 labels 來決定 pod 要建在哪台 node 上

讓 mydb pod 也用 hostport 開

先建立工作目錄

$ 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

檢視 Linux capabilities

  • Linux capabilities ,規範 Container 可以做什麼事,而規範 Container 等同於規範 pod
$ 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 ,會需要有這個 Capabilities
  • SSH 通訊協定一定會需要 "AUDIT_WRITE", "SYS_CHROOT" 這兩個 Capabilities,才能讓 CRI-O 做出來的 Container run openssh 的時候,能夠讓 openssh Server 與外部連接,給別人從外面連進來,如果沒有以上兩個 Capabilities ,即使要用 root 去登入也沒用。設定後要記得重新開機。

CRI-O 規範 Linux capabilities 的檔案

$ 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
...

連接與移除 Demo App

# 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 File

建立 OpenSSH Pod

# 建立專案目錄,編輯 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 分配到第一台工作機

再次修改 yaml 檔

$ 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"] ,一樣會被破格,強制要跑命令

建立 File Browser Server

$ 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 mode
  • filebrowser 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

建立 File Browser Server 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
  • selector: ,選擇有貼 pod-fbs 這個標籤的 pod
  • externalIPs: ,設定對外服務的 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>

FBS 架構示意圖

  • 多建立 Service 這個物件的原因是因為,K8S 的 pod 很容易浴火重生,一旦重建, pod IP 會發生變動,如果這時候我們 pod 是一個網站,這個網站的 IP 位址時不時更換,會搞死人。
  • 所以這時候需要透過 Service ,服務的 IP 在 K8S 中是固定的,不會因為主機重開,Service IP 就變動,所以只要讓 Service 透過 labels 綁住我們的 pod ,這樣即使 pod 浴火重生,也沒關係,我們一樣可以靠 Service 連的到我們的 pod

提問 : 如何得知檔案丟到網站後,存放在哪?

答 : 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 怎麼處理 ?

對 pod 的 yaml 檔新增 hostPath PV

編輯 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 目錄的規則)。
  • K8s 官網 hostPath 範例連結
  • 如果刪除 pod 再重建一次, pod 可能會跑到別台 node 主機,如何解決 ?
    答 : 針對第一次建立 pod 時,如果 pod 在 node 1 主機上,則每次重建 pod 時,就要安排在這台 node 主機上
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
tags: 系統工程

Kubernetes-Objects

目錄


認識 K8S 叢集物件

  • CustomResourceDefinitions ,自定義 K8s 的物件

K8S Object Short-names

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

Node & POD 示意圖

  • volime 儲存體 ,供 pod 使用
  • 一個 pod 可以跑很多台 Container ,代表可以 run 很多 企業要用的 Application

認識 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 中的 Container 會共享 volume , Linux Namespaces, cgroups, IP 位址

Single Container Pod 建立與連接

# 在 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

Single Container Pod 自動重新建立

重新建立並執行 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

K8S Node 系統重新啟動

# 建立並執行 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


Single Container 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

建立 Share PID Namespace Pod

先建立 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 的結構說明
    • 有兩個 image 代表有兩台 Container , 名字分別是:derby 和 shell ,他們共用 hostname: xyz
    • shareProcessNamespace: true ,兩台 Container 用同一個 pid 的 Namespace
    • tty: true ,給一個虛擬終端機,因為 quay.io/cloudwalker/alpine 這個 image 內定執行的命令是 sh ,所以一定要有一台虛擬終端機
    • imagePullPolicy: Always ,代表每次都要下載 image
    • imagePullPolicy: IfNotPresent , 代表沒有 image 再下載
    • 如果企業無法上網,就要宣告imagePullPolicy: Never
  • 最後要輸出的檔名可以*.yaml 或是 *.yml 都可以。

k8s 標準的運作,透過 yaml 檔作業

$ kubectl create -f yaml/sharepid.yml 

檢視 Pod 內部資訊

$ 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

檢測 Pod 內部 Process

登入 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 (睡一輩子)站在資安的角度:全球最安全的命令,老師翻成中文:睡夢羅漢

更多 POD 設定

$ 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
  • shareProcessNamespace: false ,如果不宣告,內定是 false。
  • restartPolicy: Never ,設定 pod 永不重啟。

建立 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

DNS for Service

取得 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 : 53
  • 透過 kubectl describekube-dns 詳細的資訊,可以看到 Endpoints: 有兩個 IP 位址,代表後面有兩個 pod 在運作
  • 因 pod 浴火重生 IP 位址可能會發生變化,只有 K8S 的服務會鎖在一個 IP 位址,透過 Selector 來抓到對應的 pod
    • 服務不會浴火重生,它只是一個儲存在 etcd 裡面的資料區塊,記錄著自己的 IP 位址,及自己真正在運作的 pod 是誰,還有紀錄整個 K8S pod 的 IP 位址及它們的 domain name

  • 當 K8S 內部的 pod 要上網時, kube-dns 會提供 名稱解析的服務,讓我們的 pod 得到要上網的 IP 位址。
  • 當我們建立一個 mysrv 的 service ,這時候他會去和 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 server10.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
    
    • subdomain ,可以讓我們在 ping 的時候,在 pod 的名字後面 + 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

新增 Pod 的搜尋網域名

$ 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

建立 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 秒,時間到會停止
tags: 系統工程