---
# System prepended metadata

title: Kubernetes intro

---

---
title: 'Kubernetes intro'
disqus: hackmd
---

# Kubernetes

[TOC]

Kubernetes Deployment Introduction
===

Kubernetes gitbook: https://feisky.gitbooks.io/kubernetes/content/

> 須先從 `基础入门`、`核心原理`、`资源对象`、`部署配置`、`访问控制`、`服务治理`、`Istio`中取得必要資訊，再進行練習較佳。


部署工具服務簡述
===
1. Ingress Controller 外部http流量控制器，可實現初步JWT檢測、特定header自動導流，使用Nginx-Ingress
2. Cert-Manager 根據服務所需domain位置，自動簽署TLS/SSL(https)憑證中心
3. Rancher 提供節點流量與節點健康度檢測，並提供Grafana(監測服務)、fleet gitops (deployment 文件上版)、金絲雀上版、A/B Test、Jaeger鏈型追蹤gRPC log、Kiali 服務網格可檢視性儀表板。
4. Istio 一種服務網格，有彈性又簡單的方式將應用程式網路功能自動化。
5. EFK (Elastic / Fluentd / Kibana) 資料採集系統，並搭配Big Query可在外部系統根據時間、容器、節點名稱，篩選出相關log 或 sql log
6. Consul 微服務註冊中心

上版服務簡述
===
1. k8s不同於以往code based的方式進行上版，是以docker image 來當作上版的內容。
而k8s架構不同於以往容器化框架，是多容器、多服務與多節點間的整題服務架構，而上版內容已不是只有單純 “推code => 上版” 的單一化處理方式，目前除了單一上版外，已衍生出漸進式上版，A/B不同版本上版等，developer必須與infra(SRE)人員進行協作。
2. 開發者開發完程式碼後，推版至git，接著會進行新版本的自動化測試，測試無誤後，會將新的code打包成一個image，並推上ACR(azure container registry)
3. infra(SRE)人員會與開發者討論，當前是需要以什麼方式上版 ; 如這段code有可能會導致服務loading很重，因此會需要漸進式地上版，以觀察服務是否正常運行、機器loading是否過大。因此選擇漸進式部署(v1 90%,V2 10% -> V1 50%, V2 50% -> V1 10%, V2 90%)。

## k8s-deployment layout

