tunnel

云端隧道 - 提供云端与边端的网络链接隧道。

Tunnel 包括 tunnel-cloudtunnel-edge 两个组件。作为云边通信的桥梁,负责在云和边之间维护一套稳定的网络连接。它打通云端和无公网暴露的边端节点去实现 Kubernetes 一致的节点运维和管理。

架构图

tunnel - 图1

实现方案

节点注册

  • 边缘节点上tunnel-edge主动连接云端tunnel-cloud service,service根据负载均衡策略将请求转到tunnel-cloud的pod。
  • tunnel-edgetunnel-cloud建立gRPC连接后,tunnel-cloud会把自身的podIp和tunnel-edge所在节点的nodeName的映射写入DNS。gRPC连接断开之后,tunnel-cloud会删除podIp和节点名映射。

云端请求的转发

  • apiserver或者其它云端的应用访问边缘节点上的kubelet或者其它应用时,tunnel-dns通过DNS劫持(将host中的节点名解析为tunnel-cloud的podIp)把请求转发到tunnel-cloud的pod。
  • tunnel-cloud根据节点名把请求信息转发到节点名对应的与tunnel-edge建立的gRPC连接。
  • tunnel-edge根据接收的请求信息请求边缘节点上的应用。

配置文件

tunnel组件包括tunnel-cloudtunnel-edge,运行在边缘节点tunnel-edge与运行在云端的tunnel-cloud建立gRPC长连接,用于云端转发到边缘节点的隧道。

tunnel-cloud

tunnel-cloud包含streamTCP和HTTPS三个模块。其中stream模块包括gRPC server和DNS组件,gRPC server用于接收tunnel-edge的gRPC长连接请求,DNS组件 用于把tunnel-cloud内存中的节点名和IP的映射更新到coredns hosts插件的configmap中。

tunnel-cloud配置文件

tunnel-cloud-conf.yaml

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: tunnel-cloud-conf
  5. namespace: edge-system
  6. data:
  7. tunnel_cloud.toml: |
  8. [mode]
  9. [mode.cloud]
  10. [mode.cloud.stream] # stream模块
  11. [mode.cloud.stream.server] # gRPC server组件
  12. grpcport = 9000 # gRPC server监听的端口
  13. logport = 8000 # log和健康检查的http server的监听端口,使用(curl -X PUT http://podip:logport/debug/flags/v -d "8")可以设置日志等级
  14. channelzaddr = "0.0.0.0:6000" # gRPC [channlez](https://grpc.io/blog/a-short-introduction-to-channelz/) server的监听地址,用于获取gRPC的调试信息
  15. key = "../../conf/certs/cloud.key" # gRPC server的server端私钥
  16. cert = "../../conf/certs/cloud.crt" # gRPC server的server端证书
  17. tokenfile = "../../conf/token" # token的列表文件(nodename:随机字符串),用于验证边缘节点tunnel-edge发送的token,如果根据节点名验证没有通过,会用default对应的token去验证
  18. [mode.cloud.stream.dns] # DNS组件
  19. configmap= "proxy-nodes" # coredns hosts插件的配置文件的configmap
  20. hosts = "/etc/superedge/proxy/nodes/hosts" # coredns hosts插件的配置文件的configmap在tunnel-cloud pod的挂载文件的路径
  21. service = "proxy-cloud-public" # tunnel-cloud的service name
  22. debug = true # DNS组件开关,debug=true DNS组件关闭,**tunnel-cloud** 内存中的节点名映射不会保存到coredns hosts插件的配置文件的configmap,默认值为false
  23. [mode.cloud.tcp] # TCP模块
  24. "0.0.0.0:6443" = "127.0.0.1:6443" # 参数的格式是"0.0.0.0:cloudPort": "EdgeServerIp:EdgeServerPort",cloudPort为tunnel-cloud TCP模块server监听端口,EdgeServerIp和EdgeServerPort为代理转发的边缘节点server的IP和端口
  25. [mode.cloud.https] # HTTPS模块
  26. cert ="../../conf/certs/kubelet.crt" # HTTPS模块server端证书
  27. key = "../../conf/certs/kubelet.key" # HTTPS模块server端私钥
  28. [mode.cloud.https.addr] # 参数的格式是"httpsServerPort":"EdgeHttpsServerIp:EdgeHttpsServerPort",httpsServerPort为HTTPS模块server端的监听端口,EdgeHttpsServerIp:EdgeHttpsServerPort为代理转发边缘节点HTTPS server的IP和port,HTTPS模块的server是跳过验证client端证书的,因此可以使用(curl -k https://podip:httpsServerPort)访问HTTPS模块监听的端口,addr参数的数据类型为map,可以支持监听多个端口
  29. "10250" = "101.206.162.213:10250"

