# kubernet筆記 docker三項元素 (比喻:image為模具,contaier為蛋糕,repository為放模具的收納櫃) **1.映像檔image** Docker 映像檔是一個模板,用來重複產生容器實體。ex:一個映像檔裡可以包含一個完整的 MySQL 服務、一個 Golang 的編譯環境、或是一個 Ubuntu 作業系統。 透過 Docker 映像檔,我們可以快速的產生可以執行應用程式的容器。而 Docker 映像檔可以透過撰寫由命令行構成的 Dockerfile 輕鬆建立,或甚至可以從公開的地方下載已經做好的映像檔來使用。 **2.容器 Container** 容器是用映像檔建立出來的執行實例。它可以被啟動、開始、停止、刪除。每個容器都是相互隔離、保證安全的平台。 *Docker 映像檔是唯讀(read-only)的,而容器在啟動的時候會建立一層可以被修改的可寫層作為最上層,讓容器的功能可以再擴充。 **3.倉庫 Repository** 倉庫(Repository)是集中存放映像檔檔案的場所,倉庫註冊伺服器(Registry)(ex Docker Hub)上則存放著多個倉庫。 Kubernetes四個元素 **1.Pod** Kubernetes 運作的最小單位,一個 Pod 對應到一個應用服務(Application) ,舉例來說一個 Pod 可能會對應到一個 API Server。 * 每個 Pod 都有一個身分證,也就是屬於這個 Pod 的 yaml 檔 * 一個 Pod 裡面可以有一個或是多個 Container,但一般情況一個 Pod 最好只有一個 Container * 同一個 Pod 中的 Containers 共享相同資源及網路,彼此透過 local port number 溝通 **2.Worker Node** Kubernetes 運作的最小硬體單位,一個 Worker Node(簡稱 Node)對應到一台機器,可以是實體機如你的筆電、或是虛擬機如 AWS 上的一台 EC2 或 GCP 上的一台 Computer Engine。 每個Node都會有三個組件1.kubelet 2.kube-proxy 3.container runtime * kubelet 該 Node 的管理員,負責管理該 Node 上的所有 Pods 的狀態並負責與 Master 溝通 * kube-proxy 該 Node 的傳訊員,負責更新 Node 的 iptables,讓 Kubernetes 中不在該 Node 的其他物件可以得知該 Node 上所有 Pods 的最新狀態 * Container Runtime 該 Node 真正負責容器執行的程式,以 Docker 容器為例其對應的 Container Runtime 就是 Docker Engine **3.Master Node** Kubernetes 運作的指揮中心,負責管理所有其他 Node。一個 Master Node(簡稱 Master)中有四個組件:kube-apiserver、etcd、kube-scheduler、kube-controller-manager。 * kube-apiserver 1. 管理整個 Kubernetes 所需 API 的接口(Endpoint),例如從 Command Line 下 kubectl 指令就會把指令送到這裏 2. 負責 Node 之間的溝通橋樑,每個 Node 彼此不能直接溝通,必須要透過 apiserver 轉介 3. 負責 Kubernetes 中的請求的身份認證與授權 * etcd 用來存放 Kubernetes Cluster 的資料作為備份,當 Master 因為某些原因而故障時,我們可以透過 etcd 幫我們還原 Kubernetes 的狀態 * kube-controller-manager 1. 負責管理並運行 Kubernetes controller 的組件,簡單來說 controller 就是 Kubernetes 裡一個個負責監視 Cluster 狀態的 Process,例如:Node Controller、Replication Controller 2. 這些 Process 會在 Cluster 與預期狀態(desire state)不符時嘗試更新現有狀態(current state)。例如:現在要多開一台機器以應付突然增加的流量,那我的預期狀態就會更新成 N+1,現有狀態為 N,這時相對應的 controller 就會想辦法多開一台機器 3. controller-manager 的監視與嘗試更新也都需要透過訪問 kube-apiserver 達成 **4. kube-scheduler** 整個 Kubernetes 的 Pods 調度員,scheduler 會監視新建立但還沒有被指定要跑在哪個 Node 上的 Pod,並根據每個 Node 上面資源規定、硬體限制等條件去協調出一個最適合放置的 Node 讓該 Pod 跑 5. Cluster Kubernetes 中多個 Node 與 Master 的集合。基本上可以想成在同一個環境裡所有 Node 集合在一起的單位。 ![](https://i.imgur.com/bP4Tiaq.png) # 創建Docker container **安裝在ubuntu 20.04** ``` $ sudo apt-get update && sudo apt-get install docker.io ``` 查看docker版本 ``` docker version ``` **將程式包成 Docker Image** 將程式dockerize時需要建立一個Dockerfile ``` FROM node:6.2.2 WORKDIR /app ADD . /app RUN npm install EXPOSE 300 CMD npm start ``` * FROM node:6.2.2: 載入程式需要的執行環境,會根據不同需求下載映像檔 * WORKDIR /app: 在這Docker中的Linux會建立一個目錄/app * ADD . /app: 將本機端與 Dockerfile 同一層的所有檔案加到 Linux 的 /app 目錄底下 * RUN npm install: 會下載 nodejs 相依的 libraries * EXPOSE 300: 指 container 對外的埠號,再與外界溝通時使用 * CMD npm start: 透過 npm start 運行 Nodejs App **Docker Build** ``` docker build '目標資料夾' ``` **條列docker image** ``` docker image ls ``` **本機上運行containerized app** ``` docker run -p 3000:3000 -it "docker image id" ``` **上傳Docker Image至docker hub** * docker image 標記tag(tag為所建的repository) ``` docker tag a6afc7d510e9 tbj123121/docker-demo ``` **指定tag版本** ``` docker tag a6afc7d510e9 tbj123121/docker-demo:v1.0.0 $ docker tag {DOCKER_IMAGE_ID} {YOUR_ACCOUNT_NAME}/{REPOSITORY_NAME}:{VERSION} ``` **上傳到指定repository** ``` doker push tbj123121/docker-demo ``` # Pod與kubectl指令 **建立一個Pod** ``` # my-first-pod.yaml apiVersion: v1 kind: Pod metadata: name: my-pod labels: app: webserver spec: containers: - name: pod-demo image: tbj123121/docker-demo ports: - containerPort: 3000 ``` * apiVersion: 代表目前 Kubernetes 中該元件的版本號 * metadata: 有三個重要的 Key,分別是 name, labels, annotations 1.metadata.name: 指定這個 Pod 的名稱 2.metadata.labels: Kubernetes 會透過 Label Selector 將Pod分群管理 3.metadata.annotations: annotations 的功能與 labels 相似。 * spec: 1. container.name: 設定 container 的名稱 2. container.image: 根據docker registry提供可下載路徑 3. container.ports: 指定該 container 有哪些 port number 是允許外部資源存取 **在kubernets Cluster建立Pod物件** ``` kubectl create -f my-first-pod.yaml #查看pods狀態 kubectl get pods #詳細pods物件資訊 kubectl describe pods my-pod ``` **與pod中container互動** minikube status 查看目前 minikube-vm 是使用本機端哪個內部網址 ``` minikube status ``` 創建一個Service ``` kubectl expose pod my-pod --type=NodePort --name=my-pod-service ``` 查看目前運行的service有哪些 ``` kubectl get services ``` **常見的 kubectl 指令** ``` #取得 Kubernetes Cluster 中所有正在運行的 Pods 的資訊 kubectl get pods #加上--show-all,則會顯示所有 Pods kubectl get pods --show-all #取得某一個 Pod 的詳細資料 kubectl describe pod <pod> #將某一 Pod 中指定的 port number expose 出來讓外部服務存取(建立一個新的 Service 物件) kubectl expose pod <pod> --port=<port> --name=<service-name #將某一 Pod 中指定的 port number mapping 到本機端的某一特定 port number kubectl port-forward <pod> <external-port>:<pod-port> #新增 Pod 的 Labels kubectl label pods <pod> <label-key>=<label-value> #刪除pods kubectl delete pod {POD_NAME} ``` # Replication Controller Replication Controller是Kubernetes上用來管理Pod的數量以及狀態的controller * 每個Replication Controller都有屬於自己的 yaml 檔 * Replication Controller設定檔中可以指定同時有多少個相同的Pods運行在Kubernetes Cluster上 * 當某一Pod發生crash, failed,而終止運行時,Replication Controller會幫我們自動偵測,並且自動創建一個新的Pod,確保Pod運行的數量與設定檔的指定的數量相同 ``` apiVersion: v1 kind: ReplicationController metadata: name: my-replication-controller spec: replicas: 3 selector: app: hello-pod-v1 template: metadata: labels: app: hello-pod-v1 spec: containers: - name: my-pod image: tbj123121/docker-demo ports: - containerPort: 3000 ``` * spec.replicas & spec.selector: spec.replicas中,必須定義Pod的數量,以及在spec.selector中指定要選擇的Pod的條件(labels) * spec.template: 在spec.template中定義pod的資訊,包含Pod的labels以及Pod中要運行的container。 * spec.template.metadata: 則是Pod的labels,metadata.labels必須被包含在select中,否則在創建Replication Controller物件時,會發生error。 * spec.template.spec 定義container **Scalingg pod數量** ``` kubectl scale --replicas={數量} -f {replication的yaml檔位置} ``` # Replica Set 與 Replication Controller最大的差異在於,Replica Sets 提供了更彈性的selector ``` apiVersion: apps/v1beta2 # for kubectl versions >= 1.9.0 use apps/v1 kind: ReplicaSet metadata: name: my-replica-set spec: replicas: 3 selector: matchLabels: env: dev matchExpressions: - {key: env, operator: In, values: [dev]} - {key: env, operator: NotIn, values: [prod]} template: metadata: labels: app: hello-pod-v1 env: dev version: v1 spec: containers: - name: my-pod image: tbj123121/docker-demo ports: - containerPort: 3000 ``` # Deployment * 部屬一個applications * 協助applications升級到某版本 * 服務升級過程中做到無停機服務遷移(zero downtime deployment) * 可以Rollback到先前版本 ``` apiVersion: apps/v1beta2 # for kubectl versions >= 1.9.0 use apps/v1 kind: Deployment metadata: name: hello-deployment spec: replicas: 3 selector: matchLabels: app: my-deployment template: metadata: labels: app: my-deployment spec: containers: - name: my-pod image: tbj123121/docker-demo:latest ports: - containerPort: 3000 ``` # 透過 kubectl 操作 Deployment 物件 ![](https://i.imgur.com/nxaCTIj.png) # 建立外部服務與Pods的溝通管道 - Services Service可以為Pods做到的事 * 創建一個ClusterIp,讓Kubernetes Cluster中的其他服務,可以透過這個 ClusterIp 訪問到正在運行中的Pods,在每次創建 Service 物件時,Kubernetes 就會預設一組virtual IP 給 Service 物件。除非我們在 Service yaml 指定想要的 virtual IP,否則 Kubernetes 每次都會隨機指定一組virtual IP。 * 創建一個NodePort,讓在 Kubernetes Cluster外但在同一個 Node 上的其他服務,可以透過這個 NodePort 訪問到 Kubernetes Cluster 內正在運行中的 Pods。 **利用kubectl expose創建Service物件** ``` kubectl expose deploy {deployment_name} --type=NodePort --name={service_name} ``` Service的yaml檔範例 ``` apiVersion: v1 kind: Service metadata: name: hello-service spec: type: NodePort ports: - port: 3000 nodePort: 30390 protocol: TCP targetPort: 3000 selector: app: my-deployment ``` * apiVersion: Service使用的Kubernetes API是v1版本號 * metadata.name: 指定該Service的名稱 * spec.type: 指定Service的型別,可以是NodePort或是LoadBalancer * spec.ports.port: 指定,創建的Service的Cluster IP,是哪個port number去對應到targetPort * spec.ports.nodePort: 指定Node物件是哪一個port numbrt,去對應到targetPort * spec.ports.targetPort: targetPort是我們指定的 Pod 的 port number * spec.ports.protocol: 目前 Service 支援TCP與UDP兩種protocl,預設為TCP * spec.selector: selector則會幫我們過濾,在範例中,我們創建的Service會將特定的port number收到的流量導向 標籤為app=my-pod的 Pods # kubernetes的labels *Labels就是一對具有辨識度的key/value。* Label的特性 * 每個物件可以同時擁有許多個labels * 透過 Selector,幫縮小要尋找的物件 **動態新增 Labels** 透過 kubectl label的指令,來為Pod 新增標籤。 ``` kubectl label pods my-pod env=production #就會新增env=production的標籤 ``` **將 Pod 部署到特定的 Node 上** * 在 Node 上貼上標籤 ``` kubectl label node minikube hardware=high-memory ``` * 在 Pod 新增 nodeSelector 定義 ![](https://i.imgur.com/oqBkG6w.png) # kubernet health check 常見的兩種health check 1. 定期透過指令訪問container 2. 定期發送一個http requet給container ``` apiVersion: apps/v1beta2 # for kubectl versions >= 1.9.0 use apps/v1 kind: Deployment metadata: name: hello-deployment spec: replicas: 3 selector: matchLabels: app: my-deployment template: metadata: labels: app: my-deployment spec: containers: - name: webapp image: zxcvbnius/docker-demo ports: - name: webapp-port containerPort: 3000 livenessProbe: httpGet: path: / port: webapp-port initialDelaySeconds: 15 periodSeconds: 15 timeoutSeconds: 30 successThreshold: 1 failureThreshold: 3 ``` 重點在於livenessProbe * httpGet.path 設定 health checks 要訪問的路徑 * httpGet.port 指定要訪問的 port * initialDelaySeconds 設定當 service 剛啟動時,要延遲幾秒再開始做 health check * periodSeconds 代表每隔幾秒訪問一次,預設值為 10秒 * successThreshold 可以設置訪問幾次就代表目前 service 還正常運行 * failureThreshold 代表 service 回傳不如預期時,在 Kubernetes 放棄該 container 之前,會嘗試的次數,預設為3次。 # Secret Secrets 是 Kubernetes 提供開發者一種存放敏感資訊的方式。Kubernetes 本身也使用相同的機制( secrets mechanism) 存放 access token,限制 API 的存取權限,確保不會有外部服務隨意操作 Kubernetes API。 在 Kubernetes 存取敏感資料(sensitive data)有以下幾種常見的使用方式: * 將 Secrets 當成 環境變數(environment variables) 使用 * 將 Secrets File 掛載(mount) 在 Pod 某個檔案路徑底下使用 * 將這些 sensitive data 統一存放在某一個 Docker Image 中,並將這個 Image 存放在私有的 Image Registry 中,透過 image pull 下載到 Kubernetes Cluster 中,讓其他 Pods 存取。 **Kubernetes創建secret物件** 三種方式創建secret物件 1.從檔案匯入sensitive data ``` #先將帳號、密碼分別存入兩個不同的檔案 echo -n "root" > ./username.txt echo -n "rootpass" > ./password.txt #使用 kubectl create secret generic 指令創建一個 Secret 物件 kubectl describe secrets demo-secret-from-file #用 kubectl get 查看所有的 Secrets kubectl get secret ``` 2.從指令輸入sensitive data ``` #透過 kuectl create 指令搭配 --from-literal 直接在指令後面輸入資料 kubectl create secret generic demo-secret-from-literal \ ``` 3.透過 YAML 創建 Secret 物件 首先,須將帳號密碼用 base64 編碼 ,以 username=root, password=rootpass 為例, ``` $ echo -n "root" | base64 cm9vdA== $ echo -n "rootpass" | base64 cm9vdHBhc3M= ``` 並把編碼過的資料寫入 my-secret.yaml 中, ``` apiVersion: v1 kind: Secret metadata: name: demo-secret-from-yaml type: Opaque data: username: cm9vdA== password: cm9vdHBhc3M= ``` 透過 kuectl create 創建一個新物件, ``` kubectl create -f ./my-secret.yaml ``` **掛載 Secret 物件到 Pods 中** 1.將 Secrets 當成 環境變數(environment variables) 使用 ``` apiVersion: v1 kind: Pod metadata: name: my-pod labels: app: webserver spec: containers: - name: demo-pod image: zxcvbnius/docker-demo ports: - containerPort: 3000 env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: demo-secret-from-yaml key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: demo-secret-from-yaml key: password ``` env 會去從指定的 Secret 物件 找出相對應的值。,若是該 Secret 物件是從 --from-file 創建,那麼 Kubernetes 會把檔案名稱當成 key ,檔案內容當成 value。 **透過kubectl exec進入到my-pod** 2.將 Secrets File 掛載(mount) 在 Pod 某個檔案路徑底下使用 ``` apiVersion: v1 kind: Pod metadata: name: my-pod-with-mounting-secret labels: app: webserver spec: containers: - name: demo-pod image: tbj123121/docker-demo ports: - containerPort: 3000 volumeMounts: - name: secret-volume mountPath: /etc/creds readOnly: true volumes: - name: secret-volume secret: secretName: demo-secret-from-yaml ``` # Wordpress介紹 一個可以協助開發者快速架設好一個網站的軟體。以 PHP 和 MySQL 為主要架構。除了提供開發者許多外掛以及主題來打造個人 Blog、商業網站之外,Wordpress 本身還提供一個好用的後台,方便網站管理者修改、編輯文件。 # 在 minikube 上架設 Stateless Wordpress 1.創建一個Secret物件,存放 Wordpress application 存取 MySQL server 的密碼 *ex: wordpress-secret.yaml* ``` apiVersion: v1 kind: Secret metadata: name: wordpress-secret type: Opaque data: # echo -n "rootpass" | base64 db-password: cm9vdHBhc3M= ``` 2.創建一個deployment物件管理wordpress服務 *ex: my-wordpress-deploy.yaml* ``` apiVersion: apps/v1 kind: Deployment metadata: name: wordpress-app spec: replicas: 1 selector: matchLabels: app: wordpress-deployment template: metadata: labels: app: wordpress-deployment spec: containers: - name: wordpress image: wordpress:4-php7.0 ports: - name: wordpress-port containerPort: 80 env: - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: wordpress-secret key: db-password - name: WORDPRESS_DB_HOST value: 127.0.0.1 - name: mysql image: mysql:5.7 ports: - name: mysql-port containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: wordpress-secret key: db-password ``` 有兩個 container,分別是 Wordpress application 與 MySQL server。 * 在 Wordpress Docker Image 介紹中表示,開發者若無指定 WORDPRESS_DB_USER,則連結到 MySQL server 的 user 預設為 root * 在 MySQL Docker Image 介紹中表示,可以透過 MYSQL_ROOT_PASSWORD 設定 root 帳號的密碼 * 只需將 MySQL Docker Image 的 MYSQL_ROOT_PASSWORD 與 Wordpress Docker Image 中的 WORDPRESS_DB_PASSWORD 設置相同的密碼。Wordpress Container 就會以 root 身份連結到 MYSQL server container * 將 Database 的密碼存在 wordpress-secret 中,並將密碼設置成 環境變數 * Pod 中的 container 會透過 localhost + port number 互相溝通,所以 Wordpress container 會透過 127.0.0.1:3306 去訪問 MySQL server container 3.還需要創建一個 Service 物件,讓本機端的瀏覽器也可以訪問到 Kubernetes Cluster 中的 wordpress-deployment 物件。 ex: wordpress-service.yaml ``` apiVersion: v1 kind: Service metadata: name: wordpress-service spec: ports: - port: 3000 nodePort: 30300 protocol: TCP targetPort: wordpress-port selector: app: wordpress-deployment type: NodePort ``` # Kube-dns kube-dns 幫助在同一個 Kernetes Cluster 中的所有 Pods ,都能透過 Service 的名稱找到彼此。 Kubernetes 在每一個 Pod 創建時,都會在該 Pod 的 /etc/resolve.conf 檔案中,自動加入 kube-dns service 的 domain name 與相對應的 IP 位址。因此 其他 Pods 可以透過名稱為 kube-dns 的 Service 物件,找到正在運行的 kube-dns ![](https://i.imgur.com/Emgm2M0.png) 上圖/etc/resolve.conf中的nameserver指向的IP為kube-dns的Service。 # 實作:不同pods透過kube-dns進行溝通 **建立Secret object** *mysql-secret.yaml* ``` apiVersion: v1 kind: Secret metadata: name: mysql-secret type: Opaque data: # echo -n "rootpass" | base64 db-root-password: cm9vdHBhc3M= ``` **建立Mysql Server** *mysql-server-pod.yaml* ``` piVersion: v1 kind: Pod metadata: name: mysql-server labels: app: mysql-server spec: containers: - name: mysql-server image: mysql:5.7 ports: - name: mysql-port containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: db-root-password ``` 創建一個mysql-server-service物件 *mysql-server-service.yaml* ``` apiVersion: v1 kind: Service metadata: name: mysql-server-service spec: ports: - port: 3306 protocol: TCP selector: app: mysql-server type: NodePort ``` # 建立Wordpress App 建立wordpress透過 wordpress-pod.yaml 與 wordpress-service.yaml *wordpress-pod.yaml* ``` apiVersion: v1 kind: Pod metadata: name: wordpress labels: app: wordpress spec: containers: - name: wordpress image: wordpress:4-php7.0 ports: - name: wordpress-port containerPort: 80 env: - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: db-root-password - name: WORDPRESS_DB_HOST value: mysql-server-service ``` *wordpress-service.yaml* ``` apiVersion: v1 kind: Service metadata: name: wordpress-service spec: ports: - port: 3000 nodePort: 30300 protocol: TCP targetPort: wordpress-port selector: app: wordpress type: NodePort ``` # 高彈性部署 Application - ConfigMap **configuration:** 程式存取外部資源或是部署所需的資料,像是資料庫的所在 IP、管理者的帳號密碼,或是 Nginx 的設定檔 **ConfigMap** * 一個 ConfigMap 物件可以存入整個 configuration * 無需修改 container 程式碼,可以替換不同環境的 Config * 統一存放所有的 configuration **建置 ConfigMap** *ex: my-redis.conf* ``` bind 127.0.0.1 port 6379 maxclients 10000 maxmemory 50mb maxmemory-policy volatile-lru syslog-enabled yes dir /var/lib/redis dbfilename redis.dump.rdb databases 1 appendfsync everysec save 600 10 ``` 透過kubecyl create ``` kubectl create configmap redis-config --from-file=myredis.conf ``` **從指令設定config** 從指令設定ConfigMap的值 ``` kubectl create configmap mysql-host --from-literal=ip=127.0.0.1 ``` 刪除configmap物件 ``` kubectl delete configmap mysql-host ``` **透過 ConfigMap 配置 Nginx** **Nginx:** Nginx 是一套輕量的 HTTP 伺服器,也是反向代理的伺服器。收到用戶的請求後,可以將流量導給後端的 service,再將後端處理好的資源回傳給前端使用者。 **Nginx 配置檔案** *ex: my-nginx.conf* ``` server { listen 80; server_name localhost; location / { proxy_bind 127.0.0.1; proxy_pass http://127.0.0.1:3000; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` *ex: my-pod.yaml* ``` apiVersion: v1 kind: Pod metadata: name: apiserver labels: app: webserver tier: backend spec: containers: - name: nodejs-app image: zxcvbnius/docker-demo ports: - containerPort: 3000 - name: nginx image: nginx:1.13 ports: - containerPort: 80 volumeMounts: - name: nginx-conf-volume mountPath: /etc/nginx/conf.d volumes: - name: nginx-conf-volume configMap: name: nginx-conf items: - key: my-nginx.conf path: my-nginx.conf ``` 利用kubectl expose 將port 80指定到minikube上的port ``` kubectl expose pod apiserver --port=80 --type=NodePort ``` # Kubernetes 中實現負載平衡 - Ingress Controller ![](https://i.imgur.com/1mXe1Im.png) **Ingress:** ![](https://i.imgur.com/FjTkWaV.png) 使用 Ingress ,只需開放一個對外的 port number,Ingress 可以在設定檔中設置不同的路徑,決定要將使用者的請求傳送到哪個 Service 物件,好處是無需維護多個 port 或頻繁更改防火牆(firewall)外,可以自設條件的功能也使得請求的導向更加彈性。 *ex:將不同路徑的request對應到不同Service物件(ingress-example-1.yaml )* ``` apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-1 spec: rules: - http: paths: - path: /test backend: serviceName: test servicePort: 80 ``` 從ingress-example-1.yaml可知 * Ingress支援版本api版本為extensions/v1beta1 * 該設定檔中設定了一個規則:Node 收到流量之後,判斷流量路徑,若是請求路徑為 /test 則該流量將導到名稱為 test 的 Service 物件。 (這裡有問題可能是apiversion不合) *ex: 將不同 domain name 的request對應到不同的Service物件 (ingress-example-2.yaml)* ``` apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-2 spec: rules: - host: helloworld-v1.example.com http: paths: - path: / backend: serviceName: hellworld-v1 servicePort: 80 - host: helloworld-v2.example.com http: paths: - path: / backend: serviceName: helloworld-v2 servicePort: 80 ``` 有多個 Domain Name 同時指向一台 Node 時,可以透過這樣路徑的設置,將不同的 Domain Name 對應到不同的 Service 物件。 (這裡有問題可能是apiversion不合) *ex:支援終止SSL* 首先,透過 ingress-ssl-sceret.yaml ,將 SSL 憑證存入 Secret 物件, ``` apiVersion: v1 data: tls.crt: base64_encoded_cert tls.key: base64_encoded_key kind: Secret metadata: name: ssh-secret namespace: default type: Opaque ``` 創建好後,可以在 Ingress 物件中,透過 spec.tls 將該憑證掛載載 Ingress 底下,以 ingress-example-3.yaml 為例 ``` apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-3 spec: tls: - secretName: ssh-secret backend: serviceName: apiservice servicePort: 80 ``` **minikube上架設Nginx Ingress Controller** 創建helloworld-pod物件 *ex: helloworld-pod.yaml* ``` apiVersion: v1 kind: Pod metadata: name: helloworld-pod labels: app: helloworld-pod tier: backend spec: containers: - name: api-server image: tbj123121/docker-demo ports: - containerPort: 3000 ``` 再來創建一個helloworld-pod對應的Service物件 ``` apiVersion: v1 kind: Service metadata: name: helloworld-service spec: ports: - port: 3000 protocol: TCP targetPort: 3000 selector: app: helloworld-pod ``` **Create ingress-nginx namespace** 創建ingress-nginx的namespace ``` $ curl https://raw.githubusercontent.com/kubernetes\ >/ingress-nginx/master/deploy/namespace.yaml \ >| kubectl apply -f - ``` **Create default backend** ``` $ curl https://raw.githubusercontent.com/kubernetes\ >/ingress-nginx/master/deploy/default-backend.yaml \ >| kubectl apply -f - ``` **Create ConfigMaps** ``` $ curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/configmap.yaml | kubectl apply -f - $ curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/tcp-services-configmap.yaml | kubectl apply -f - $ curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/udp-services-configmap.yaml | kubectl apply -f - ``` **Create Nginx Ingress Controller** ``` $ curl https://raw.githubusercontent.com/kubernetes\ >/ingress-nginx/master/deploy/without-rbac.yaml \ >| kubectl apply -f - ``` **Create Helloworld Ingress** ``` apiVersion: extensions/v1beta1 kind: Ingress metadata: name: helloworld-ingress namespace: default spec: rules: - host: helloworld.example.com http: paths: - backend: serviceName: helloworld-service servicePort: 3000 ``` 開啟minikube ingress指令 ``` minikube addons enable ingress ``` # **保存 Container 中資料 - Volumes** **volumes** 1. 暫時性儲存空間 2. 共用儲存空間 volume類型 * emptyDir 該 Pod 中所有的 container 都可以讀寫 emptyDir 中的資料 ``` %範例 apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: - mountPath: /cache name: cache-volume volumes: - name: cache-volume emptyDir: {} ``` * hostPath 在 Pod 物件上,掛載 Node 的資料夾或檔案。生命週期與Node相同 ``` %範例hostpath-example.yaml apiVersion: v1 kind: Pod metadata: name: apiserver spec: containers: - name: apiserver image: tbj123121/docker-demo volumeMounts: - mountPath: /tmp name: tmp-volume imagePullPolicy: Always volumes: - name: tmp-volume hostPath: path: /tmp type: Directory ``` Node 的 /tmp 掛在 apiserver 的 /tmp 下,當 apiserver 的 /tmp 新增檔案時,可以從 Node 的 /tmp 中底下找到該檔案。 * Cloud Storage Kubernetes 也支援 AWS EBS、Google Disk 與 Microsoft Azure Disk 等雲端硬碟類型的 Volumes。 * NFS(Network FileSystem) ``` %範例nfs-example.yaml apiVersion: v1 kind: Pod metadata: name: apiserver spec: containers: - name: apiserver image: tbj123121/docker-demo ports: - name: api-port containerPort: 3000 volumeMounts: - name: nfs-volumes mountPath: /tmp volumes: - name: nfs-volumes nfs: server: {YOUR_NFS_SERVER_URL} path: / ``` # 動態提供 & 管理儲存資源 - Storage Class & PersistentVolumeClaim **Storge Class** 根據 Volumes 的提供者(provisioner)、類型(type)、所在地(Region),以及回收政策(reclaimPolicy)去定義不同的 Storage Class *ex: ebs-standard-storage-class.yaml* ``` kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: standard provisioner: kubernetes.io/aws-ebs parameters: type: gp2 zone: us-west-2 reclaimPolicy: Delete ``` * apiVersion 版本為storage.k8s.io/v1 * metadata.name 定義StorageClass的物件名稱 * provisioner 定義儲存空間使用AWS的EBS服務 * parameters.type 定義 EBS 的 的種類,若 provisioner 為 kubernetes.io/aws-ebs ,預設為 gps。 * parameters.zone 代表 EBS 放置在 AWS 的 us-west-2(Oregon) 這個 region。 * reclaimPolicy 由該 Storage Class 作為模板產出的 Volumes ,在綁定的 Pod 消失後的行為。 兩種型別Delete和Retain **PersistentVolumeClaim** PersistentVolumeClaim 透過設定好的 Storage Class 的模板,創建出所需要的資源。 *ex: my-persistent-volume-claim.yaml* ``` apiVersion: v1 metadata: name: myclaim spec: accessModes: - ReadWriteOnce resources: requests: storage: 8Gi storageClassName: standard ``` * spec.accessModes 三種Acess Modes 1. ReadWriteOnce 該 PersistentVolumeClaim 產生出來的 Volume 同時只可以掛載在同一個 Node 上提供讀寫功能。 2. ReadOnlyMany 該 PersistentVolumeClaim 產生出來的 Volume 同時可以在多個 Node 上提供讀取功能 3. ReadWriteMany 該 PersistentVolumeClaim 產生出來的 Volume 同時可以在多個 Node 上提供讀寫功能 * spec.resources.requestes.storage 請求的儲存空間大小 * spec.storageClassName 指定想使用哪個 Storage Class 作為模板。 **將動態產生的 Volume 掛載在特定 Pod 中** *ex: my-pod.yaml* ``` apiVersion: v1 kind: Pod metadata: name: apiserver labels: app: apiserver tier: backend spec: containers: - name: my-pod image: tbj123121/docker-demo ports: - containerPort: 3000 volumeMounts: - name: my-pvc mountPath: "/tmp" volumes: - name: my-pvc persistentVolumeClaim: claimName: myclaim ``` * spec .containers.volumeMounts.name 指定的 volume 的名稱 * spec.containers.volumeMounts.mountPath 在 container 中掛載的路徑 * spec.volumes.persistentVolumeClaim 指定將使用的 PersistentVolumeClaim 物件的名稱 * spec .volumes.name 給定該 PersistentVolumeClaim 物件對應的名稱,供spec .containers.volumeMounts.name 使用 # Kuberbetes 上實現排程服務 Cronjob **Cronjob** ![](https://i.imgur.com/qS0f0jn.png) 左至右為 * 分鐘(0-59) * 小時(0-23) * 每個月第幾天(1-31) * 月(1-12) * 星期幾(1-7) **在 minikube 上實現排程服務** *ex: my-cronjob.yaml* ``` apiVersion: batch/v1 kind: CronJob metadata: name: hello spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: hello image: apline args: - /bin/sh - -c - echo "Hi, current time is $(date)" restartPolicy: OnFailure ``` * spec.schedule: "*/1 * * * *" 為每一分鐘執行echo 透過指令即時查看目前 job 運行的狀況 ``` kubectl get jobs --watch ``` **查看 Logs** 透過 kubectl get 找到相對應的 Pod ``` kubectl get pods -a --show-all=true -o wide --show-labels=true ``` 印出屬於 hello Cronjob 底下的 Pod 的 log ``` kubectl logs 'Pod_Name' ``` 檢查在 Kubernetes 上所有的 Cronjob ``` kubectl get cronjobs --show-all=true --all-namespaces=true ``` # 在 Kubernetes 上監控服務的資源使用 - Heapster **Heapster** Heapster 是一個對 Kubernetes Cluster 進行監控與性能採集的系統,透過 kubelet 取得 Node 上的資訊,透過 Restful API ,將收集到的資料(metrics) 傳到後端的儲存系統。 **Influxdb** 無需外部依賴的的分散式時序資料庫(time series database),主要用於處理以及分析與時序相關的監控數據。 **Grafana** 將這些數據視覺化的平台(Dashboard),將 Influxdb 中搜集到的大規模資料變成圖表呈現出來。 **透過 Heapster +Influxdb + Grafana 監控 minikube 上的資源使用** 1. 下載 heapter ``` git clone https://github.com/kubernetes/heapster/archive/master.zip ``` 2. 解壓縮 ``` unzip master.zip ``` 3. 編輯 grafana.yaml (將 type: NodePort 的註解拿掉。) 4. kubectl create 指令創建 grafana.yaml、heapster.yaml、influxdb.yaml 5. 查看建立物件 ``` kubectl get deploy,svc -n kube-system ``` 6. 透過 minikube service 找出 Grafana 所在的 url ``` minikube service monitoring-grafana --url -n kube-system ``` # 實現 Horizontal Pod Autoscaling - HPA **Horizontal Pod Autoscaling** *ex: helloworld-deployment.yaml* ``` # helloworld-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: helloworld-deployment spec: replicas: 2 selector: matchLabels: app: helloworld-pod template: metadata: labels: app: helloworld-pod spec: containers: - name: my-pod image: zxcvbnius/docker-demo:latest ports: - containerPort: 3000 resources: requests: cpu: 200m ``` * spec.resources.requests.cpu 代表當 Kubernetes 在運行該 Pod 時,需要配置 200m CPU 給該 Pod。 *ex: helloworld-hpa.yaml* ``` apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: helloworld-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: helloworld-deployment minReplicas: 2 maxReplicas: 5 targetCPUUtilizationPercentage: 50 ``` * spec.scaleTargetRef 指定 autoscaling 的對象 * spec.targetCPUUtilizationPercentage 以 helloworld-deployment 為例,指定 CPU 200m 的資源,代表當該 helloworld-pod CPU 使用率達到 100 m 時,HorizontalPodAutoscaler 就會新產生一個 Pod。 **支援Colddown / Delay** 在建立 Kubernetes Cluster 時,加入 --horizontal-pod-autoscaler-downscale-delay 與 --horizontal-pod-autoscaler-upscale-delay 去限制 Autoscaling 的回應時間。 **minikube 中實現 Horizontal Pod Autoscaling** 1. 創建 helloworld-deployment ``` kubectl create -f ./helloworld-depolyment.yaml ``` 2. 創建一個 helloworld-service ``` kubectl expose deploy helloworld-deployment \ > --name helloworld-service \ > --type=ClusterIP ``` 3. 接著創建 helloworld-hpa ``` kubectl create -f ./helloworld-hpa.yaml ``` # 管理 Kubernetes 上的資源分配 - Resource Quotas **Resource Quotas** 每個 container 都可以有屬於它自己的 resource request 與 resource limit,設定檔中加入 spec.resources.requests.cpu 要求該 container 運行時需要多少 CPU 的資源。Kubernetes 也會透過設定的 resource request 去決定要將該 Pod 分配到哪個 Node 上。 *ex:helloworld-deployment.yaml* ``` apiVersion: apps/v1 kind: Deployment metadata: name: helloworld-deployment spec: replicas: 2 selector: matchLabels: app: helloworld-pod template: metadata: labels: app: helloworld-pod spec: containers: - name: my-pod image: tbj123121/docker-demo:latest ports: - containerPort: 3000 resources: requests: cpu: 200m limits: cpu: 400m ``` * spec.resources.limits.cpu container 最多能使用的 cpu 的資源為 400m 同等於 400milicpu(milicore) 除了可以針對 CPU 與 Memory 等計算資源限制之外,也可以限制 * configmaps * persistentvolumesclaims * pods * replicationscontrollers * resourcequotas * services * services.loadbalancer * secrets # Kubernetes 上管理不同的專案 - Namespaces **Namespaces** 將原本擁有實體資源的單一 Kubernetes Cluster ,劃分成幾個不同的抽象的 Cluster (virtual cluster) 透過 kubectl get 指令,看到當前有 default, kube-system 與 kube-public 這三個 namespaces ![](https://i.imgur.com/3O9xGFT.png) * default 沒特別指定 Namespace 都是存放在名稱為 default 的 namespaces 中 * kube-system 特別的資源都會存放在 kube-system 這個 namespace。可用kuebectl get all -n kube-system查看 * kube-public 存放在裡面的物件可被所有的使用者讀取。 **Namespaces特點** * 在同一個 Kubernetes Cluster 中,每個 Namespaces 的名稱都是要獨特 * 當一個 Namespaces 被刪除時,在該 Namespace 裡的所有物件也會被刪除 * 可以透過 Resource Quotas 限制一個 Namespaces 所可以存取的資源 **創建 Namespaces** 建立一個名稱為newspace的 namespace ``` kubectl create namespace <名稱:newspace> ``` **切換預設 Namespaces** 1. 查看目前在哪個 Namespace ``` kubectl config view | grep namespace: ``` 2. 將預設的指令切換為 newspace ``` kubectl config set-context \ $(kubectl config current-context) \ --namespace=newspace ``` **刪除單一 Namespaces** ``` kubectl delete namespaces newspace ``` **限制某一 Namespaces 的運算資源** *ex: hellospace.yaml* ``` apiVersion: v1 kind: Namespace metadata: name: hellospace --- apiVersion: v1 kind: ResourceQuota metadata: name: compute-quotas namespace: hellospace spec: hard: requests.cpu: "1" requests.memory: 1Gi limits.cpu: "1" requests.memory: 10Gi --- apiVersion: v1 kind: ResourceQuota metadata: name: object-quotas namespace: hellospace spec: hard: services: "2" services.loadbalancers: "1" secrets: "1" configmaps: "1" replicationcontrollers: "10" ``` * 運算資源(compute-quotas) CPU 最多只有 1 core ,以及 memory 的使用被限制在 10Gi 以下 * 物件資源(object-quotas) 限制 hellospace 最多只能有 2 個 services 物件,且只能有 1 個 loadbalancer, secret, 以及 configmap。 # 將一台機器上的服務搬移到另外一台 - Node Maintenance **Node Controller** * 知道目前 Kubernetes 中可用的 Node 清單 (Available Node List) * 定期監控每個 Node 的狀態,若是有 Node 呈現 unhealthy 的狀態時,就將該 Node 從清單中移除,而在該 Node 上的 Pod 就會被重新分配到其他 Node 上。 * 當 Node 的狀態狀態變回 healthy 時,Node Controller 則會把該 Node 加回可用的清單中。 **隔離 Node** 從 Kubernetes Cluster 移除node ``` kubectl drain {node_name} ``` kubectl drain 代表將該 Node 狀態變更為維護模式,該 Node 上面的 Pod,就會轉移到其他 Node 上。 **恢復 Node** ``` kubectl uncordon {node_name} ``` # Kubernetes 的調度 - Master Node Master Node 上四種不同元件 1. etcd 在終端機輸入上述指令後,kubectl 的請求會送往 Kubernetes Cluster 中的 Master Node。Master 中的 API Server 接收到該請求之前,會先經過一層認證(authorization),確認傳送方的身份沒問題後,再將這個請求傳遞給 Master Node 中的 API Server, API Server 每次收到指令後,會先把每個接收到的請求內容存放在 etcd 中。 etcd 用來存放 Cluster 中所有的 data。當 master nodes 因為某些原因而故障時,可以透過 etcd 幫還原 Cluster 的狀態。 2. Controller Manager Controller Manager 則是 Kubernetes 中所有 Controllers 的核心管理者。Controller Manager 會定期去訪問 API Server,若有接收到變更的指令 Controller Manager 則會去更改這些 Controllers 的狀態。 3. Scheduler API Server 保有每個 Node 目前的最新狀況,而 Scheduler 可以根據 API Server 上每個 Node 目前的狀況,透過特定的調度邏輯將 Pod 放置在最適合的 Node 上。 4. Check pod status ![](https://i.imgur.com/BWuS2kp.png)