容器化技術(No.4) -- Kubernetes 進階(二) Service, Ingress與配置管理

回顧

容器化技術(No.3) – Kubernetes 進階(一)

Kubernetes 對象

Service 對象

Service 概述

Service存在的意義

  • 防止Pod失聯(服務發現)pod 會漂移, pod ip 會變化
  • 定義一組Pod的訪問策略(負載均衡)

Pod 與 Service 的關係

  • 通過 label-selector 相關聯

  • 通過 Service 實現 Pod 的負載均衡( TCP/UDP 4層)

    - 四層: OSI 中的傳輸層, TCP/UDP, 四元組(源 ip+port, 目標 ip+port), 只負責IP數據包轉發
    - 七層: OSI 中的應用層, HTTP、FTP、SNMP協議,可以拿到這些協議頭部信息, 那就可以實現基於協議層面的處理
    

service

協議支持

  • TCP
    您可以將TCP用於任何類型的服務,這是默認的網絡協議。

  • UDP
    您可以將UDP用於大多數服務。 對於 type=LoadBalancer 服務,對 UDP 的支持取決於提供此功能的雲提供商。

  • HTTP
    如果您的雲提供商支持它,則可以在 LoadBalancer 模式下使用服務來設置外部 HTTP/HTTPS 反向代理,並將其轉發到該服務的 Endpoints。

注意:

您還可以使用 Ingress 代替 Service 來公開HTTP / HTTPS服務。

示例:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

上述配置創建一個名稱爲 my-serviceService 對象,它會將請求代理到使用 TCP 端口 9376,並且具有標籤 app=MyAppPod

注意:

需要注意的是, Service 能夠將一個接收 port 映射到任意的 targetPort. 默認情況下, targetPort 將被設置爲與 port 字段相同的值.

多端口 Service

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

注意:

與一般的 Kubernetes 名稱一樣,端口名稱只能包含 小寫字母數字字符 和 -。 端口名稱還必須以字母數字字符開頭和結尾。

例如,名稱 123-abcweb 有效,但是 123_abc-web 無效。

Service 四種類型

  • ClusterIP: 默認. 集羣內部使用, 分配一個穩定的 IP 地址, 即 VIP, 只能在集羣內部訪問.
    clusterip
  • NodePort: 對外暴露應用. 在每個節點上啓用一個端口來暴露服務, 可以在集羣外部訪問. 也會分配一個穩定內部集羣IP地址. 訪問地址: <NodeIP>:<NodePort>
user -> 域名(公網IP) -> node ip:port -> iptables/ipvs -> pod

nodeport

  • LoadBalancer: 對外暴露應用, 適用公有云、與NodePort類似, 在每個節點上啓用一個端口來暴露服務. 除此之外, Kubernetes會請求底層雲平臺上的負載均衡器, 將每個Node([NodeIP]:[NodePort])作爲後端添加進去.
    loadbalancer
  user -> 域名(公網IP) -> 公有云上的負載均衡器(自動配置,控制器去完成) -> node ip:port
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
      - ip: 146.148.47.155
  • ExternalName: 通過返回 CNAME 和它的值, 可以將服務映射到 externalName 字段的內容(例如, foo.bar.example.com). 沒有任何類型代理被創建.

    **注意: ** 需要 CoreDNS 1.7 或更高版本才能使用 ExternalName 類型.

Service 代理模式

iptables 代理模式下 Service 概覽圖:
在這裏插入圖片描述
IPVS 代理的 Services 概述圖:
在這裏插入圖片描述
Iptables:

  • 靈活, 功能強大
  • 規則遍歷匹配和更新, 呈線性時延

IPVS:

  • 工作在內核態, 有更好的性能
  • 調度算法豐富
    • rr: round-robin
    • lc: least connection (smallest number of open connections)
    • dh: destination hashing
    • sh: source hashing
    • sed: shortest expected delay
    • nq: never queue

啓用 IPVS

$ lsmod|grep ip_vs
$ modprobe ip_vs
$ modprobe -- ip_vs
$ modprobe -- ip_vs_rr
$ modprobe -- ip_vs_wrr
$ modprobe -- ip_vs_sh
$ modprobe -- nf_conntrack_ipv4

注意:

要在 IPVS 模式下運行 kube-proxy,必須在啓動 kube-proxy 之前使 IPVS Linux 在節點上可用。

當 kube-proxy 以 IPVS 代理模式啓動時,它將驗證 IPVS 內核模塊是否可用。 如果未檢測到 IPVS 內核模塊,則 kube-proxy 將退回到以 iptables 代理模式運行。

Service DNS名稱

DNS 服務監視 Kubernetes API, 爲每一個 Service 創建 DNS 記錄用於域名解析.

ClusterIP 記錄格式:

<service-name>.<namespace-name>.[svc.cluster.local].
中括號內容根據版本不同, 新版本可不寫

示例: my-svc.my-namespace.svc.cluster.local

