多环境下的Kubernetes网络打通方案研究

更多关注: https://mknight.cn/

前言

本文中的Kubernetes集群统称为集群。

现实的工作环境中,我们可能会在开发环境使用Kubernetes,也会在测试和开发环境使用Kubernetes。而一般来说,大部分人不会直接去连接或访问生产环境的集群,但是对于开发环境就不一样了。很多开发人员需要经常访问开发环境,而不是在本地起一堆依赖的服务。而且真的只有这么一个问题吗?

常见问题

整理了若干常见的问题:

  1. 开发人员本地服务访问开发环境
  2. 新集群和旧集群的网络互通,或者迁移
  3. 域名访问

因此,我们需要解决以上问题,才能更安心的使用Kubernetes。

网络互通

首先来解决最主要的问题,如何实现不同网络环境下的互通问题?

众所周知,集群暴露服务的方式有以下几种:

  1. NodePort 每个服务维护一个端口,服务越多端口越多
  2. LoadBalancer 在nodePort的基础上使用公有云的负载均衡器
  3. ClusterIP 每个service有一个虚拟IP
  4. Ingress 一个服务暴露多个service的服务,本质是一种路由转发机制

场景一、注册中心eureka的应用

首先ureka可以使用StatefulSet的方式,实现一个集群的部署。以下文件可以直接使用:

1. 办公网访问应用

开发人员本地访问的时候可以使用NodePort的方式访问,也可以使用Ingress的方式访问。

[图片上传失败...(image-9ee49c-1659432853998)]

如图所示,创建了两个Python的应用,并注册到eureka。代码如下:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@time:2022/04/27
@file:test_eureka.py
@author:medivh
@IDE:PyCharm 
"""
from flask import Flask
import py_eureka_client.eureka_client as eureka_client

app = Flask(__name__)


def setEureka():
    eureka_client.init(eureka_server="http://192.168.1.217:31331/eureka",
                       app_name="flask_server-2",
                       # 当前组件的主机名,可选参数,如果不填写会自动计算一个,如果服务和 eureka 服务器部署在同一台机器,请必须填写,否则会计算出 127.0.0.1
                       instance_host='10.1.3.141',
                       instance_port=5001,
                       # 调用其他服务时的高可用策略,可选,默认为随机
                       ha_strategy=eureka_client.HA_STRATEGY_RANDOM)


setEureka()


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/info')
def hello_info():
    return 'Hello Info!'


if __name__ == '__main__':
    app.run(debug=True, threaded=True, port=5001, host="0.0.0.0")

另外一个测试脚本:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@time:2022/04/27
@file:test_eureka-service.py
@author:medivh
@IDE:PyCharm 
"""
import py_eureka_client.eureka_client as eureka_client
import json

eureka_client.init(eureka_server="http://192.168.1.109:31331/eureka", app_name='do_service', instance_host="10.1.3.141",
                   instance_port=5011)
# get请求的调用
res = eureka_client.do_service("flask_server-2", "/info",
                               # 返回类型,默认为 `string`,可以传入 `json`,如果传入值是 `json`,那么该方法会返回一个 `dict` 对象
                               return_type="string")
print(res)
res = eureka_client.do_service("O2O-SERVICE", "/env-path/actuator/info",
                               # 返回类型,默认为 `string`,可以传入 `json`,如果传入值是 `json`,那么该方法会返回一个 `dict` 对象
                               return_type="string")
print(res)
## post请求的调用
# d = {'a': 1}
# res = eureka_client.do_service('helloindex', '/user', return_type='string', method='POST', data=json.dumps(d))
# print(res)
res = eureka_client.do_service("O2O-SERVICE", "/env-path/actuator/info",
                               # 返回类型,默认为 `string`,可以传入 `json`,如果传入值是 `json`,那么该方法会返回一个 `dict` 对象
                               return_type="string")
print(res)

调用测试结果:

python test_eureka-service.py
Hello Info!