```gherkin=
├── cert-manager                 //-> 部署 自動tls簽證服務
│    ├── cert-manager.yaml       //-> 部署 nginx-ingress 的 cert-manager設定 
│    ├── cert-manager-istio.yaml //-> 部署 istio 的 ingressgateway 的 cert-manager 設定
│    └── namespace.yaml          //-> 部署 cert-manager 的namespace
├── consul                       //-> 部署 微服務 註冊中心
│    └── ingress.yaml            //-> 部署 微服務 註冊中心 consul 的 ingress
├── efk                          //-> ( elastic / fluentd / kibana )
│    ├── deployment.yaml         //-> 部署 log 採集服務 的 deployment (elastic / kibana)
│    ├── fluentd-configmap.yaml  //-> 部署 log 採集服務 的 configmap (fluentd)
│    └── fluentd.yaml            //-> 部署 log 採集服務 的 deployment (fluentd)
├── istio
│    ├── certificate.yaml        //-> 部署 網格服務的tls簽證
│    └── gateway.yaml
├── pma
|    └── ingress.yaml            //-> 部署 pma 服務 的 ingress
|
|  -----------------------以下為 gitops部署文件-----------------------------
└── services                              //-> fleet 的 gitops 服務 會持續 watch 設定的分支 並且自動 apply 下列的部署文件
        ├── namespace.yaml                //-> 部署 ap服務相關 namespace
        ├── pod-rbac.yaml                 //-> 部署 ap服務相關 namespace 的使用權限
        ├── api
        │    └── member                   //-> 部署所有api服務的deployment / service
        │       ├── service.yaml          //-> 部署 api/member 的 service
        │       ├── deployment.yaml       //-> 部署 api/member 的 deployment
        |       ├── deployment-v2.yaml    //-> 部署 api/member 的 v2 deployment
        │       ├── destination-rule.yaml //-> 部署 api/member 的 v2 istio destination-rule
        │       └── virtual-service.yaml  //-> 部署 api/member 的 v2 istio virtual-service
        └── svc
             └── member                   //-> 部署所有svc服務的deployment / service
                ├── service.yaml          //-> 部署 svc/member 的 service
                ├── deployment.yaml       //-> 部署 svc/member 的 deployment
                ├── deployment-v2.yaml    //-> 部署 svc/member 的 v2 deployment
                ├── destination-rule.yaml //-> 部署 svc/member 的 v2 istio destination-rule
                └── virtual-service.yaml  //-> 部署 svc/member 的 v2 istio virtual-service

```
> 若其他服務的 ["ingress.yaml", "deployment.yaml"] 不在 k8s-deployment/ 中，則是以[Helm](https://helm.sh/zh/) 或網路文件作部署（如：MariaDB / pma / efk / redis ... ）

k8s 基礎部署步驟
---
1. 以 Azure 部署一個 k8s service
2. 待 k8s service 部署完成後，透過下列圖示步驟來連上 k8s service
    1. ![step1](https://static.stors.club/upload/2021524/527a80c744a4d0366a0f474caa4c7a08.png "RUNOOB")
    -----------------------------
    2. ![step2](https://static.stors.club/upload/2021524/28be10ce7a0a28d50a9997fe59d06fbc.png "RUNOOB")
3. 以上圖為例，取得兩個相對應的token後，在本地terminal輸入，來連上k8s。
```gherkin=
az account set --subscription bde24ffc-88bd-42ba-bf8d-64b56d13c59b
az aks get-credentials --resource-group rg-18platform-v2-dev --name sg-18platform-v2-dev
```
> 以後會泛用 `kubectl` 與 `kubectl -n`，推薦寫入 alias 以節省輸入時間。
4. 輸入以下指令測試是否連上k8s服務。
```gherkin=
kubectl get deployments --all-namespaces=true
```
5. 在 /k8s-deployment 資料夾中，首先需要先部署AP所需要的namespace與rbac權限，因此執行以下指令。
```gherkin=
kubectl apply -f namespace.yaml
#部署namespace
kubectl apply -f pod-rbac.yaml
#部署rbac權限
```
6. 安裝 [Helm](https://helm.sh/zh/)

k8s Nginx-Ingress (LoadBalancer)部署步驟
---

1. 部署 [Nginx-Ingress](https://docs.microsoft.com/zh-tw/azure/aks/ingress-basic)
```gherkin=
kubectl create namespace ingress-basic
#建立 ingress controller 專屬 namespace
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
#添加 ingress-nginx 的源
helm install nginx-ingress ingress-nginx/ingress-nginx \
    --namespace ingress-basic \ 
    --set controller.replicaCount=2 \
    --set controller.nodeSelector."beta\.kubernetes\.io/os"=linux \
    --set defaultBackend.nodeSelector."beta\.kubernetes\.io/os"=linux \
    --set controller.admissionWebhooks.patch.nodeSelector."beta\.kubernetes\.io/os"=linux
# 使用 helm 安裝 ingress-nginx
# helm install 自己取的套件名稱(nginx-ingress) 來源庫(ingress-nginx/ingress-nginx) \
#     -n namespace 
```
2. 輸入以下指令可得到 namespace = ingress-basic 的所有 service，並以拿到的 External IP:80 來進行連線測試。

```gherkin=
kubectl --namespace ingress-basic get services -o wide

NAME                                               TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                                                                   AGE
nginx-ingress-ingress-nginx-controller             LoadBalancer   10.0.76.32     52.229.186.90   80:31168/TCP,443:30939/TCP                                                14d    app.kubernetes.io/compo
```

k8s cert-manager (自動簽署TLS)部署步驟
---

1. 部署 cert-manager
```gherkin=
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.crds.yaml
#先部署 cert-manager 的 kubernetes crd 權限
kubectl create namespace cert-manager
#創建 cert-manager 命名空間
helm repo add jetstack https://charts.jetstack.io

helm repo update

helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.3.1 
```
2. 驗證部署
```gherkin=
kubectl get pods --namespace cert-manager

NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-7998c69865-nqn5z              1/1     Running   0          3d7h
cert-manager-cainjector-7b744d56fb-cwfbj   1/1     Running   0          3d7h
cert-manager-webhook-7d6d4c78bc-tnhwf      1/1     Running   0          3d7h
```
k8s Istio Service Mesh 部署步驟
---
> Rancher 內部的 Apps & Marketplace 也可以自動部署 istio，但有不少問題，因此還是需要透過官網來部署較佳
1. 本地安裝 [Istio](https://istio.io/latest/zh/docs/setup/getting-started/#download)
2. 安裝完後，請執行下列步驟安裝istio
```gherkin=
istioctl install
#在k8s上安裝istio
kubectl -n istio-system get svc
  
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                                      AGE
istio-ingressgateway   LoadBalancer   10.0.226.135   13.70.21.229   15021:32337/TCP,80:31615/TCP,443:31916/TCP   19h
istiod                 ClusterIP      10.0.119.103   <none>         15010/TCP,15012/TCP,443/TCP,15014/TCP        19h     
``` 
2. `istio-ingressgateway` 的對外 ip (LoadBalancer) 即是之後ap服務要對外的ip，因為 nginx-ingress 對於智能路由（設定特定的設定(特定header、特定uri prefix)來 routing 到特定版本號的 ap pod) 的支援尚不是很明朗，而 istio 這部分已經做得非常完整，之後只要設定 Deployment, Service, VirtualService, DestinationRule 就可以根據特定設定來跑 A/B Test, Rolling update 等
3. 因此，先前部署的 nginx-ingress 將會被拿來當作是工具的入口 (Rancher/Consul/phpmyadmin等)，而 ap 服務主要是走 istio 的 ingressgateway。
4. 透過以下指令部署 jaeger / kaili
```gherkin=
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.10/samples/addons/jaeger.yaml
#部署jaeger
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.10/samples/addons/kiali.yaml
#部署kaili

#等待部署完畢後
istioctl dashboard jaeger
#port-forwarding jaeger對外
istioctl dashboard kaili
#port-forwarding kaili對外
``` 
k8s Rancher (Dashboard, ClusterManager, GitOps, Grafana, Prometheus) 部署步驟
---

1. 透過以下指令部署 Rancher （Dashboard 、 ClusterManager）
```gherkin=
kubectl create namespace cattle-system

helm repo add rancher-latest https://releases.rancher.com/server-charts/latest

helm install rancher rancher-latest/rancher \
  --namespace cattle-system \
  --set hostname=YOURDOMAIN \
  --set ingress.tls.source=letsEncrypt \
  --set letsEncrypt.email=YOUREMAIL
  
kubectl -n cattle-system rollout status deploy/rancher
``` 
2. 接著，進入 上述填寫的https domain 即可進入 Rancher。
3. 因為k8s 只要第一步驟的 token 外流，即非常危險（可對 Cluster 做任何操作），因此，此步驟可以開放 kubectl 給特定人員，減少外流風險。
4. 進入 Rancher 後，點擊左上角切換至 `Apps & Marketplace` ，選擇部署 `Monitoring` ，稍待片刻後點擊左上角，切換至 `Monitoring` 即可點擊進入 `Alertmanager` (告警服務)、 `Grafana`(監控服務)。

k8s efk (log 採集)部署步驟
---

1. 首先部署 [ECK Operator](https://makeoptim.com/kubernetes/logging-high-availability#%E9%83%A8%E7%BD%B2-fluentd) ，安裝成功後，會自動創建一個 namespace = elastic-system 以及一個 operator 的 Pod。
```gherkin=
kubectl apply -f https://download.elastic.co/downloads/eck/1.3.0/all-in-one.yaml
```
2. 在 /k8s-deployment/efk 資料夾中，部署 Elastic / Kibana (deployment.yaml) 
以及 Fluentd 的 Configmap (fluentd-configmap.yaml) 
以及 ServiceAccount / ClusterRoleBinding / DaemonSet (fluentd.yaml) 。

```gherkin=
kubectl apply -f deployment.yaml

kubectl apply -f fluentd-configmap.yaml

kubectl apply -f fluentd.yaml
```
3. 輸入以下指令可得到 namespace = elastic-system 的所有 service。其中，`elastic-es-http` 為 elastic 提供api來搜集數據的 IP ，需要與 `fluentd.yaml` 中的 `FLUENT_ELASTICSEARCH_HOST` 有所對應。得到以下結果後，即可取得 `kibana-kb-http` 的 External IP:5601 來進行連線測試。

```gherkin=
kubectl --namespace elastic-system get services -o wide

NAME                     TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)          AGE   SELECTOR
elastic-es-default       ClusterIP      None          <none>         9200/TCP         10d   common.k8s.elastic.co/type=elasticsearch,elasticsearch.k8s.elastic.co/cluster-name=elastic,elasticsearch.k8s.elastic.co/statefulset-name=elastic-es-default
elastic-es-http          LoadBalancer   10.0.97.27    52.184.14.39   9200:31117/TCP   10d   common.k8s.elastic.co/type=elasticsearch,elasticsearch.k8s.elastic.co/cluster-name=elastic
elastic-es-transport     ClusterIP      None          <none>         9300/TCP         10d   common.k8s.elastic.co/type=elasticsearch,elasticsearch.k8s.elastic.co/cluster-name=elastic
elastic-webhook-server   ClusterIP      10.0.94.71    <none>         443/TCP          10d   control-plane=elastic-operator
kibana-kb-http           LoadBalancer   10.0.28.137   13.70.56.230   5601:31215/TCP   10d   common.k8s.elastic.co/type=kibana,kibana.k8s.elastic.co/name=kibana
```
3. 透過 `kibana-kb-http` EXTERNAL-IP:5601 就可以訪問 Kibana 囉。

```gherkin=
kubectl apply -f cert-manager.yaml
```
4. 另外，此處需要另外找尋設定外掛 Azure 儲存空間的設定（PV），不然 elastic 的所搜集的 log 很快就會佔滿 node 的空間，以及需要進去 elastic 設定資料的 lifecycle （資料儲存多久自我清除）。

k8s consul (服務註冊中心)部署步驟
---

1. 透過 helm 部署 [consul](https://github.com/hashicorp/consul-helm)
```gherkin=
helm repo add hashicorp https://helm.releases.hashicorp.com
helm install -n consul consul hashicorp/consul --set global.name=consul
```
2. 透過 /k8s-deployment/consul/ingress.yaml 部署 consul ui 的 Ingress，部署完成後稍待5至10秒，等待 cert-manager 簽名，即可透過 Nginx-ingress 所設定的 consul.XXXX.XXX 訪問 consul ui
```gherkin=
kubectl apply -f ingress.yaml

kubectl -n consul describe ingress
Name:             consul-ingress
Namespace:        consul
Address:          52.229.186.90
Default backend:  default-http-backend:80 (<none>)
TLS:
  tls-secret-consul terminates consul.18plm.com
Rules:
  Host              Path  Backends
  ----              ----  --------
  consul.18plm.com
                    /   consul-ui:80 (10.244.0.79:8500,10.244.1.74:8500,10.244.2.52:8500)
Annotations:
  kubernetes.io/ingress.class:                       nginx
  cert-manager.io/cluster-issuer:                    letsencrypt
  field.cattle.io/publicEndpoints:                   [{"addresses":["52.229.186.90"],"port":443,"protocol":"HTTPS","serviceName":"18xl-backend:consul-ui","ingressName":"18xl-backend:18xl-backend-consul-ingress","hostname":"consul.18plm.com","path":"/","allNodes":false}]
  ingress.kubernetes.io/ssl-redirect:                true
  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"annotations":{"cert-manager.io/cluster-issuer":"letsencrypt","ingress.kubernetes.io/ssl-redirect":"true","kubernetes.io/ingress.class":"nginx"},"name":"18xl-backend-consul-ingress","namespace":"18xl-backend"},"spec":{"rules":[{"host":"consul.18plm.com","http":{"paths":[{"backend":{"service":{"name":"consul-ui","port":{"number":80}}},"path":"/","pathType":"Prefix"}]}}],"tls":[{"hosts":["consul.18plm.com"],"secretName":"tls-secret-consul"}]}}

Events:  <none>

```
4.  但這樣所有人都能訪問該網址，因此要在 ingress.yaml 中 `annotations` 的部分添加 ingress 白名單

```gherkin=
metadata:
  name: consul-ingress
  namespace: consul
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
    ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/whitelist-source-range: '192.168.8.0/24'   # 访问白名单
```

k8s redis 部署步驟
---

1. 透過 helm 部署 [redis](https://github.com/bitnami/charts/tree/master/bitnami/redis)
```gherkin=
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install redis --set auth.password=18tgcity,master.service.type=LoadBalancer bitnami/redis
```
2. 待部署完畢，執行下列指令取得 redis-master svc 的對外 IP。
```gherkin=
kubectl get svc

NAME             TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
redis-headless   ClusterIP      None           <none>         6379/TCP         21s
redis-master     LoadBalancer   10.0.125.149   13.70.35.126   6379:31185/TCP   21s
redis-replicas   ClusterIP      10.0.12.190    <none>         6379/TCP         21s

```

k8s mariadb 部署步驟
---

1. 透過 helm 部署 [mariadb](https://github.com/bitnami/charts/tree/master/bitnami/mariadb)
```gherkin=
helm install mariadb bitnami/mariadb --set auth.rootPassword=ROOTPASSWORD,auth.database=18xl bitnami/mariadb
```

k8s phpmyadmin 部署步驟
---

1. 透過 helm 部署 [phpmyadmin](https://bitnami.com/stack/phpmyadmin/helm)
```gherkin=
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install phpmyadmin bitnami/phpmyadmin
```
2. 透過 helm update 來更新 mariadb host
```gherkin=
helm upgrade phpmyadmin bitnami/phpmyadmin --set db.host=mariadb.default.svc.cluster.local
```
3. 至 /k8s-deployment/pma/ 中 執行 

```gherkin=
kubectl apply -f ingress.yaml
```

4. 即可從 /k8s-deployment/pma/ingress.yaml 所設定的 domain 位置訪問 phpmyadmin

k8s GitOps 
---

1. k8s 不同於以往 code based 的方式進行上版，是以 docker image 來作為 ap 的啟動內容。
2. 而k8s架構不同於以往容器化框架，是多容器、多服務與多節點間的整體服務架構，而上版內容已不是只有單純 `推code => 上版` 的單一化處理方式，目前除了單一上版外，已衍生出漸進式上版，A/B不同版本上版等，developer必須與infra(SRE)人員進行協作。

```gherkin=
[RD線] 
git push -> github/gitlab... -> CI(各項目自動化測試) -> 編譯程式碼/打包成 docker image -> Azure ACR

[DevOps線]
與RD討論上版內容 -> 修改 k8s-deployment/services 內的 .yaml 配置 -> git push -> fleet gitOps 自動化部屬

```

3. 開發RD 開發完程式碼後，推版至git，接著會進行新版本的自動化測試，測試無誤後，會將新的code打包成一個image，並推上ACR(azure container registry)

4. infra(SRE)人員會與開發者討論，當前是需要以什麼方式上版，並修改 k8s-deployment/services 內的 .yaml 配置，修改完後一樣透過git上版的方式，把設置推上去 git，讓 fleet 去自動偵測並自動部署。 
>上版方式可能有
>1. 金絲雀上版(Canary Update)
> ex: 新版code有可能會導致服務loading很重，因此會需要漸進式地上版，以觀察服務是否正常運行、機器loading是否過大
>(v1 90%, V2 10%) -> (V1 50%, V2 50%) -> (V1 10%, V2 90%)
>2. 藍紅上版 (A/B Test)
>3. 灰度上版 (request 帶特定 header 才能被 routing 至相對應的 `測試版本` or `新版本`)

k8s GitOps 工具 Fleet
---

1. 部署 Rancher 就會自動幫你部署了 fleet ，但一樣有些小問題，因此推薦透過 helm 移除後重新安裝
```gherkin=
helm -n fleet-system uninstall fleet-crd
helm -n fleet-system uninstall fleet
#移除後，再透過指令進行安裝

helm -n fleet-system install --create-namespace --wait fleet-crd https://github.com/rancher/fleet/releases/download/v0.3.3/fleet-crd-0.3.3.tgz
helm -n fleet-system install --create-namespace --wait fleet https://github.com/rancher/fleet/releases/download/v0.3.3/fleet-0.3.3.tgz

kubectl -n fleet-system get po
#驗證一下 pod 是否 ready
NAME                                READY   STATUS    RESTARTS   AGE
fleet-agent-d59db746-9778t          1/1     Running   0          3h24m
fleet-controller-79554fcbf5-zdxwc   1/1     Running   0          3h24m
gitjob-568c57cfb9-dmp6p             1/1     Running   0          3h24m
```

2. 點擊 Rancher 左上角 切換至 Continuous Delivery (CD) 
3. 點擊 Continuous Delivery 右邊的下拉式選單至 fleet-local （確認左側 Clusters 數量必須為 1）
4. 點擊 Git Repos -> Create
5. 填寫 Name / Repository URL / Watch Branch Name 以及 Authentication （推薦 http）
> Username 填寫 github 帳號 
> Password 產生方式：點擊[Github Profile](https://github.com/settings/profile) -> Developer settings -> Personal access tokens
6. Path 填寫 k8s-deployment/services 
7. 點擊建立後，靜待 Git Repos 的 State 變化