tunnel-edge

tunnel-edge同样包含streamTCPHTTPS三个模块。其中stream模块包括gRPC client组件,用于向 tunnel-cloud发送gRPC长连接的请求。

tunnel-edge 配置文件

tunnel-edge-conf.yaml

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: tunnel-edge-conf
  5. namespace: edge-system
  6. data:
  7. tunnel_edge.toml: |
  8. [mode]
  9. [mode.edge]
  10. [mode.edge.stream] # stream模块
  11. [mode.edge.stream.client] # gRPC client组件
  12. token = "6ff2a1ea0f1611eb9896362096106d9d" # 访问tunnel-cloud的验证token
  13. cert = "../../conf/certs/ca.crt" # tunnel-cloud的gRPC server 的 server端证书的ca证书,用于验证server端证书
  14. dns = "localhost" # tunnel-cloud的gRPC server证书签的IP或域名
  15. servername = "localhost:9000" # tunnel-cloud的gRPC server的IP和端口
  16. logport = 7000 # log和健康检查的http server的监听端口,使用(curl -X PUT http://podip:logport/debug/flags/v -d "8")可以设置日志等级
  17. channelzaddr = "0.0.0.0:5000" # gRPC channlez server的监听地址,用于获取gRPC的调试信息
  18. [mode.edge.https] # HTTPS模块
  19. cert= "../../conf/certs/kubelet-client.crt" # tunnel-cloud 代理转发的HTTPS server的client端的证书
  20. key= "../../conf/certs/kubelet-client.key" # **tunnel-cloud** 代理转发的HTTPS server的client端的私钥

tunnel 转发模式

tunnel代理支持TCPHTTPS请求转发。

TCP转发

TCP模块会把TCP请求转发到第一个连接云端的边缘节点, 当tunnel-cloud只有一个tunnel-edge连接时, 请求会转发到tunnel-edge所在的节点

tunnel-cloud 配置文件

tunnel-cloud-conf.yaml

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: tunnel-cloud-conf
  5. namespace: edge-system
  6. data:
  7. tunnel_cloud.toml: |
  8. [mode]
  9. [mode.cloud]
  10. [mode.cloud.stream]
  11. [mode.cloud.stream.server]
  12. grpcport = 9000
  13. key = "/etc/superedge/tunnel/certs/tunnel-cloud-server.key"
  14. cert = "/etc/superedge/tunnel/certs/tunnel-cloud-server.crt"
  15. tokenfile = "/etc/superedge/tunnel/token/token"
  16. logport = 51000
  17. [mode.cloud.stream.dns]
  18. debug = true
  19. [mode.cloud.tcp]
  20. "0.0.0.0:6443" = "127.0.0.1:6443"
  21. [mode.cloud.https]

tunnel-cloud 的gRPC server监听在9000端口,等待tunnel-edge建立gRPC长连接。访问tunnel-cloud的6443的请求会被转发到边缘节点的访问地址127.0.0.1:6443的server

