需求背景
k8s的集羣容器需要訪問宿主機的某個服務(mysql或者其他類型的服務),或者其他外部遠程設備的服務,但是服務不在集羣當中
訪問外部服務
訪問遠程外部服務,如下任選一個實現
- 外部域名映射到內部
service - 外部
IP映射到內部Service
訪問當前Pod所在宿主機服務,如下任選一個實現
- 在
pod中掛載環境變量表示宿主機的IP,容器內部通過環境變量映射的IP訪問服務 - 如果是隻訪問當前宿主機服務,通過創建
linux虛擬網橋的訪問,指定一個固定的網橋IP,在容器內部訪問該IP來實現訪問宿主機的效果,也可以疊加外部IP映射到內部Service
外部域名映射到內部service
mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
externalName: mysql.example.com
type: ExternalName
應用
$ kubectl apply -f mysql-service.yaml
創建之後,Pod 就可以通過 mysql:3306 訪問外部的mysql服務(服務需要存在一個可訪問的域名)
外部 IP 映射到內部 Service
outer-ip.yaml
注意點
endpoints/service的metadata.name需要保持一致- 如果
service配置了端口名稱,在endpoints的端口配置也需要配置端口名稱,名稱保持一致
kind: Endpoints
apiVersion: v1
metadata:
name: outer-ip
subsets:
- addresses:
- ip: 192.168.0.1
ports:
- port: 3306
name: tcp
---
apiVersion: v1
kind: Service
metadata:
name: outer-ip
spec:
ports:
- protocol: TCP
port: 3306
name: tcp
targetPort: 3306
比如在IP: 192.168.0.1有一個mysql服務,但是服務不是在集羣內部的,想通過k8s service訪問,就可以創建一個endpoints, service的綁定關係,在集羣內部訪問outer-ip:3306訪問數據庫
應用
$ kubectl apply -f outer-ip.yaml
查看endpoints
$ kubectl describe endpoints outer-ip
Name: outer-ip
Namespace: default
Labels: <none>
Annotations: <none>
Subsets:
Addresses: 192.168.0.1
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
tcp 3306 TCP
Events: <none>
查看service
$ kubectl describe svc outer-ip
Name: outer-ip
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.1.83
IPs: 10.43.1.83
Port: tcp 3306/TCP
TargetPort: 3306/TCP
Endpoints: 192.168.0.1:3306
Session Affinity: None
Events: <none>
掛載Pod環境變量
對於容器來説,在不與 Kubernetes 過度耦合的情況下,擁有關於自身的信息有時是很有用的,**Downward API** 允許容器在不使用 Kubernetes 客户端或 API 服務器的情況下獲得自己或集羣的信息
創建test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-env
spec:
replicas: 1
selector:
matchLabels:
app: test-env
template:
metadata:
labels:
app: test-env
spec:
containers:
- name: test-env
image: alpine
command: [ "sleep" ]
args: [ "infinity" ]
env:
- name: NODE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
上面通過環境變量NODE把k8s的status.hostIP(Pod 所在節點的主 IP 地址)掛載進入容器
應用配置
$ kubectl apply -f test.yaml
查詢生成的pod
$ kubectl get pods|grep test-env
test-env-5f55d9d96f-kj69p 2/2 Running 0 3m43s
測試環境變量,宿主機IP是10.30.6.88
$ kubectl exec -it test-env-5f55d9d96f-kj69p -- sh
/ # env|grep NODE
NODE=10.30.6.88
更多可掛載參數查閲k8s官方文檔的Downward API文檔
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/downward-api/
創建linux網橋
該方案參考docker服務的docker0網橋實現
Docker 服務默認會創建一個 docker0 網橋(其上有一個 docker0 內部接口),它在內核層連通了其他的物理或虛擬網卡,這就將所有容器和本地主機都放到同一個物理網絡
Docker 默認指定了docker0 接口 的 IP 地址(默認172.17.0.1)和子網掩碼,讓主機和容器之間可以通過網橋相互通信
在Ubuntu上創建網橋採用netplan實現
(其他發行版本創建自行搜索,保證創建網橋之後可以持久化,網橋不需要連接其他網口,有網橋IP即可)
創建/etc/netplan/k8s-bridge.yaml
network:
version: 2
renderer: networkd
bridges:
k8s-bridge:
addresses: [172.172.0.1/28]
配置應用
$ netplan apply
查看網口狀態信息
$ ip address show k8s-bridge
65: k8s-bridge: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 56:97:87:ba:a8:97 brd ff:ff:ff:ff:ff:ff
inet 172.172.0.1/28 brd 172.172.0.15 scope global k8s-bridge
valid_lft forever preferred_lft forever
創建一個容器測試一下網絡訪問,容器內部ping 172.172.0.1,該IP值是固定的,即剛剛創建的網橋的IP,即使主機IP修改了,也不會影響容器的通信
$ kubectl run -it alpine-test --image=alpine -- sh
If you don't see a command prompt, try pressing enter.
/ # ping 172.172.0.1
PING 172.172.0.1 (172.172.0.1): 56 data bytes
64 bytes from 172.172.0.1: seq=0 ttl=64 time=0.209 ms
創建endponints, service的綁定
k8s-bridge-service.yaml
kind: Endpoints
apiVersion: v1
metadata:
name: k8s-bridge
subsets:
- addresses:
- ip: 172.172.0.1
ports:
- port: 3306
name: tcp
---
apiVersion: v1
kind: Service
metadata:
name: k8s-bridge
spec:
ports:
- protocol: TCP
port: 3306
name: tcp
targetPort: 3306
如果宿主機存在外部mysql服務,則容器內部就可以通過k8s-bridge:3306訪問該服務
如果存在一個宿主機上的HTTP服務,則可以用這種方式配置一個service,這樣就可以在ingress配置路由訪問該service
參考閲讀
k8s downwward API
Defining a Service for an External Database
Docker0網絡及原理探究