CoreDNS 爲 Kubernetes 集羣內部提供域名解析服務. kubelet 運行 pod, pod 默認走 CoreDNS 解析.

小結

  1. 採用 NodePort 對外暴露應用, 前面加一個 LB 實現統一訪問入口

  2. 優先使用 IPVS 代理模式

  3. 集羣內應用採用 DNS 名稱訪問

Ingress

Ingress 是對集羣中服務的外部訪問進行管理的 API 對象, 典型的訪問方式是 HTTP. 它公開了從集羣外部到集羣內 services 的 HTTP 和 HTTPS 路由. 流量路由由 Ingress 資源上定義的規則控制.

    internet
        |
   [ Ingress ]
   --|-----|--
   [ Services ]

Ingress 爲彌補 NodePort 不足而生
NodePort存在的不足:

  • 一個端口只能一個服務使用, 端口需提前規劃
  • 只支持4層負載均衡

Pod 與 Ingress 的關係

  • 通過Service相關聯
  • 通過 Traefik 實現Pod的負載均衡
  • 支持TCP/UDP 4層和HTTP 7層
    在這裏插入圖片描述

ingress controller

爲了使 Ingress 資源正常工作, 集羣必須運行一個 ingress controller(負載均衡實現).

所以要想通過 ingress 暴露你的應用, 大致分爲兩步:

  1. 部署 ingress controller
  2. 創建 Ingress 規則

整體流程如下:
在這裏插入圖片描述
常見 ingress controller 對照

  • APISIX Ingress:APISIX Ingress 的優點前面也提到了,它具有非常強大的路由能力、靈活的插件拓展能力,在性能上表現也非常優秀。同時,它的缺點也非常明顯,儘管APISIX開源後有非常多的功能,但是缺少落地案例,沒有相關的文檔指引大家如何使用這些功能。
  • Kubernetes Ingress:即 Kubernetes 推薦默認使用的 Nginx Ingress。它的主要優點爲簡單、易接入。缺點是Nginx reload耗時長的問題根本無法解決。另外,雖然可用插件很多,但插件擴展能力非常弱。
  • Nginx Ingress:主要優點是在於它完全支持 TCP 和 UDP 協議,但是缺失了鑑權方式、流量調度等其他功能。
  • Kong:其本身就是一個 API 網關,它也算是開創了先河,將 API 網關引入到 Kubernetes 中當 Ingress。另外相對邊緣網關,Kong 在鑑權、限流、灰度部署等方面做得非常好。Kong Ingress 還有一個很大的優點:提供了一些 API、服務的定義,可以抽象成 Kubernetes 的 CRD,通過Kubernetes Ingress 配置便可完成同步狀態至 Kong 集羣。缺點就是部署特別困難,另外在高可用方面,與 APISIX 相比也是相形見絀。
  • Traefik :基於 Golang 的 Ingress,它本身是一個微服務網關,在 Ingress 的場景應用比較多。他的主要平臺基於 Golang,自身支持的協議也非常多,總體來說是沒有什麼缺點。如果大家熟悉 Golang 的話,也推薦一用。
  • HAproxy:是一個久負盛名的負載均衡器。它主要優點是具有非常強大的負載均衡能力,其他方面並不佔優勢。
  • Istio Ingress 和 Ambassador Ingress 都是基於非常流行的 Envoy。說實話,我認爲這兩個 Ingress 沒有什麼缺點,可能唯一的缺點是他們基於 Envoy 平臺,大家對這個平臺都不是很熟悉,上手門檻會比較高。 綜上所述,大家在瞭解了各個 Ingress 的優劣勢後,可以結合自身情況快速選擇適合自己的 Ingress。

Traefik

Traefik 是一款開源的邊緣路由, 意味着它是您平臺的入口,並且它攔截並路由每個傳入的請求:它知道確定哪些服務處理哪些請求的所有邏輯和規則 (基於 path, host, headers 等等)
在這裏插入圖片描述

Ingress 規則

接下來, 就可以創建ingress規則了.

在ingress裏有三個必要字段:

  • host: 訪問該應用的域名, 也就是域名解析
  • serverName: 應用的service名稱
  • serverPort: service端口

1、HTTP訪問

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-gateway-service
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /service
        backend:
          serviceName: svc-gateway-service
          servicePort: http

生產環境: example.com 域名是在你購買域名的運營商上進行解析, 記錄值爲K8S Node的公網IP(該Node必須運行了Ingress controller).

測試環境: 可以綁定hosts模擬域名解析 /etc/hosts, 對應 IP 是 K8S Node 的內網 IP. 例如:

192.168.0.11 example.com

2、HTTPS訪問

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
    - sslexample.com
    secretName: example-com
    rules:
    - host: sslexample.com
      http:
        paths:
        - path: /
          backend:
            serviceName: web
            servicePort: 80

裏面用到了secret名爲secret-tls, 用於保存https證書.

這裏使用cfssl工具自簽證書用於測試, 先下載cfssl工具:

