Galaxy固定容器IP網路方案

背景

在推動業務上容器過程中,存在業務方框架(如Java的dubbo)對ip依賴較重,但框架改造週期較長的問題。爲了解決這個問題,運維側從網絡層面固定容器IP的方式着手,引入了騰訊開源的Galaxy插件。這裏對此插件的安裝部署進行說明。

Galaxy架構概覽

在這裏插入圖片描述
Galaxy網絡方案主要包括兩個模塊:

  • galaxy:以daemonset形式存在每個k8s集羣的節點上,它通過判斷pod annotation信息,來設定pod網絡是用固定ip還是非固定ip
  • galaxy-ipam:根據pod的生命週期,完成pod ip的分配、釋放、已分配ip信息的記錄等功能

配置部署

預備環境

已有K8S集羣環境

組件名 版本
kube各組件 1.16.4
docker 19.03.5

Galaxy方案的落地,需要結合cni插件一起來完成。Galaxy支持多種cni插件,包括flannel、SRIOV、vlan等,這裏我們選擇vlan的方式。

因此,每個k8s集羣節點上需要安裝kubernete-cni

yum install -y kubernetes-cni

注意:
執行以上操作之後,由於kubernetes-cni依賴於kubelet,導致其自動安裝了最新版本的kubelet,將我們原本1.16.4的kubelet覆蓋,所以需要做以下回退操作:rpm -e kubelet --nodeps && yum install -y kubelet-1.16.4

鏡像準備

部署過程中,Galaxy網絡方案的兩個模塊都是以容器方式運行的,因此我們先定製鏡像,推送到我們的私有容器倉庫。我們在部署使用的時候,當時galaxy最新版是v1.0.2,這裏也以此版本來進行部署說明。

1. 下載鏡像

我們需要的是:tkestack/galaxy:v1.0.2tkestack/galaxy-ipam:v1.0.2兩個鏡像。

由於國內下載速度感人,大家可以想辦法用其他代理方式進行下載,這裏就不多說了。

2. 定製鏡像

下載的鏡像時區並不是北京時間的,可以採用兩種方式解決:

  • 方法一:每次使用鏡像時,在yaml加上掛載宿主機/etc/localtime的配置
  • 方法二:直接修改原鏡像中的/etc/localtime爲/usr/share/zoneinfo/Asia/Shanghai,一勞永逸

個人傾向方法二,這裏以galaxy:v1.0.2鏡像製作爲例進行說明(假設,私用倉庫地址爲:myhub.example.com/op-base)。

修改時區的Dockerfile如下:

FROM tkestack/galaxy:v1.0.2

RUN rm -f /etc/localtime \
    && ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

在Dockerfile文件所在目錄,執行build操作:

docker build -t myhub.example.com/op-base/galaxy:v1.0.2 .

將鏡像推送到我們的私有Harbor倉庫(推送需要賬號權限,需先docker login):

docker push myhub.example.com/op-base/galaxy:v1.0.2

配置部署galaxy模塊

在Galaxy項目https://github.com/tkestack/galaxy/tree/master/yaml路徑下有我們需要的yaml配置文件:
在這裏插入圖片描述
將其下載到有kubectl的節點,但是這些yaml有些地方需要修改。

1 運行galaxy的daemonset

配置部署galaxy模塊,使用的是galaxy.yaml,有兩處需要修改

(1)修改鏡像和啓動參數

......
      imagePullSecrets:
      - name: myhub
      containers:
      - image: myhub.example.com/op-base/galaxy:v1.0.2
        command: ["/bin/sh"]
      # qcloud galaxy should run with --route-eni
      #  args: ["-c", "cp -p /etc/galaxy/cni/00-galaxy.conf /etc/cni/net.d/; cp -p /opt/cni/galaxy/bin/galaxy-sdn /opt/cni/galaxy/bin/loopback /opt/cni/bin/; /usr/bin/galaxy --logtostderr=true --v=3 --route-eni"]
      # private-cloud should run without --route-eni
        args: ["-c", "cp -p /etc/galaxy/cni/00-galaxy.conf /etc/cni/net.d/; cp -p /opt/cni/galaxy/bin/galaxy-sdn /opt/cni/galaxy/bin/loopback /opt/cni/bin/; /usr/bin/galaxy --logtostderr=true --v=3"]