tunnel-cloud.yaml

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: tunnel-cloud-conf
  5. namespace: edge-system
  6. data:
  7. tunnel_cloud.toml: |
  8. {{tunnel_cloud.toml}}
  9. ---
  10. apiVersion: v1
  11. kind: ConfigMap
  12. metadata:
  13. name: tunnel-cloud-token
  14. namespace: edge-system
  15. data:
  16. token: |
  17. default:{{.TunnelCloudEdgeToken}}
  18. ---
  19. apiVersion: v1
  20. data:
  21. tunnel-cloud-server.crt: '{{tunnel-cloud-server.crt}}'
  22. tunnel-cloud-server.key: '{{tunnel-cloud-server.key}}'
  23. kind: Secret
  24. metadata:
  25. name: tunnel-cloud-cert
  26. namespace: edge-system
  27. type: Opaque
  28. ---
  29. apiVersion: v1
  30. kind: Service
  31. metadata:
  32. name: tunnel-cloud
  33. namespace: edge-system
  34. spec:
  35. ports:
  36. - name: proxycloud
  37. port: 9000
  38. protocol: TCP
  39. targetPort: 9000
  40. selector:
  41. app: tunnel-cloud
  42. type: NodePort
  43. ---
  44. apiVersion: apps/v1
  45. kind: Deployment
  46. metadata:
  47. labels:
  48. app: tunnel-cloud
  49. name: tunnel-cloud
  50. namespace: edge-system
  51. spec:
  52. selector:
  53. matchLabels:
  54. app: tunnel-cloud
  55. template:
  56. metadata:
  57. labels:
  58. app: tunnel-cloud
  59. spec:
  60. serviceAccount: tunnel-cloud
  61. serviceAccountName: tunnel-cloud
  62. containers:
  63. - name: tunnel-cloud
  64. image: superedge/tunnel:v0.2.0
  65. imagePullPolicy: IfNotPresent
  66. livenessProbe:
  67. httpGet:
  68. path: /cloud/healthz
  69. port: 51010
  70. initialDelaySeconds: 10
  71. periodSeconds: 60
  72. timeoutSeconds: 3
  73. successThreshold: 1
  74. failureThreshold: 1
  75. command:
  76. - /usr/local/bin/tunnel
  77. args:
  78. - --m=cloud
  79. - --c=/etc/superedge/tunnel/conf/mode.toml
  80. - --log-dir=/var/log/tunnel
  81. - --alsologtostderr
  82. volumeMounts:
  83. - name: token
  84. mountPath: /etc/superedge/tunnel/token
  85. - name: certs
  86. mountPath: /etc/superedge/tunnel/certs
  87. - name: conf
  88. mountPath: /etc/superedge/tunnel/conf
  89. ports:
  90. - containerPort: 9000
  91. name: tunnel
  92. protocol: TCP
  93. - containerPort: 6443
  94. name: apiserver
  95. protocol: TCP
  96. resources:
  97. limits:
  98. cpu: 50m
  99. memory: 100Mi
  100. requests:
  101. cpu: 10m
  102. memory: 20Mi
  103. volumes:
  104. - name: token
  105. configMap:
  106. name: tunnel-cloud-token
  107. - name: certs
  108. secret:
  109. secretName: tunnel-cloud-cert
  110. - name: conf
  111. configMap:
  112. name: tunnel-cloud-conf
  113. nodeSelector:
  114. node-role.kubernetes.io/master: ""
  115. tolerations:
  116. - key: "node-role.kubernetes.io/master"
  117. operator: "Exists"
  118. effect: "NoSchedule"

tunnel-cloud-token的configmap中的TunnelCloudEdgeToken为随机字符串,用于验证tunnel-edge;tunnel-cloud-cert的secret对应的gRPC server的server端证书和私钥。

tunnel-edge 配置文件

tunnel-edge-conf.yaml

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: tunnel-edge-conf
  5. namespace: edge-system
  6. data:
  7. tunnel_edge.toml: |
  8. [mode]
  9. [mode.edge]
  10. [mode.edge.stream]
  11. [mode.edge.stream.client]
  12. token = "{{.TunnelCloudEdgeToken}}"
  13. cert = "/etc/superedge/tunnel/certs/tunnel-ca.crt"
  14. dns = "{{ServerName}}"
  15. servername = "{{.MasterIP}}:9000"
  16. logport = 51000

