背景
在推動業務上容器過程中,存在業務方框架(如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.2
和tkestack/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-sdn
和loopback
兩個二進制文件;/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的釋放策略,有never
和immutable
兩種-
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配置:
requests
和limits
屬性都要添加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版本,新版本中已經解決了上述兩個問題。