提高开发效率:打通 K8s 与本地之间的网络

陪她去流浪 桃子 2020年09月12日 编辑 阅读次数:4540

背景

部门开发的一套系统包含了很多组件,以及其依赖的三方组件,比如: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
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sshd
  labels:
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: sshd
      app.kubernetes.io/instance: sshd
  template:
    metadata:
      labels:
        app.kubernetes.io/name: sshd
        app.kubernetes.io/instance: sshd
    spec:
      hostname: pipeline
      containers:
        - name: sshd
          image: panubo/sshd
          imagePullPolicy: Always
          env:
          - name: SSH_ENABLE_ROOT
            value: "true"
          command:
          - /bin/sh
          - -c
          args:
          - |
            set -euo pipefail
            /entry.sh
            echo 'ecdsa-sha2-nistp256 AAAAE3VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOsJRdlR0g2AatKk93rjkzbSnDTKXteJMQWtZLibJU7d/fUnsbgA71a8YltSz5qaBrQ4va5ShpQOVlaJi3YgSrs=' > /etc/ssh/keys/ssh_host_ecdsa_key.pub
            echo '-----BEGIN OPENSSH PRIVATE KEY-----
            b4BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
            1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTrCUXZUdINgGrSpPd645M20pw0yl7X
            iTEFrWS4myVO3f31J7G4AO9WvGJbUs+amga0OL2uUoaUDlZWiYt2IEq7AAAAsH2N+3d9jf
            t3AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOsJRdlR0g2AatKk
            93rjkzbSnDTKXteJMQWtZLibJU7d/fUnsbgA71a8YltSz5qaBrQ4va5ShpQOVlaJi3YgSr
            sAAAAhANr5qpUS1qt0Thpli78qrLD61kUki9V2+ss3KlTPmsW/AAAAEXJvb3RAZTY5NzVl
            MjgyODE0AQIDBAUG
            -----END OPENSSH PRIVATE KEY-----
            ' > /etc/ssh/keys/ssh_host_ecdsa_key
            echo 'ssh-ed25519 AAAAC4NzaC1lZDI1NTE5AAAAIOOVKCHGjqlQB+SSfzOwqBbFgX59EVXR8fCyA/mw9aZE tao.yang@Taos-MacBook-Pro.local' >> /root/.ssh/authorized_keys
            sed -i 's/GatewayPorts no/GatewayPorts yes/' /etc/ssh/sshd_config
            sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/' /etc/ssh/sshd_config
            apk add python2
            /usr/sbin/sshd -D -e -f /etc/ssh/sshd_config            
---
apiVersion: v1
kind: Service
metadata:
  name: sshd
  labels:
spec:
  type: NodePort
  ports:
    - name: sshd
      nodePort: 2222
      port: 22
      targetPort: 22
      protocol: TCP
  selector:
    app.kubernetes.io/name: sshd
    app.kubernetes.io/instance: sshd

上面出现的服务器的公钥、私钥和我自己的公钥都是临时通过 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
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

从本机直接访问 K8s 的内部网络就算打通了,接下来。

在 K8s 内部访问本机服务

前面已经创建了 sshd 服务,借助 ssh 的远程转发,要实现远程主机访问本机的服务简直不要太容易!

前面的 sshd 部署文件中有以下几行:

1
2
sed -i 's/GatewayPorts no/GatewayPorts yes/' /etc/ssh/sshd_config
sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/' /etc/ssh/sshd_config

这两行开启了 sshd 的端口转发服务。于是用以下一行代码即可实现在 K8s 内部访问本机服务:

1
$ ssh -R '*:5601:localhost:5601' sshd

结束语

我把把这个能力做成成品的任务留给了新入职的 3 个社招生来完成。

不过,他们找到了跟我想要实现的能力几乎完全一样的场景实现。来自于阿里巴巴的 kt-connect 项目

标签:Kubernetes