背景
部门开发的一套系统包含了很多组件,以及其依赖的三方组件,比如:Kafka、MySQL。 开发、测试人员都有各自的场景,所以不能共用同一套环境。
于是,我们基于 K8s 做了一套 “Pipeline” 系统。所谓的 Pipeline,其实就是每个人一个独立 K8s 名字空间, 这个空间内包含一整套系统所有需要的组件,各自互不影响,可一键启动、一键删除。
问题
这套系统虽然看来非常优秀,但是在流程上也有一些严重的问题: 开发每次改完代码想要测试验证,需要:编译二进制、打包镜像、上传镜像、更新 K8s 的部署。 这简直是一项极其复杂的流程。整个过程走一遍,估计十分钟没了。 当然,我是相对于本地开发环境在 IDE 里面可以单步调试而言。
目的
在项目组里面,我估计我是最反感这一套开发流程的,我不喜欢 Mock 写,我要在 IDE 里面调试,我要观察程序运行流程! 所以我花了不少时间,想把本机的网络和 K8s 的内部网络打通。
网络打通,这包括:
- 能在本地通过域名访问 K8s 内的服务
- 能在 K8s 内通过域名访问本地的服务
以上就可以了。
方案
这里给出一个实测可行的方案,但是作者目前还没有把它做成成品。
本地能够解析 K8s 内的服务域名
K8s 内运行的应用都有对应的服务,所以通过以下命令可以找到所有的服务(域名)及其 IP 地址和端口(有省略):
$ kubectl -n xxx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
adminer ClusterIP 10.107.83.151 <none> 80/TCP 8d
es-3-1 ClusterIP 10.107.126.242 <none> 9200/TCP,9300/TCP 8d
etcd ClusterIP 10.111.191.166 <none> 2379/TCP,2380/TCP 8d
kafka-1 ClusterIP 10.110.59.149 <none> 9092/TCP 8d
mysql ClusterIP 10.100.193.187 <none> 3306/TCP 8d
nginx NodePort 10.109.151.73 <none> 80:31349/TCP 8d
NAME
就是 K8s 内的域名,CLUSTER-IP
就是其 IP。
可以简单地将它们提取出来保存到 /etc/hosts
文件后即可使用。
$ cat /etc/hosts | grep nginx
10.109.151.73 nginx
$ ping nginx
PING nginx (10.109.151.73): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
^C
--- nginx ping statistics ---
3 packets transmitted, 0 packets received, 100.0% packet loss
域名已能手动方式解析正确。
也有其它方案可直接利用 K8s 的相关 API 来获取全量/监控变更,比如:sercand/kuberesolver。 这样更简洁,维护性高。
本地属于 K8s 内的流量转发到 K8s 内网
这一步其实是最难的。我一开始想着手动改路由表,后来我搜索到了一款非常好用的工具:
sshuttle: where transparent proxy meets VPN meets ssh
Transparent proxy server that works as a poor man's VPN. Forwards over ssh. Doesn't require admin. Works with Linux and MacOS. Supports DNS tunneling.
这是一款非常容易使用的通过 SSH 建立 VPN 的工具。
VPN 的原理大家知道:把本机网络的流量通过 VPN 网络发送出去。 简单说就是:你不在办公室,却能连上办公室的网络并使用。完全就像处于办公室内一样。 所以我赶紧在我的 K8s 内部署了一个 sshd 服务,尝试把它搞起来。
在 K8s 创建 sshd 服务
以下是 sshd 的部署文件(sshd.yaml):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|
上面出现的服务器的公钥、私钥和我自己的公钥都是临时通过 ssh-keygen 生成的。
然后,部署它:
$ kubectl apply -f sshd.yaml
$ kubectl get pods | grep sshd
NAME READY STATUS RESTARTS AGE
sshd-689d4b987-m6gbr 1/1 Running 0 1m
$ kubectl get svc | grep sshd
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
sshd NodePort 10.43.179.7 <none> 22:2222/TCP 1m
由于是 NodePort 服务,所以我可以在集群外部也能访问它。
然后我在 .ssh/config
里面增加了一条 ssh 快捷登录记录:
Host sshd
# 这是 K8s 所在机器某一节点
HostName 192.168.1.6
# sshd 服务的 NodePort
Port 2222
User root
# root 的私钥
IdentityFile ~/.ssh/sshd_id_ed25519
然后通过以下方式 ssh 到 K8s 内部的 sshd 服务:
$ ssh sshd
Welcome to Alpine!
The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <http://wiki.alpinelinux.org/>.
You can setup the system with the command: setup-alpine
You may change this message by editing /etc/motd.
ssh:~#
用 sshuttle 正向打通网络
查看 sshuttle 的帮助文档并执行以下命令即可:
$ sshuttle -r sshd 10.109.0.0/16
client: Connected.
后面是需要打通的 K8s 的网段,可以从服务列表里面取得。
此时访问 K8s 内部的 nginx,应该就可以直达了:
$ curl 10.109.151.73
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
从本机直接访问 K8s 的内部网络就算打通了,接下来。
在 K8s 内部访问本机服务
前面已经创建了 sshd 服务,借助 ssh 的远程转发,要实现远程主机访问本机的服务简直不要太容易!
前面的 sshd 部署文件中有以下几行:
1 2 |
|
这两行开启了 sshd 的端口转发服务。于是用以下一行代码即可实现在 K8s 内部访问本机服务:
1
|
|
结束语
我把把这个能力做成成品的任务留给了新入职的 3 个社招生来完成。
不过,他们找到了跟我想要实现的能力几乎完全一样的场景实现。来自于阿里巴巴的 kt-connect 项目。