导读

云原生的概念越来越深入人心,作为典型技术之一的微服务架构,过去我们还说它是一把双刃剑,带来一定好处的同时,对服务团队的技术要求也提高了很多。但是,随着开源技术的不断发展,越来越多优秀的技术和工具涌现出来,让云原生落地不再困难。为此,博云研究院后续将不定期的总结整理云原生相关的开发、测试、运维等方面的最佳实践,借助博云和开源社区共同的力量,帮助客户更加标准化、简单化开启云原生之路。

背景

基于Spring Boot框架开发微服务,并部署在Kubernetes集群中,是现在很多企业的实施微服务的技术选型。微服务架构下,开发人员主要关注于所在服务团队维护的微服务,通常情况下这些微服务依赖于其它团队开发的微服务,同时也被另外一组微服务所依赖。

在微服务开发过程中,一种传统的做法是通过单元测试对代码进行验证,尽可能早的发现问题,待相关服务开发基本完成以后,统一部署到Kubernetes集群中进行联调。这种方式下,一方面是开发人员花大量的时间设计测试用例,编写测试代码,还要为每个依赖的服务打桩;另一方面,由于集成的时间点比较晚,大量接口不一致导致的问题可能会在集成测试的过程中集中爆发,造成人力物力的浪费。

如果将服务尽早的部署到Kubernetes测试环境中进行联调测试,可以提前发现问题。由于部署的过程在开发过程中需要反复执行,所以很多企业搭建了CICD工具,比较常见的流程是:提交代码 -> Jenkins 拉取源码 -> Maven编译 -> Docker镜像构建 -> 部署 -> 测试。虽然该过程可以自动完成,但是每次调试均需要经历该步骤,耗时比较长,效率很低。同时,Kubernetes集群内部的微服务的特点是,各微服务之间在集群内可以互相访问,集群外部没有很好的方法来获取集群内部的功能,比如读写数据库中的数据等,所以观察服务的运行结果也是比较困难的事情,无法提供像单体应用一样本地单步调试的体验。

Telepresence是一款为Kubernetes微服务框架提供快速本地化开发功能的开源软件。Telepresence在Kubernetes集群中运行的Pod中部署双向网络代理,该Pod将Kubernetes环境(如TCP连接,环境变量,卷)中的数据代理到本地进程。本地进程透明地覆盖其网络,以便DNS调用和TCP连接通过代理路由到远程Kubernetes集群,能够获取远端K8S集群的各项资源。该工具可以实现:

  • 本地服务可以完全访问远程群集中的其他服务;
  • 本地服务可以完全访问Kubernetes的环境变量,Secrets和ConfigMap;
  • K8S中运行的远程服务也可以完全访问本地服务。

实践

1.安装kubectl命令行工具,并配置本地可以访问Kubernetes集群。

  1. 安装Telepresence工具

在OS X中可使用如下命令安装Telepresence工具。

# brew cask install osxfuse
# brew install datawire/blackbird/telepresence
  1. 部署Demo微服务

在集成测试环境的Kubernetes集群中部署微服务。

# kubectl get all
NAME                             READY   STATUS    RESTARTS   AGE
demo-backend-7bb6cf9994-9qnxp    1/1     Running   0          118s
demo-frontend-84b7f5c75f-8r69n   1/1     Running   0          40h

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/demo-backend    ClusterIP   10.233.55.68            8080/TCP         40h
service/demo-frontend   NodePort    10.233.17.241           8081:30001/TCP   40h

NAME                            DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/demo-backend    1         1         1            1           40h
deployment.apps/demo-frontend   1         1         1            1           40h

NAME                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/demo-backend-7bb6cf9994    1         1         1       40h
replicaset.apps/demo-frontend-84b7f5c75f   1         1         1       40h

通过Kubernetes暴露的NodePort访问应用访问该应用,验证服务没有问题。

# curl http://10.20.1.71:30001/
Message from backend is: Hello from demo-backend-7bb6cf9994-9qnxp 
  1. 通过Telepresence工具启动本地服务
