文章目錄
回顧
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協議,可以拿到這些協議頭部信息, 那就可以實現基於協議層面的處理
協議支持
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-service
的 Service
對象,它會將請求代理到使用 TCP 端口 9376,並且具有標籤 app=MyApp
的 Pod
上
注意:
需要注意的是,
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-abc
和web
有效,但是123_abc
和-web
無效。
Service 四種類型
- ClusterIP: 默認. 集羣內部使用, 分配一個穩定的 IP 地址, 即 VIP, 只能在集羣內部訪問.
- NodePort: 對外暴露應用. 在每個節點上啓用一個端口來暴露服務, 可以在集羣外部訪問. 也會分配一個穩定內部集羣IP地址. 訪問地址:
<NodeIP>:<NodePort>
user -> 域名(公網IP) -> node ip:port -> iptables/ipvs -> pod
- LoadBalancer: 對外暴露應用, 適用公有云、與NodePort類似, 在每個節點上啓用一個端口來暴露服務. 除此之外, Kubernetes會請求底層雲平臺上的負載均衡器, 將每個Node([NodeIP]:[NodePort])作爲後端添加進去.
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-robinlc
: least connection (smallest number of open connections)dh
: destination hashingsh
: source hashingsed
: shortest expected delaynq
: 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 解析.
小結
-
採用 NodePort 對外暴露應用, 前面加一個 LB 實現統一訪問入口
-
優先使用 IPVS 代理模式
-
集羣內應用採用 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 暴露你的應用, 大致分爲兩步:
- 部署 ingress controller
- 創建 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發生變更時, 應用程序動態加載
-
觸發滾動更新, 即重啓服務