Think Deep,Work Lean

k8s开发环境之 telepresence

Posted on By zack

什么是 telepresence?

k8s 的开发环境,比如:当开发一个新的 k8s 的 feature 或是修复一个 bug 时,修改完 golang 代码后,需要快速验证下这个代码的功能是否达到了预期,如果不借助工具,那么一般思维是:

change code -> rebuild image -> push image to docker registry - redeploy k8s deployment

但是,这太慢了。

此时 telepresence 就派上了用场。它的功能是可以在本地直接启动新的服务后,直接替换 k8s(本地或是远程)上的某个特定的deployment。

快速搭建k8s cluster 及环境及配置

k8s 的环境是必不可少的。

本地可以用 minikube

brew install minikube
brew install kubectl

然后就会在本地生成 ~/.kube/config文件,然后本地就可以用 kubectl 来操作环境。

建议备份下这个 config 文件,如果发生修改了,可以直接还原回来。如果没有备份,又被修改了,导致 kubectl 执行命令失败。可以通过关机 minikube,然后开机就可以恢复。 minikube stop && minikube start

远程的 k8s 可以用 kubesphere 的 all-in-one来快速搭建。

搭建完之后,本地要想通过 kubectl 命令来操作远程的 K8s cluster,如何操作呢?

利用本地 kubectl 来操作远程 k8s cluster

直接把远程 k8s cluster 的~/.kube/config 拷备到本地并替换。

但是还是无法通过命令访问,为什么呢?

原因是这样的:比如你在青云的云平台上创建了个主机,然后该主机有自己的内网 IP 192.168.0.x 。然后你还要申请并绑定个 eip 才能访问外网。但是这个 eip 并不是该主机的的 eth 上的 ip。也就是在这个主机执行 ip ad上你是看不到这个 eip 的。这个时候我部署的 k8s cluster它用的 ip 是 192.168.0.x这个内网 IP。同时,在生成的 ~/.kube/config 里面也是这个 IP。因此即使拷备到了本地,网络也是不通的,还是不通的,如果手动把这个 IP改成 eip,这个时候网络是通了,但是会提示 cert/key 证书不对。

要怎么办呢?

这个时候其实只需要搭建个代理就行了。

在这个远程主机上通过 ncat -l --proxy-type http localhost 81。执行完这个,就搭建了个 http 的正向代理。

然后在 iterm2 本地,执行

export http_proxy="http://eip:81"; export HTTP_PROXY="http://eip:81"; export https_proxy="http://eip:81"; export HTTPS_PROXY="http://eip:81"

这样就把本地的 http(s)的流量代理到了这个主机上,然后此时就可以像在远程环境上一样,正常使用 kubectl 这个命令了。

telepresence的安装与使用

如果要用 teletepresence debug 远程k8s,一般要按上述方法来配置从本地通用 kubectl 来操作远程 k8s 环境。(当然 teletepresence也支持加上代理的参数。)

安装

brew install teletepresence

也可以直找到 github 的 release,直接下载一个包,解压后是二进制文件etcd kube-apiserver kubebuilder kubectl。把这些二制拷备到系统环境变量下即可。

debug 本地 k8s cluster

首先把 ~/.kube/config 下的文件替换成本地的配置。

确认能够正常用 kubectl 命令了。

$ kubectl create deployment hello-world --image=datawire/hello-world
$ kubectl expose deployment hello-world --type=LoadBalancer --port=8000

然后看下 service

hugo@zack:/Users/hugo/.kube $ kubectl get svc hello-world
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-world   LoadBalancer   10.101.139.40   <pending>     8000:32293/TCP   63m

可以看到这个externalIP 是 pending 的,官网让你一直等下去,其实不对。要 edit servce把它改成 NodePort或是加上externalIPs

hugo@zack:/Users/hugo $ kubectl edit svc hello-world
service/hello-world edited
hugo@zack:/Users/hugo $ kubectl get svc -w
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-world   NodePort    10.101.139.40   <none>        8000:32293/TCP   141m

看下本地的 node 的 ip

hugo@zack:/Users/hugo $ minikube node list
minikube	192.168.64.2

从本地可以 curl 到内容

hugo@zack:/Users/hugo $ curl 192.168.64.2:32293
Hello, world!

在本地启动 http web server,并能成功 curl 成功

$ python3 -m http.server 8002 &
$ curl localhost:8002
$ kill %1

此时,把这个这个本地 web 用 telepresence替换到k8s 环境上的 hello-world 服务。

$ telepresence --swap-deployment hello-world --expose 9090   --run python3 -m http.server 9090

这个命令会把hello-world 这个deploy 给 scale 到 0,然后替换成 teletepresence的

