90. Service


serivce_0

▲ NodePort type 的 service


service_type

service 的種類: NodePort, Cluster IP, LoadBlance


nodeport_0

▲ NodePort 總共有三個 port number 分別是: node-port, port, target-port


  • NodePort range: 30000~32767 (option)。
  • 如果沒有給定 targetPort,預設 port == targetPort

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app: MyApp
  ports:
    - port: 80
      nodePort: 30007
      targetPort: 9376

NodePort service 使用 selector 來決定要將流量導向到哪些 pod 身上。
P.S 這邊先偷吃步給一個簡單的方法:
kubectl expose deployment nginx-rollout --port=80 --target-port=80


kubectl_expose

▲ 這樣就不用自己寫 YAML file 了,而且還可以自動幫我們設定 selector


kubectl_expose_1

▲ 實際用 curl 訪問 (kubectl get pod -o wide 可以看每個 pod 實際被放在哪個 worker-node 身上)


multi_pods_multi_nodes

▲ 當 service 被建立,pod 所在的 worker-node 就會被建立 service


91. Services - Cluster IP


ClusterIP 是一種 service type,主要用於 K8s Cluster 內互連


cluster_ip


94. Ingress Networking


首先講師要從 Ingressservice 的差別來講解 Ingress


ingress_0

▲ 範例場景 1

  • 能夠使用 IP address / domain 訪問,但必須帶 port number

ingress_1