# cd spring-cloud-kubernetes/demo-backend/
# telepresence \
    --mount /tmp/known \
    --swap-deployment demo-backend \
    --docker-run \
    --rm \
    -v $(pwd):/build \
    -v $HOME/.m2/repository:/m2 \
    -v=/tmp/known/var/run/secrets:/var/run/secrets \
    -p 8080:8080 \
    -p 5005:5005 \
    maven:3.6-jdk-8-alpine \
        mvn \
            Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005" \
            -Dmaven.repo.local=/m2 \
            -f /build \
            spring-boot:run

参数详细解析如下。

telepresence:启动telepresence的命令;

--mount /tmp/known:告诉Telepresence将TELEPRESENCE_ROOT挂载到本地的/tmp/known目录;

--swap-deployment foo:假设我们已在集群中运行名为foo的Deployment,下一步将使用本地运行的进程进行替换;

--docker-run:告诉Telepresence接下来以Docker容器方式运行;

--rm:告诉Docker终止时丢弃之前创建的容器,不会对本地造成混乱;

-v$(pwd):/build:将当前目录(pwd命令的结果)挂载到Docker容器内的文件夹/build中,这也是本地源代码的位置;

-v $HOME/.m2/repository:/m2:安装Maven缓存文件夹,这样我们就不必每次运行容器时都下载Maven组件;

-v=/tmp/known/var/run/secrets:/var/run/secrets:将上述的本地/tmp/known目录下的secrets目录挂载到本地容器的/var/run/secrets路径下,类似Fabric8 Kubernetes Client的工具可以通过该目录读取Secretes信息,就像容器是运行在Kubernetes内部一样;

-p 8080:8080 -p 5005:5005:将本地容器的服务端口暴露出来,可以在需要直接向服务发出请求时访问该端口;

maven:3.6-jdk-8-alpine:这是我们将用于构建和运行我们的服务的镜像,它包含了Maven和Java 8等工具;

mvn ... spring-boot:run:要在Docker容器中运行的命令。这里它使用Spring Boot Maven插件,但您可以使用构建工具所需的任何命令。它告诉maven指向已安装的存储库缓存以及源代码所在的位置

-Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005":表示为服务打开5005远程调试端口。在Telepresence的文档中是通过MAVEN_OPTS参数传递Debug配置,该配置实际上是对Maven进程开启Debug模式,并不是demo-backend应用,无法提供远程单步调试的能力。

-Dmaven.repo.local=/m2:表示本地Maven仓库的位置;

-f /build:Maven构建时的主目录,也即源代码的根目录;

spring-boot:run:spring-boot-maven-plugin插件发布的Goal,用于启动Spring Boot应用。

查看Kubernetes集群中的Pod信息。可以看到,之前的Pod demo-backend-7bb6cf9994-9qnxp已经被新的容器替换,使用的是Telepresence提供的镜像。

# kubectl get pod
NAME                                                             READY   STATUS    RESTARTS   AGE
demo-backend-c9df931b5c83411aad5a329ec9ecbcbb-5b4fdd7b-wzz9r     1/1     Running   0          11s
demo-frontend-84b7f5c75f-8r69n                                   1/1     Running   0          40h

查看本地Docker运行情况。

# docker ps
CONTAINER ID        IMAGE                              COMMAND                  CREATED             STATUS              PORTS                                                                        NAMES
c7c10281a32c        maven:3.6-jdk-8-alpine             "/usr/local/bin/mvn-…"   35 seconds ago      Up 33 seconds                                                                                    telepresence-1555471863-056155-58429
fd94d372f832        datawire/telepresence-local:0.98   "/sbin/tini -v -- py…"   44 seconds ago      Up 43 seconds       0.0.0.0:5005->5005/tcp, 0.0.0.0:8080->8080/tcp, 127.0.0.1:64163->38022/tcp   telepresence-1555471853-8663979-58429

确认本地开放了8080和5005端口。

# netstat -na | grep -E "5005|8080"
tcp6       0      0  ::1.5005               *.*                    LISTEN
tcp4       0      0  *.5005                 *.*                    LISTEN
tcp6       0      0  ::1.8080               *.*                    LISTEN
tcp4       0      0  *.8080                 *.*                    LISTEN