hugo@zack:/Users/hugo $ kubectl get deploy -w
NAME                                           READY   UP-TO-DATE   AVAILABLE   AGE
hello-world                                    0/0     0            0           157m
hello-world-1ea1add6ad3f4c91a4a2a24435ad66cb   0/1     1            0           108s

发现 deploy 替换后一直不正常,describe pod 发现是拉镜像datawire/telepresence-k8s:0.105不成功。

关闭 minikube然后重新启动,看下能不能找到一些提示。

果然有些提示:

 To pull new external images, you may need to configure a proxy: https://minikube.sigs.k8s.io/docs/reference/networking/proxy/

提示要配置代理

 $ export http_proxy="http://ss:81"; export HTTP_PROXY="http://ss:81"; export https_proxy="http://ss:81"; export HTTPS_PROXY="http://ss:81";NO_PROXY=localhost,127.0.0.1
 $ minikube start

这个时候就正常了。

hugo@zack:/Users/hugo $ kubectl get deploy -w
NAME                                           READY   UP-TO-DATE   AVAILABLE   AGE
hello-world                                    0/0     0            0           3h4m
hello-world-8fc02edba0a34faf947bf77f659da50d   1/1     1            1           22s
hugo@zack:/tmp/telepresence-test $ telepresence --swap-deployment hello-world --expose 9090   --run python3 -m http.server 9090
T: Starting proxy with method 'vpn-tcp', which has the following
T: limitations: All processes are affected, only one telepresence can
T: run per machine, and you can't use other VPNs. You may need to add
T: cloud hosts and headless services with --also-proxy. For a full
T: list of method limitations see
T: https://telepresence.io/reference/methods.html
T: Volumes are rooted at $TELEPRESENCE_ROOT. See
T: https://telepresence.io/howto/volumes.html for details.
T: Starting network proxy to cluster by swapping out Deployment hello-
T: world with a proxy
T: Forwarding remote port 9090 to local port 9090.

T: Connected. Flushing DNS cache.
T: Setup complete. Launching your command.
Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ...

这里有个疑问,这个把远端 9090 端口映射到本地 9090是什么意思?

teletepresence --help看帮助,说是把 Deployment 的端口映射什么什么的,没明白,deployment 管端口什么事,那不 service 的事情吗?

对,其实就是 service的 port 端口(非 targetPort/NodePort)。

hugo@zack:/Users/hugo $ kubectl get svc
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-world   NodePort    10.101.139.40   <none>        8000:32293/TCP   3h31m

hugo@zack:/tmp/telepresence-test $  telepresence --run curl http://10.101.139.40:8000
...
Hello, world!
...

注意这里用的是 service_ip和 port(区分 targePort/nodePort)。

上述的操作是替换个新的 deployment。

但是想下我们如果要开发怎样的?

如果我们是要开发一个的功能,如一个新的 web 服务。我们会怎样做?

首先会先写上代码:

# helloworld.py
#
#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write(b"Hello, world!\n")
        return
httpd = HTTPServer(('', 8080), RequestHandler)
httpd.serve_forever()

然后把这个代码跑起来,它是个 web 服务,要放在 cluster 集群中,跟其他服务进行交互。

那么就要部署一个新的服务让它监听到本地的流量。

在 cluster 集群中用一个新的 Deployment,用 teletepresence让它接管本地的 8080 端口的数据:

localhost$ telepresence --new-deployment hello-world2 --expose 8080

如果是直接替换 cluster 上面本来就有的 Deployment 就用 swapdeployment。

然后在本地把这个 web 跑起来:

localhost$ python3 helloworld.py

然后就可以在这个 k8s 集群上看到这个 hello-world2 的服务了,然后可以新建个容器,在窗口里面直接访问这个 serviceIP:port 就可以访问到数据了。如下:

localhost$ kubectl --restart=Never run -i -t --image=alpine console /bin/sh
kubernetes# wget -O - -q http://hello-world:8080/
Hello, world!

但是在 minikube 中,创建pod 来验证有点问题,一直卡着不动。

原因为:minikube ssh 进入之后,发现无法联网,关闭 minikube,配置代理,启动 minikube,minikube ssh 进入后,docker pull alipine。

然后就可以进入 Pod了。 这个是进入 pod 的执行结果:

/ # wget -O - -q http://10.100.138.48:8000
Hello, world!

但是 minikube 有点特殊,只要进入 minikube ssh 进入后,可以直接获取能用 wget 获取到数据。 但是直接 Ping 这个 service ip 却 ping 不通

$ wget -O - -q http://10.100.138.48:8000
Hello, world!

$ ping 10.100.138.48
--- 10.100.138.48 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
$