▲ 那好呀~ 我自己弄個 Nginx reverse proxy 把 80nodePort 38080,這樣就可以讓 client 訪問時不用帶 port 好了 (灑花


ingress_2

▲ 如果使用 GCP 的話還可以把 service type 改成 LoadBlancer 然後去跟 Google 要一個 GCP Load Blancer (相當於 Google 幫你架設 reverse proxy)


ingress_3

▲ 這個時候公司業務增加啦! 多一個影音平台 (video),依照上面使用 GCP Load Blancer 會產生出一個 不同 IP 的 Load Blancer (價格當然 ++)

  • 出現一個問題: 如果以相同 FQDN 的情況下,DNS 一次只能指到一個 IP address 呀!

ingress_4

▲ 那我在 GCP LB 前面在加一個 LB (……) 一看就知道不是好架構


ingress_5

▲ Ingress 就是整合這些東西的地方! 一個在 K8s cluster 內的 L7 Load Blancer


ingress_6

▲ 既然是在 K8s cluster 內,那麼一樣需要一個 nodePort/LoadBlancer service

  • 阿如果沒上公有雲不就還續要 一個 Nginx reverse proxy 擺在 Ingress service 前面嗎?

ingress_7

▲ Ingress 分成 (1) Deployment (2) Configure,兩個部分。
前者到底是要部屬什麼呢? Ans: Ingress Controller
後者會被定義在 YAML 裡面,就跟其它 K8s object 一樣


注意!! Kubernetes 預設並沒有自帶 Ingress Controller !!

Ingress Controller 介紹


ingress_controller_0

▲ Ingress Controller 候選人有這些,不過目前只有 GCP HTTPs LB 與 Nginx 被 Kubernetes 官方維護,以下講師會使用 Nginx。
Ingress Controller 不僅只是有 Load Blancer 的功能,它還能使用 service account 與 K8s cluster 連接


Ingress Controller 部屬


ingress_controller_1

▲ 就像其它 App 一樣被部屬,只不過有一些特殊設定需要透過 args 傳入。
Nginx 剩餘的設定 (例如: SSL, error log path, keep-alive …) 則是額外用一個 ConfigMap 去儲存。
在講師的範例中 ConfigMap 目前是空的,不過還是建議建立一個~ 這樣在未來要修改設定值會比較方便!


ingress_controller_2

▲ 另外還需要提供兩個環境變數 (env): POD_NAME, POD_NAMESPACE,Nginx 需要這兩個變數去讀取 config。最後開啟 80, 443 port


ingress_controller_3

▲ 既然有 port 要被訪問,就一定要設定一個 nodePort service 囉!


ingress_controller_4

▲ 最後最後,上面提到的 service account 需要這些權限


ingress_controller_5

▲ 總結所需


Ingress Resources


ingress_resources_0

▲ 我們可以根據不同的 path 或者 FQDN 來制定規則


ingress_resources_1

▲ 我們先從最簡單的單一服務開始。如同前面提到的 Ingress resources 是被定義在 YAML 裡面,而且透過 kubectl 建立。


ingress_resources_2

▲ 基於不同 FQDN,Nginx 可以設定特定 path 該去找誰拿服務。整個列表可能會長這樣,下面每一個紅框對映不同服務類型的 pod (通常應該是 deployment 啦)


ingress_resources_3

基於相同 FQDN,不同 path 的 Ingress resource 就會像上圖右邊的 YAML 這樣。
我們從 .spec 看起,首先 .spec.rules[].http 就好像 Nginx http code block 一樣 (這樣形容應該會比較有感覺)
.spec.rules[].http.paths 鹿西派: 可以有很多條 path,所以要加 s (合理!)
.spec.rules[].http.paths[].path 開始 key-value 了~ 定義 path 並且帶入 backend


ingress_resources_4

可以看到兩個 path 分別給定不同 backend。有趣的是 Default backend 這個欄位,預設建立完 Ingress 後 K8s 會自動幫你產生這個 backend,當 path 沒有被 match 時就會走這個 backend,所以請記得再建立一個 service 給它!! (service name 就是 default-http-backend 醬~)


ingress_resources_5

基於不同 FQDN,不同 path 的 Ingress YAML file 就會長這樣,多了 .spec.rules[].host 定義 FQDN。


Kubernetes v1.20.x Update


ingress_resources_6

▲ 首先是 YAML syntax 上的差異


在來是可以以 imperative way 建立 ingress 了 (灑花

1
2
3
4
#kubectl create ingress <ingress-name> --rule="host/path=service:port"

kubectl create ingress ingress-test --rule="wear.my-online-store.com/wear*=wear-service:80"
kubectl create ingress ingress-test --rule="/pay*=pay-service:8282"

Rewrite-target

假設我們有兩個 service:

  • http://<watch-service>:<port>/
  • http://<wear-service>:<port>/

接著使用 Ingress 做銜接:

  • http://<ingress-service>:<ingress-port>/watch -> http://<watch-service>:<port>/
  • http://<ingress-service>:<ingress-port>/wear -> http://<wear-service>:<port>/

事與願違 因為當你實際訪問會變這樣:

  • http://<ingress-service>:<ingress-port>/watch -> http://<watch-service>:<port>/watch
  • http://<ingress-service>:<ingress-port>/wear -> http://<wear-service>:<port>/wear

不管是 app-watch 或者 app-waer 都沒有這個 path所以我們需要請 Ingress 做 URL rewrite

Ingress-Nginx rewrite docs


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: rewrite
  namespace: default
spec:
  rules:
  - host: rewrite.bar.com
    http:
      paths:
      - path: /something(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: http-svc
            port: 
              number: 80

透過設定 annotation nginx.ingress.kubernetes.io/rewrite-target: $2 達成。
依照官方 docs 後面應該是接 string 舉例: nginx.ingress.kubernetes.io/rewrite-target: /app
被換成 $2 是什麼呢 ? 原來是正規表示法 (Regexp) 的 Captured groups 啦~
也就是說當 client 端請求 http://rewrite.bar.com/something/robot.txt, Ingress 會把這個 request 改成
http://<service_name>:<service_port>/robot.txt (把 something 變不見,或者說 rewrite)


101. Network Policies


network_policy_0

▲ 我們的場景長這樣


  • ingress traffic: 進入
  • egress traffic: 流出
  • Kubernetes Default Netowrk Plicy: All Allow

network_policy_1

▲ 統整如上


network_policy_2

▲ Multi node 網路會是這樣


network_policy_3

▲ demo


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-policy
spec:
  podSelector:
    matchLabels:
      role: db
  ingress:
  - from:
    - podSelector:
        matchLabels:
          name: api-pod
    ports:
    - protocol: TCP
      port: 3306

2021.12.08 更新 NetworkPolicy 條件差異

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
....
spec:
  podSelector:
    matchLabels:
      id: frontend          # label of the pods this policy should be applied on
  policyTypes:
  - Egress                  # we only want to control egress
  egress:
  - to:                     # 1st egress rule
    - podSelector:            # allow egress only to pods with api label
        matchLabels:
          id: api
  - ports:                  # 2nd egress rule
    - port: 53                # allow DNS UDP
      protocol: UDP
    - port: 53                # allow DNS TCP
      protocol: TCP

上面的 -ports 是隸屬 .spec.egress[] 所以邏輯閘是 OR

1
2
allow outgoing traffic if
  (destination pod has label id:api) OR ((port is 53 UDP) OR (port is 53 TCP))

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# this example does not work in our case
...
  egress:
  - to:                     # 1st AND ONLY egress rule
    - podSelector:            # allow egress only to pods with api label
        matchLabels:
          id: api
    ports:                  # STILL THE SAME RULE but just an additional selector
    - port: 53                # allow DNS UDP
      protocol: UDP
    - port: 53                # allow DNS TCP
      protocol: TCP

上面的 ports 是隸屬 .spec.egress[].to[] 所以邏輯閘是 AND

1
2
allow outgoing traffic if
  (destination pod has label id:api) AND ((port is 53 UDP) OR (port is 53 TCP))

支援 Network Policy 的專案

  • 太多了 請看官網

declare-network-policy

  • 不支援的: Flannel

102. Developing network policies


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  • 符合 Label OR Namespace
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          project: myproject
      podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  • 符合 Label AND Namespace

另外也支援 IP,有需要的話去看 docs 吧~~


Multi from/to

當然可以允許多個來源/目的 囉~

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: multi-network-policy
spec:
  podSelector:
    matchLabels:
      app: nginx-rollout
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          run: r2b
    ports:
    - protocol: TCP
      port: 80
  - from:
    - podSelector:
        matchLabels:
          run: not-exited
    ports:
    - protocol: TCP
      port: 80