博客 / 詳情

返回

k8s容器訪問宿主機或者集羣外部服務

需求背景

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/servicemetadata.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        

上面通過環境變量NODEk8sstatus.hostIPPod 所在節點的主 IP 地址)掛載進入容器

應用配置

$ kubectl apply -f test.yaml

查詢生成的pod

$ kubectl get pods|grep test-env
test-env-5f55d9d96f-kj69p           2/2     Running            0                 3m43s

測試環境變量,宿主機IP10.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網絡及原理探究

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.