curl -s -L -o /usr/local/bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -s -L -o /usr/local/bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
curl -s -L -o /usr/local/bin/cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x /usr/local/bin/cfssl*

執行如下腳本, 生成證書:

$ cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF

$ cat > ca-csr.json <<EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing"
        }
    ]
}
EOF

$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

$ cat > example.com-csr.json <<EOF
{
  "CN": "example.com",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing"
    }
  ]
}
EOF

$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes example.com-csr.json | cfssljson -bare example.com 

$ kubectl create secret tls example-com --cert=example.com.pem --key=example.com-key.pem

$ ls *pem
ca.pem ca-key.pem example.com.pem example.com-key.pem

將證書保存在secret裏:

$ kubectl create secret tls example-com --cert=example.com.pem --key=example.com-key.pem

這樣, ingress 就能通過 secret 名稱拿到要用的證書了.

然後綁定本地 hosts, 就可以 https 訪問了: https://example.com

3、根據URL路由到多個服務

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: url-ingress
spec:
  rules:
  - host: foobar.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: service1
          servicePort: 80
  - host: foobar.com
    http:
      paths:
      - path: /bar
        backend:
          serviceName: service2
          servicePort: 80

工作流程:

foobar.com -> 178.91.123.132 -> / foo    service1:80
                                / bar    service2:80

4、基於名稱的虛擬主機

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: bar.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80

工作流程:

foo.bar.com --|                 |-> service1:80
              | 178.91.123.132  |
bar.foo.com --|                 |-> service2:80

配置管理

secret

secret 加密數據並存放 Etcd 中, 讓 Pod 的容器以掛載 Volume 方式訪問.

應用場景: 憑據

  • https證書
  • secret存放 docker registry 認證信息
  • 存放文件內容或者字符串,例如用戶名密碼

Pod 使用 secret 兩種方式:

  • 變量注入

  • 掛載

Secret 指令

$ kubectl create secret --help
Create a secret using specified subcommand.

Available Commands:
  docker-registry 創建一個給 Docker registry 使用的 secret
  generic         從本地 file, directory 或者 literal value 創建一個 secret
  tls             創建一個 TLS secret

Usage:
  kubectl create secret [flags] [options]

Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all commands).

示例一:

創建一個給 Docker registry 使用的 secret

kubectl -n mineservice-ns create secret docker-registry registry-key --docker-server=192.168.0.14:8082 --docker-username=admin --docker-password=admin@123

配置資源編排文件

...
template:
metadata:
 labels:
   app: my-service
spec:
 imagePullSecrets:
    - name: registry-key
    containers:
    - name: my-pod
      image: 192.168.0.14:8082/my-service:latest
...

示例二:

創建一個 secret 用於保存應用程序用到的用戶名和密碼

$ echo -n 'admin' | base64
YWRtaW4=
$ echo -n 'admin@123' | base64
YWRtaW5AMTIz

創建secret:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: YWRtaW5AMTIz

變量注入方式在 Pod 中使用 secret:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password

進入到Pod中測試是否傳入變量:

$ kubectl exec -it mypod bash
$ echo $SECRET_USERNAME
admin
$ echo $SECRET_PASSWORD
admin@123

數據掛載方式在Pod中使用 secret:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

進入到Pod中測試是否寫入文件:

$ kubectl exec -it mypod bash
$ cat /etc/foo/username
admin
$ cat /etc/foo/password
admin@123 

configmap

與 Secret 類似, 區別在於 ConfigMap 保存的是不需要加密配置信息.

應用場景: 應用配置

例如: 創建一個 configmap 用於保存應用程序用到的字段值

apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfig
  namespace: default
data:
  special.level: info
  special.type: hello

變量注入方式在Pod中使用configmap:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: busybox
      image: busybox
      command: [ "/bin/sh", "-c", "echo $(LEVEL) $(TYPE)" ]
      env:
        - name: LEVEL
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.level
        - name: TYPE
          valueFrom:
            configMapKeyRef:
              name: myconfig
              key: special.type
  restartPolicy: Never

查看Pod日誌就可以看到容器裏打印的鍵值了:

$ kubectl logs mypod 
info hello

舉一個常見的用法, 例如將應用程序的配置文件保存到configmap中, 這裏以redis爲例:

apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
data:
  redis.properties: |
    redis.host=127.0.0.1
    redis.port=6379
    redis.password=admin@123
---
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: busybox
      image: busybox
      command: [ "/bin/sh","-c","cat /etc/config/redis.properties" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: redis-config
  restartPolicy: Never

查看Pod日誌就可以看到容器裏打印的文件了:

$ kubectl logs mypod 
redis.host=127.0.0.1
redis.port=6379
redis.password=admin@123

配置動態更新

ConfigMap更新時, 業務也隨之更新的方案:

  • 當ConfigMap發生變更時, 應用程序動態加載

  • 觸發滾動更新, 即重啓服務

總結

Kubernetes 進階(二)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章