tunnel-edge使用MasterIP:9000访问云端tunnel-cloud,使用TunnelCloudEdgeToken做为验证token,发向云端进行验证; token为tunnel-cloud的部署deployment的tunnel-cloud-token的configmap中的TunnelCloudEdgeToken;DNS为tunnel-cloud的gRPC server的证书签的域名或IP;MasterIP为云端tunnel-cloud 所在节点的IP,9000为 tunnel-cloud service的nodePort

tunnel-edge.yaml

  1. ---
  2. kind: ClusterRole
  3. apiVersion: rbac.authorization.k8s.io/v1
  4. metadata:
  5. name: tunnel-edge
  6. rules:
  7. - apiGroups: [""]
  8. resources: ["configmaps"]
  9. verbs: ["get"]
  10. ---
  11. apiVersion: rbac.authorization.k8s.io/v1
  12. kind: ClusterRoleBinding
  13. metadata:
  14. name: tunnel-edge
  15. roleRef:
  16. apiGroup: rbac.authorization.k8s.io
  17. kind: ClusterRole
  18. name: tunnel-edge
  19. subjects:
  20. - kind: ServiceAccount
  21. name: tunnel-edge
  22. namespace: edge-system
  23. ---
  24. apiVersion: v1
  25. kind: ServiceAccount
  26. metadata:
  27. name: tunnel-edge
  28. namespace: edge-system
  29. ---
  30. apiVersion: v1
  31. kind: ConfigMap
  32. metadata:
  33. name: tunnel-edge-conf
  34. namespace: edge-system
  35. data:
  36. mode.toml: |
  37. {{tunnel-edge-conf}}
  38. ---
  39. apiVersion: v1
  40. data:
  41. tunnel-ca.crt: '{{.tunnel-ca.crt}}'
  42. kind: Secret
  43. metadata:
  44. name: tunnel-edge-cert
  45. namespace: edge-system
  46. type: Opaque
  47. ---
  48. apiVersion: apps/v1
  49. kind: Deployment
  50. metadata:
  51. name: tunnel-edge
  52. namespace: edge-system
  53. spec:
  54. selector:
  55. matchLabels:
  56. app: tunnel-edge
  57. template:
  58. metadata:
  59. labels:
  60. app: tunnel-edge
  61. spec:
  62. hostNetwork: true
  63. containers:
  64. - name: tunnel-edge
  65. image: superedge/tunnel:v0.2.0
  66. imagePullPolicy: IfNotPresent
  67. livenessProbe:
  68. httpGet:
  69. path: /edge/healthz
  70. port: 51010
  71. initialDelaySeconds: 10
  72. periodSeconds: 180
  73. timeoutSeconds: 3
  74. successThreshold: 1
  75. failureThreshold: 3
  76. resources:
  77. limits:
  78. cpu: 20m
  79. memory: 20Mi
  80. requests:
  81. cpu: 10m
  82. memory: 10Mi
  83. command:
  84. - /usr/local/bin/tunnel
  85. env:
  86. - name: NODE_NAME
  87. valueFrom:
  88. fieldRef:
  89. apiVersion: v1
  90. fieldPath: spec.nodeName
  91. args:
  92. - --m=edge
  93. - --c=/etc/superedge/tunnel/conf/tunnel_edge.toml
  94. - --log-dir=/var/log/tunnel
  95. - --alsologtostderr
  96. volumeMounts:
  97. - name: certs
  98. mountPath: /etc/superedge/tunnel/certs
  99. - name: conf
  100. mountPath: /etc/superedge/tunnel/conf
  101. volumes:
  102. - secret:
  103. secretName: tunnel-edge-cert
  104. name: certs
  105. - configMap:
  106. name: tunnel-edge-conf
  107. name: conf