直接通过8080端口访问本地的demo-backend服务。

# curl localhost:8080
Hello from demo-backend-c9df931b5c83411aad5a329ec9ecbcbb-5b4fdd7b-wzz9r

通过Kubernetes暴露的NodePort访问应用,可以看到此时到demo-backend的请求已经被转发到本地运行的docker中。

# curl http://10.20.1.71:30001
Message from backend is: Hello from demo-backend-80d454f85d8b43118f5740d9f25260ba-854fcd9fbd-lzkb7
  1. 通过IDE进行代码开发和单步调试
  • 启动IDEA,在上述源代码路径上打开工程。

Telepresence:让微服务本地开发不再难_第1张图片

  • 配置远程调试,连接本地5005端口。

Telepresence:让微服务本地开发不再难_第2张图片

  • 设置断点,并访问应用,在IDEA中可以进行单步调试。
    Telepresence:让微服务本地开发不再难_第3张图片

  • 修改代码并保存,然后点击Build -> Build Project。

在demo-backend项目的pom文件中,配置了devtools的依赖。


    
        org.springframework.boot
        spring-boot-devtools
        true
    

而当IntelliJ IDEA重新构建时,会更新该目录下的文件demo-backend/target/classes。通过mvn spring-boot:run方式启动应用时,Devtools监听的classpath,也即上述目录,当目录内的文件发生变化后,会启动restart,执行新的代码。

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::        (v2.1.3.RELEASE)

11:17:44.328 [restartedMain] INFO  c.b.s.c.k.b.KubernetesBackendApplication - Starting KubernetesBackendApplication on Waret with PID 58351 (/Users/Waret87/WorkSpace/telepresence/spring-cloud-kubernetes/demo-backend/target/classes started by Waret87 in /Users/Waret87/WorkSpace/telepresence/spring-cloud-kubernetes/demo-backend)
11:17:44.331 [restartedMain] INFO  c.b.s.c.k.b.KubernetesBackendApplication - No active profile set, falling back to default profiles: default
...
11:17:45.931 [restartedMain] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
11:17:45.934 [restartedMain] INFO  c.b.s.c.k.b.KubernetesBackendApplication - Started KubernetesBackendApplication in 1.873 seconds (JVM running for 2.282)
11:20:35.483 [Thread-6] INFO  o.s.s.c.ThreadPoolTaskExecutor - Shutting down ExecutorService 'applicationTaskExecutor'

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot ::        (v2.1.3.RELEASE)

11:20:36.838 [restartedMain] INFO  c.b.s.c.k.b.KubernetesBackendApplication - Starting KubernetesBackendApplication on Waret with PID 58351 (/Users/Waret87/WorkSpace/telepresence/spring-cloud-kubernetes/demo-backend/target/classes started by Waret87 in /Users/Waret87/WorkSpace/telepresence/spring-cloud-kubernetes/demo-backend)
11:20:36.838 [restartedMain] INFO  c.b.s.c.k.b.KubernetesBackendApplication - No active profile set, falling back to default profiles: default
...
11:20:37.272 [restartedMain] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
11:20:37.272 [restartedMain] INFO  c.b.s.c.k.b.KubernetesBackendApplication - Started KubernetesBackendApplication in 0.467 seconds (JVM running for 173.62)

再次访问服务,可以看到新的代码已经生效。

# curl http://10.20.1.71:30001
Message from backend is: Hello World from demo-backend-c9df931b5c83411aad5a329ec9ecbcbb-5b4fdd7b-wzz9r

总结

通过上面的实践我们可以看到,Telepresence项目很好的解决了开发测试和部署上线之间的衔接问题,是对Kubernetes生态环境很好的补充。该项目已捐献给CNCF基金会,社区的文档也是比较完善的,更详细的原理和用法可参考文档。

本文由博云研究院原创发表,转载请注明出处。

Telepresence:让微服务本地开发不再难_第4张图片