Traceback (most recent call last):
  File "/Users/medivh/github/box/test_eureka-service.py", line 19, in <module>
    res = eureka_client.do_service("O2O-SERVICE", "/",
  File "/Users/medivh/github/box/venv/lib/python3.8/site-packages/py_eureka_client/eureka_client.py", line 1736, in do_service
    return cli.do_service(app_name=app_name, service=service, return_type=return_type,
  File "/Users/medivh/github/box/venv/lib/python3.8/site-packages/py_eureka_client/eureka_client.py", line 1467, in do_service
    return self.walk_nodes(app_name, service, prefer_ip, prefer_https, walk_using_urllib)
  File "/Users/medivh/github/box/venv/lib/python3.8/site-packages/py_eureka_client/eureka_client.py", line 1415, in walk_nodes
    raise http_client.URLError("Try all up instances in registry, but all fail")
urllib.error.URLError: <urlopen error Try all up instances in registry, but all fail>
[2022-04-27 17:32:39]-[eureka_client]-[line:1409] -WARNING: do service / in node [o2o-service-287077292-hj7q7:o2o-service:8080] error, use next node. Error: <urlopen error timed out>

这个时候说明注册中心是可以正常使用的,但是如果调用集群的服务就无法访问了,毕竟不在一个网段,也没有合适的路由。接下来就去解决这个问题。

2.办公网访问集群Pod/Service

在网关和路由器上添加路由,把属于集群的Pod和Service的子网IP全部转发给其中某个node,这样访问Pod IP和Service IP,网络包会到达某个node,而集群内的node中,CNI会与这些目的地址互通。

网段:

网段名称 网段范围
办公网段 10.1.3.0/24
Pod地址池 10.200.0.0/16
Svc地址池 10.96.0.0/12

如果不记得当时设置的网段是多少,可以用以下方式查询:

~ kubectl get cm kubeadm-config -n kube-system -o yaml | grep -i podsub
      podSubnet: 10.200.0.0/16
~ kubectl get cm kubeadm-config -n kube-system -o yaml | grep -i servicesub
      serviceSubnet: 10.96.0.0/12
steps 1

选择集群中的一个节点进行路由转发,可以使用一台配置不高的节点,打上污点不允许调度占用资源。本文中使用的是master节点。

  1. 设置污点 kubectl taint nodes k8s-master forward=k8s-node5:NoSchedule
  2. 开启路由转发 echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
  3. 查看是否生效 sysctl -p
# 开启转发
~ vim /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
~ sysctl -p
 
# 在k8s-master上设置snat
~ iptables -t nat -A POSTROUTING -s 10.200.0.0/16  -d 10.1.3.0/24   -j MASQUERADE
~ iptables -t nat -A POSTROUTING -s 10.96.0.0/12 -d 110.1.3.0/24   -j MASQUERADE
 
# (可选)查看设置的snat
~ iptables -t nat -L -n --line-numbers | grep -A 10 "Chain POSTROUTING"
Chain POSTROUTING (policy ACCEPT)
num  target     prot opt source               destination        
1    ......
2    ......
3    ......
4    MASQUERADE  all  --  10.1.3.0/24          10.200.0.0/16
5    MASQUERADE  all  --  10.1.3.0/24          10.96.0.0/12

# (可选)如配置失误可删除已设置的snat条目
~ iptables -t nat -D POSTROUTING 4
setps 2

在办公网的出口路由上设置静态路由,将集群的Pod和Service网段,路由到master节点。

# 在路由器上需要做的配置:
ip route add -net  10.200.0.0/16 gw 192.168.1.109
ip route add -net  10.96.0.0/12 gw 192.168.1.109

[图片上传失败...(image-7dee38-1659432853998)]

setps 3

这个时候在办公网主机上ping 集群中的Pod或者service的IP,理论上就是通的。

# Pod 
➜  ~ traceroute 10.200.96.148
traceroute to 10.200.96.148 (10.200.96.148), 64 hops max, 52 byte packets
 1  10.1.3.1 (10.1.3.1)  2.781 ms  2.422 ms  2.388 ms
 2  bogon (192.168.1.109)  2.723 ms  3.352 ms  3.647 ms
 3  bogon (10.200.96.128)  3.724 ms  3.485 ms  3.417 ms
 4  10.200.96.148 (10.200.96.148)  3.551 ms  3.440 ms  3.390 ms

 # service 

 ➜  ~ traceroute 10.96.230.195
traceroute to 10.96.230.195 (10.96.230.195), 64 hops max, 52 byte packets
 1  10.1.3.1 (10.1.3.1)  5.698 ms  2.482 ms  2.375 ms
 2  10.96.230.195 (10.96.230.195)  2.774 ms  2.807 ms  2.834 ms
问题总结

如果以上操作都执行了,但是还是不通,请检查以下项目:

  1. 转发是否开启
  2. iptables的规则是否正确
  3. iptables规则是否有MASQUERADE all -- 192.168.1.0/24 0.0.0.0/0 这么一条

至此,解决了开发人员无法在办公网访问集群环境的IP问题。

跨集群访问

如果我们有多个集群,这个时候如果实现网络打通呢,比如新旧集群迁移的时候?

以IP形式访问

其实我们依然可以采用之前的方案,添加静态路由。

网段:

网段名称 网段范围
办公网段 10.1.3.0/24
集群A Pod地址池 10.200.0.0/16
集群A Svc地址池 10.96.0.0/12
集群B Pod地址池 10.100.0.0/16
集群B Svc地址池 10.254.0.0/16

主要实现逻辑是每个集群选择一个节点,开启路由转发,实现办公网段可以访问集群中的IP。

~ iptables -t nat -A POSTROUTING -s 10.1.3.0/24 -d 10.254.0.0/16 -j MASQUERADE -w
~ iptables -t nat -A POSTROUTING -s 10.1.3.0/24 -d 10.100.0.0/16 -j MASQUERADE -w
~ iptables -t nat -L -n --line-numbers | grep -A 10 "Chain POSTROUTING"
Chain POSTROUTING (policy ACCEPT)
num  target     prot opt source               destination
1    MASQUERADE  all  --  10.100.72.0/24       0.0.0.0/0
2    KUBE-POSTROUTING  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes postrouting rules */
3    MASQUERADE  all  --  10.1.3.0/24          10.254.0.0/16
4    MASQUERADE  all  --  10.1.3.0/24          10.100.0.0/16

Chain DOCKER (2 references)
num  target     prot opt source               destination
1    RETURN     all  --  0.0.0.0/0            0.0.0.0/0

~ iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 0.0.0.0/0  -j MASQUERADE

验证:

# 办公网
➜  ~ traceroute 10.100.5.7
traceroute to 10.100.5.7 (10.100.5.7), 64 hops max, 52 byte packets
 1  bogon (10.1.3.1)  2.699 ms  2.201 ms  2.140 ms
 2  *.demo.yourdomain.com (192.168.1.243)  2.213 ms  7.957 ms  7.389 ms
 3  bogon (10.100.5.0)  2.902 ms  4.637 ms  2.616 ms
 4  bogon (10.100.5.7)  4.109 ms  3.944 ms  2.782 ms

 # A集群到B集群
~ tracepath 10.100.5.7
 1?: [LOCALHOST]                                         pmtu 1500
 1:  gateway                                               0.365ms
 1:  gateway                                               2.044ms
 2:  192.168.1.243                                         0.767ms
 3:  192.168.1.243                                         0.848ms pmtu 1450
 3:  10.100.5.0                                            1.536ms
 4:  10.100.5.7                                            1.382ms reached

办公网通过注册中心调用

[图片上传失败...(image-852dc6-1659432853998)]

以域名形式访问Service

实现了办公网和集群的网络互通后,就可以实现自由访问Pod和Service了。但是由于Pod IP会经常变化,Service IP也不是很容易记住,所以希望通过内网DNS的形式访问*.cluser.local时自动解析相应的IP。

steps 1. 获取coredns的IP

~ kubectl get svc -n kube-system -l k8s-app=kube-dns
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   34d

steps 2. 部署DNS服务dnsmasq

yum install dnsmasq

修改/etc/dnsmasq.conf:

resolv-file=/etc/resolv.dnsmasq.conf   #指定上游dns服务器
strict-order   #严格按照resolv-file文件中的顺序进行从上到下解析,直到成功为止
server=/cluster.local/10.96.0.10  #指定以cluster.local为后缀的域名,使用coredns的地址解析,这里可以不配直接把coredns配置在/etc/resolv.dnsmasq.conf 里面。
listen-address=192.168.1.109   #指定本地IP地址
addn-hosts=/etc/dnsmasq.hosts   #自定义dns记录文件
conf-dir=/etc/dnsmasq.d    #所有的解析记录都会存在此目录下

修改resolv.dnsmasq.conf

nameserver 61.139.2.69
nameserver 202.106.0.20
nameserver 192.168.1.109

开机并启动:

systemctl enable dnsmasq && systemctl start dnsmasq

steps 3. 验证

在其他机器上测试

~ traceroute eureka.default.svc.cluster.local
traceroute to eureka.default.svc.cluster.local (10.200.229.155), 30 hops max, 60 byte packets
 1  192.168.1.1 (192.168.1.1)  0.151 ms  0.145 ms  0.137 ms
 2  new.eureka.com (192.168.1.109)  0.622 ms  0.595 ms *
 3  bogon (10.200.229.128)  1.311 ms  1.972 ms  2.019 ms
 4  bogon (10.200.229.155)  2.075 ms  2.743 ms  2.741 ms

curl -I eureka.default.svc.cluster.local:8761
HTTP/1.1 200
X-Application-Context: DiscoveryServer:8761
Content-Type: text/html;charset=UTF-8
Content-Language: en-US
Content-Length: 7451
Date: Thu, 28 Apr 2022 05:47:08 GMT

后来在其他的服务器上测试可以正常访问,在Mac下能使用nslookup解析,但是无法访问,也无法路由跟踪,以后再解决。

后来发现如果是Mac的话,直接在网络设置-高级-DNS,添加dns地址并拖到上面即可实现访问。只是奇怪的是手动修改/etc/resolv.conf 并没有实现访问,这种界面的形式反而可以,总之,解决了问题。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容