tunnel-edge-cert的secret对应的验证gRPC server证书的ca证书;tunnel-edge是以deployment的形式部署的,副本数为1,TCP转发现在只支持转发到单个节点。

HTTPS转发

通过tunnel将云端请求转发到边缘节点,需要使用边缘节点名做为HTTPS request的host的域名,域名解析可以复用tunnel-coredns 。使用HTTPS转发需要部署tunnel-cloudtunnel-edgetunnel-coredns三个模块。

tunnel-cloud 配置文件

tunnel-cloud-conf.yaml

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: tunnel-cloud-conf
  5. namespace: edge-system
  6. data:
  7. tunnel_cloud.toml: |
  8. [mode]
  9. [mode.cloud]
  10. [mode.cloud.stream]
  11. [mode.cloud.stream.server]
  12. grpcport = 9000
  13. logport = 51010
  14. key = "/etc/superedge/tunnel/certs/tunnel-cloud-server.key"
  15. cert = "/etc/superedge/tunnel/certs/tunnel-cloud-server.crt"
  16. tokenfile = "/etc/superedge/tunnel/token/token"
  17. [mode.cloud.stream.dns]
  18. configmap= "tunnel-nodes"
  19. hosts = "/etc/superedge/tunnel/nodes/hosts"
  20. service = "tunnel-cloud"
  21. [mode.cloud.https]
  22. cert = "/etc/superedge/tunnel/certs/apiserver-kubelet-server.crt"
  23. key = "/etc/superedge/tunnel/certs/apiserver-kubelet-server.key"
  24. [mode.cloud.https.addr]
  25. "10250" = "127.0.0.1:10250"

tunnel-cloud 的gRPC server监听在9000端口,等待tunnel-edge建立gRPC长连接。访问tunnel-cloud的10250的请求会被转发到边缘节点的访问地址127.0.0.1:10250的server。

tunel-cloud.yaml

tunnel-cloud.yaml

tunnel-edge 配置文件

tunnel-edge-conf.yaml

  1. apiVersion: v1
  2. kind: ConfigMap
  3. metadata:
  4. name: tunnel-edge-conf
  5. namespace: edge-system
  6. data:
  7. tunnel_edge.toml: |
  8. [mode]
  9. [mode.edge]
  10. [mode.edge.stream]
  11. [mode.edge.stream.client]
  12. token = "{{.TunnelCloudEdgeToken}}"
  13. cert = "/etc/superedge/tunnel/certs/cluster-ca.crt"
  14. dns = "tunnel.cloud.io"
  15. servername = "{{.MasterIP}}:9000"
  16. logport = 51000
  17. [mode.edge.https]
  18. cert= "/etc/superedge/tunnel/certs/apiserver-kubelet-client.crt"
  19. key= "/etc/superedge/tunnel/certs/apiserver-kubelet-client.key"

HTTPS模块的证书和私钥是tunnel-cloud代理转发的边缘节点的server的server端证书对应的client证书,例如tunnel-cloud转发apiserver到kubelet的请求,需要配置kubelet 10250端口server端证书对应的client证书和私钥。

tunnel-edge.yaml

tunnel-edge.yaml

本地调试