......

說明:

  • 修改image爲我們自定義的鏡像myhub.example.com/op-base/galaxy:v1.0.2
  • 指定從私有倉庫拉取鏡像所需的secrets:imagePullSecrets配置爲name: myhub (這裏根據自己實際情況而定)
  • 部署環境爲自建機房,不是騰訊雲,所以這裏啓動參數使用不帶--route-eni

(2)修改galaxy的網絡配置
我們的訴求是:在一個K8S集羣上,pod需要既能配置不使用固定ip,也可以配置使用固定ip。

即,在加上指定annotation參數時才使用固定ip方式配置pod網絡,默認情況下,不用固定ip。
因此,我們採用bridge+vlan的兩種網絡方式:
默認情況下,走bridge網絡,不會固定網絡ip;
當識別帶有

......
apiVersion: v1
kind: ConfigMap
metadata:
  name: galaxy-etc
  namespace: kube-system
data:
  # update network card name in "galaxy-k8s-vlan" and "galaxy-k8s-sriov" if necessary
  # update vf_num in "galaxy-k8s-sriov" according to demand
  galaxy.json: |
    {
      "NetworkConf":[
        {"name":"tke-route-eni","type":"tke-route-eni","eni":"eth1","routeTable":1},
        {"name":"galaxy-flannel","type":"galaxy-flannel", "delegate":{"type":"galaxy-veth"},"subnetFile":"/run/flannel/subnet.env"},
        {"name":"galaxy-k8s-vlan","type":"galaxy-k8s-vlan", "device":"eth0"},
        {"name":"galaxy-k8s-sriov","type": "galaxy-k8s-sriov", "device": "eth1", "vf_num": 10}
      ],
      "DefaultNetworks": ["my-bridge"],
      "ENIIPNetwork": "galaxy-k8s-vlan"
    }
......

說明:

  • DefaultNetworks指定爲使用bridge方式,這裏取名爲my-bridge (這個名字需要跟服務器節點上cni配置中的bridge名一致)
  • device指定服務器的網卡名爲eth0 (網卡名根據實際情況來修改)

除了上面兩處修改外,別忘了添加cni的bridge配置

# cat /etc/cni/net.d/10-my-bridge.conf 
{
    "cniVersion": "0.2.0",
    "name": "my-bridge",
    "type": "bridge",
    "bridge": "cbr0",
    "addIf": "eth0",
    "isGateway": true, 
    "ipMasq": false,
    "ipam": {
        "type": "host-local",
        "ranges": [
            [{
                "subnet": "10.66.24.0/26" 
            }]
        ],
        "routes": [
            { "dst": "0.0.0.0/0" }
        ]
    }
}

最後,執行如下命令就可以將galaxy的daemonset運行起來:

kubectl apply -f galaxy.yaml

運行成功後,在集羣節點上會發現:

  • /opt/cni/bin/目錄下多了galaxy-sdnloopback兩個二進制文件;
  • /etc/cni/net.d/目錄下生成了一個00-galaxy.conf配置文件,內容如下:
{
  "type": "galaxy-sdn",
  "capabilities": {"portMappings": true},
  "cniVersion": "0.2.0"
}

注意:之所以將bridge配置文件10-my-bridge.conf取名爲10-前綴,是爲了讓其排在00-前綴的00-galaxy.conf文件後面被加載,不然會出現奇怪的問題。

2 配置kubelet支持cni網絡插件

在kubelet的啓動腳本里加上--network-plugin=cni --cni-bin-dir=/opt/cni/bin/參數,然後重啓kubelet。

kubelet的完整啓動腳本內容類似如下:

