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版本,新版本中已经解决了上述两个问题。

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