tunnel支持HTTPS(HTTPS模块)和TCP协议(TCP模块),协议模块的数据是通过gRPC长连接传输(stream模块),因此可以分模块进行本地调试。本地调试可以使用go的testing测试框架。配置文件的生成可以通过调用config_test的 测试方法Test_Config(其中constant变量config_path是生成的配置文件的路径相对于config_test go 文件的路径,main_path 是配置文件相对testing文件的 路径),例如stream模块的本地调试:config_path = “../../../conf”(生成的配置文件在项目的根目录下的conf文件夹),则 main_path=”../../../../conf”((stream_test相对 于conf的路径),同时生成配置文件支持配置ca.crt和ca.key(在configpath/certs/ca.crt和configpath/certs/ca.key存在时则使用指定的ca签发证书)。

stream模块调试

stream server的启动

  1. func Test_StreamServer(t *testing.T) {
  2. err := conf.InitConf(util.CLOUD, "../../../../conf/cloud_mode.toml")
  3. if err != nil {
  4. t.Errorf("failed to initialize stream server configuration file err = %v", err)
  5. return
  6. }
  7. model.InitModules(util.CLOUD)
  8. InitStream(util.CLOUD)
  9. model.LoadModules(util.CLOUD)
  10. context.GetContext().RegisterHandler(util.MODULE_DEBUG, util.STREAM, StreamDebugHandler)
  11. model.ShutDown()
  12. }
  1. 加载配置文件(conf.InitConf)->初始化模块(model.InitMoudule)->初始化stream模块(InitStream)->加载初始化的模块->注册自定义的handler(StreamDebugHandler)->关闭模块(model.ShutDown)

StreamDebugHandler是调试云边消息收发的自定义handler

stream client的启动

  1. func Test_StreamClient(t *testing.T) {
  2. os.Setenv(util.NODE_NAME_ENV, "node1")
  3. err := conf.InitConf(util.EDGE, "../../../../conf/edge_mode.toml")
  4. if err != nil {
  5. t.Errorf("failed to initialize stream client configuration file err = %v", err)
  6. return
  7. }
  8. model.InitModules(util.EDGE)
  9. InitStream(util.EDGE)
  10. model.LoadModules(util.EDGE)
  11. context.GetContext().RegisterHandler(util.MODULE_DEBUG, util.STREAM, StreamDebugHandler)
  12. go func() {
  13. running := true
  14. for running {
  15. node := context.GetContext().GetNode(os.Getenv(util.NODE_NAME_ENV))
  16. if node != nil {
  17. node.Send2Node(&proto.StreamMsg{
  18. Node: os.Getenv(util.NODE_NAME_ENV),
  19. Category: util.STREAM,
  20. Type: util.MODULE_DEBUG,
  21. Topic: uuid.NewV4().String(),
  22. Data: []byte{'c'},
  23. })
  24. }
  25. time.Sleep(10 * time.Second)
  26. }
  27. }()
  28. model.ShutDown()
  29. }
  1. 设置节点名环境变量->加载配置文件(conf.InitConf)->初始化模块(model.InitMoudule)->初始化stream模块(InitStream)->加载初始化的模块->注册自定义的handler(StreamDebugHandler)->关闭模块(model.ShutDown)

节点名是通过NODE_NAME的环境变量加载的

TCP模块调试

TCP server的调试

  1. func Test_TcpServer(t *testing.T) {
  2. err := conf.InitConf(util.CLOUD, "../../../../conf/cloud_mode.toml")
  3. if err != nil {
  4. t.Errorf("failed to initialize stream server configuration file err = %v", err)
  5. return
  6. }
  7. model.InitModules(util.CLOUD)
  8. InitTcp()
  9. stream.InitStream(util.CLOUD)
  10. model.LoadModules(util.CLOUD)
  11. model.ShutDown()
  12. }

需要同时初始化TCP模块(InitTcp)和stream模块(stream.InitStream)

TCP client的调试

  1. func Test_TcpClient(t *testing.T) {
  2. os.Setenv(util.NODE_NAME_ENV, "node1")
  3. err := conf.InitConf(util.EDGE, "../../../../conf/edge_mode.toml")
  4. if err != nil {
  5. t.Errorf("failed to initialize stream client configuration file err = %v", err)
  6. return
  7. }
  8. model.InitModules(util.EDGE)
  9. InitTcp()
  10. stream.InitStream(util.EDGE)
  11. model.LoadModules(util.EDGE)
  12. model.ShutDown()
  13. }

HTTPS模块调试

TCP模块调试类似,需要同时加载HTTPS模块stream模块

tunnel main()函数的调试

在tunnel的main的测试文件tunnel_test,需要使用init()设置参数,同时需要使用TestMain解析参数和调用测试方法

最后修改 June 16, 2021 : Update Tunnel description (6bb9132)