# /usr/lib/systemd/system/kubelet.service
[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=https://kubernetes.io/docs/

[Service]
ExecStart=/usr/bin/kubelet \
  --eviction-hard=memory.available<1024Mi,nodefs.available<10%,nodefs.inodesFree<5% \
  --system-reserved=cpu=1,memory=1G \
  --kube-reserved=cpu=1,memory=1G \
  --cgroups-per-qos=true \
  --address={{your_node_ip}} \
  --hostname-override={{your_node_ip}} \
  --cgroup-driver=systemd \
  --pod-infra-container-image=myhub.example.com/kubernetes/pause-amd64:3.1 \
  --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.conf \
  --kubeconfig=/etc/kubernetes/kubelet.conf \
  --cert-dir=/etc/kubernetes/pki \
  --cluster-dns={{your_dns_ip}} \
  --cluster-domain={{your_domain_postfix}}. \
  --network-plugin=cni \
  --cni-bin-dir=/opt/cni/bin/ \
  --hairpin-mode=promiscuous-bridge \
  --fail-swap-on=false \
  --feature-gates=RotateKubeletServerCertificate=true,RotateKubeletClientCertificate=true \
  --rotate-certificates \
  --serialize-image-pulls=false \
  --max-pods=60 \
  --logtostderr=true \
  --v=2 
Restart=always
StartLimitInterval=0
RestartSec=5

[Install]
WantedBy=multi-user.target

配置部署galaxy-ipam模塊

1. 配置floatingip-config

假設:
k8s節點所屬ip網段爲:10.10.100.0/24
容器可用ip範圍爲:10.11.128.1~10.11.135.253
容器ip所屬網段爲:10.11.128.0/21
容器的網關ip爲:10.11.135.254
vlan的id爲:1200

注:這些網絡信息,實際通常需要網絡組同事幫忙分配確定

以ConfigMap的方式配置,這裏創建floatingip-config.yaml配置文件,內容如下:

kind: ConfigMap
apiVersion: v1
metadata:
 name: floatingip-config
 namespace: kube-system
data:
  floatingips: '[{"routableSubnet":"10.10.100.0/24","ips":["10.11.128.1~10.11.135.253"],"subnet":"10.11.128.0/21","gateway":"10.11.135.254","vlan":1200}]'

說明:

  • routableSubnet:指定kubelet節點所在網段
  • ips:指定爲容器ip分配的地址範圍
  • subnet:容器ip的網段
  • gateway:容器地址分配的網關
  • vlan:是用來指定容器ip的vlan id,當容器ip和節點ip不屬於同一個vlan時需要配置(id爲數值類型,寫的時候不要帶引號)

floatingip-config.yaml創建完後,執行以下命令生效:

kubectl apply -f floatingip-config.yaml

2 修改galaxy-ipam.yaml配置文件

(1)修改鏡像

......
      imagePullSecrets:
      - name: myhub
      containers:
      - image: myhub.example.com/op-base/galaxy-ipam:v1.0.2
......

說明:

  • 修改image爲自定義鏡像myhub.example.com/op-base/galaxy-ipam:v1.0.2
  • 指定從私有倉庫拉取鏡像所需的secrets:imagePullSecrets配置爲name: myhub (這裏根據自己實際情況而定)

(2)修改galaxy-ipam-etc配置

......
apiVersion: v1
kind: ConfigMap
metadata:
  name: galaxy-ipam-etc
  namespace: kube-system
data:
  # delete cloudProviderGrpcAddr if not ENI
  galaxy-ipam.json: |
    {
      "schedule_plugin": {
        "storageDriver": "k8s-crd",
        "cloudProviderGrpcAddr": "127.0.0.2:80"  # 刪除此行
      }
    }
......

說明:

  • 由於我們是自建IDC環境,刪除"cloudProviderGrpcAddr": "127.0.0.2:80"此行配置

修改完後,執行以下命令生效:

kubectl apply -f galaxy-ipam.yaml

3 配置kube-scheduler

(1)首先配置scheduler-policy.yaml:

# make sue --policy-configmap=scheduler-policy of kube-scheduler is set
# note that --policy-config-file and --use-legacy-policy-config is conflict with --policy-configmap
apiVersion: v1
kind: ConfigMap
metadata:
  name: scheduler-policy
  namespace: kube-system
data:
  # set "ignoredByScheduler" to true if not ENI
  policy.cfg: |
    {
      "kind": "Policy",
      "apiVersion": "v1",
      "extenders": [
        {
          "urlPrefix": "http://10.18.4.85:9040/v1",
          "httpTimeout": 70000000000,
          "filterVerb": "filter",
          "BindVerb": "bind",
          "weight": 1,
          "enableHttps": false,
          "managedResources": [
            {
              "name": "tke.cloud.tencent.com/eni-ip",
              "ignoredByScheduler": true
            }
          ]
        }
      ]
    }

說明:

  • urlPrefix這裏指定的地址是galaxy-ipam的svc的CLUSTER-IP(可以通過kubectl get svc -n kube-system查看)
  • ignoredByScheduler的說明是這樣的If you want to limit each node's max Float IPs, please set ignoredByScheduler to false, then the Float IP resource will be judge by scheduler's PodFitsResource algorithm.。這裏沒有太理解它的意思,由於我們是自建IDC環境,按照註釋提示,應該設爲true

(2)配置kube-scheduler的啓動腳本
在kube-scheduler的啓動腳本里添加參數:--policy-configmap=scheduler-policy,然後重啓kube-scheduler

注意:要確保kube-scheduler有訪問configmap的權限,否則會有類似如下的報錯:

在這裏插入圖片描述

system:kube-scheduler添加configmaps權限最直接的方法就是執行如下命令進入編輯:

kubectl edit clusterrole system:kube-scheduler -o yaml

然後添加:

- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - get
  - list
  - watch
  - update
  - create
  - patch

之後再查看已經有了configmaps權限:

kubectl describe clusterrole system:kube-scheduler

在這裏插入圖片描述
這時再執行systemctl restart kube-scheduler重啓即可。

如何讓pod配置使用固定ip

當galaxy和galaxy-ipam模塊按上述步驟配置完成後,配置一個示例驗證一下固定ip的功能是否實現

創建一個test-busybox.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-busybox
spec:
  replicas: 3
  selector:
    matchLabels:
      name: test-busybox
  template:
    metadata:
      labels:
        tag: lxcfs
        name: test-busybox
      annotations:
        k8s.v1.cni.cncf.io/networks: "galaxy-k8s-vlan"
        k8s.v1.cni.galaxy.io/release-policy: "never"
    spec:
      tolerations:
        - operator: "Exists"
      containers:
      - name: test-busybox
        image: busybox
        command:
        - sleep
        - "3600"
        resources:
          requests:
            cpu: "0.1"
            memory: "32Mi"
            tke.cloud.tencent.com/eni-ip: "1"
          limits:
            cpu: "0.1"
            memory: "32Mi"
            tke.cloud.tencent.com/eni-ip: "1"

說明:

  • annotations配置:

    • k8s.v1.cni.cncf.io/networks: "galaxy-k8s-vlan":指明使用的網絡類型爲galaxy-k8s-vlan
    • k8s.v1.cni.galaxy.io/release-policy: "never":指明ip的釋放策略,有neverimmutable兩種
      • never: Never Release IP even if the Deployment or Statefulset is deleted. Submitting a same name Deployment or Statefulset will reuse previous reserved IPs.

      • immutable: Release IP Only when deleting or scaling down Deployment or Statefulset. If POD float onto a new node in case of original Node became NotReady, it will get the previous IP.

    注: 在跟galaxy開發者交流時,他建議使用never參數,immutable可能在後面的迭代中被廢棄掉。

  • resources配置:

    • requestslimits屬性都要添加tke.cloud.tencent.com/eni-ip: "1"
      • 不配置的話,會報如下錯誤:fail to establish network map[]:neither ipInfo from cni args nor ipam type from netconf

通過刪除一個正在Running的pod,然後會發現再被創建出來的pod使用的ip和被刪除時的ip一樣,這便達到了我們固定ip的目的。

v1.0.2版本galaxy存在的問題

經過我們實際使用測試,發現v1.0.2版本存在兩個問題:

  • 多vlan支持: 當有多個vlan時,一個vlan中的ip分配完之後,不會自動分配下一個vlan的ip,而是pending
  • svc問題:啓用了固定ip的pod訪問集羣任意的svc地址都不通

對於第一個問題,我們使用一個vlan,然後在交換機層面用vxlan來隔離來避開了。
對於第二個問題,由於我們業務應用暫時沒有訪問svc的場景,所以也暫時不影響使用。

目前,galaxy已更新到了v1.0.3版本,新版本中已經解決了上述兩個問題。

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