SpringCloud配置实战:演示和测试切换

人气:226 ℃/2023-08-06 18:29:53

SpringCloud配置实战:演示和测试切换。我们现在将实现注册中心和SpringCloud提供者配置信息从SpringCloud Config读取配置文件的方式

工具/材料

SpringCloud Config

Intellij idea

操作方法

现在我们的Config服务端配置OK而且测试通过,我们可以从Config+GitHub进行配置修改并获得内容。

此时我们做一个eureka服务+一个Dept访问的微服务,将这两个微服务的配置统一又github获得实现统一配置分布式管理,完成多环境的变更。

在github本地仓库下新建文件microcloudservice-config-eureka-client.yml、microcloudservice-config-dept-client.yml文件。

把这两个文件上传到github服务器上。

新建项目microcloudservice-config-eureka-client-7001注册中心。

在pom文件中增加相关的依赖包。

修改主程序启动类。

在项目中新建bootstrap.yml、application.yml两个配置文件。

启动SpringCloud Config服务端,测试一下这个Eureka服务端配置是否成功。

新建项目microcloudservice-config-provider-dept-client-8001提供者。

在类路径下新增bootstrap.yml和application.yml配置文件。

启动项目主程序类,查看程序的配置是否正常。

spring cloud部署实战

k8s部署springcloud落地实战步骤

前言

在我的这篇文章中,我介绍了工作中实际落地的方案,大家都很感兴趣,想要更详细地介绍,这里我在本地用minikube给大家做一个演示。

演示的最终效果如下图,把网关部分和ingress去掉了,这里只演示核心,写了一个提供者和一个消费者,然后把他们注册到外部到注册中心nacos中,然后浏览器访问消费者的时候,消费者用注册中心获取的服务名远程去访问提供者的数据。

同时还会演示nacos配置中心动态修改配置文件的效果。从而达到了,k8s只部署应用服务的目的,而公共组件,数据库,配置中心,中间件等放到k8s外面部署。

具体为什么这样搞可以看我的这篇文章,我认为对于我们来说是最适合的方案了。

准备工作

minikube:我这里的演示环境用的minikube。相当于你们的k8s的部署环境

为了方便大家开发和体验Kubernetes,提供了可以在本地部署的Minikube,minikube安装部署方便,功能和真实环境的Kubernetes功能是一样的。

docker:编译和上传应用程序镜像需要用它,相关于你自己的开发环境。

镜像仓库:镜像仓库用来上传打包好的镜像,然后在其他服务器上可以pull下来,我的镜像仓库中心直接用的docker hub官方的。你可以用阿里的或者私有docker中心。

我的演示电脑是mac

springcloud微服务k8s部署

代码说明

这里先看下provider以及consumer核心代码,如下:

provider的controller代码:

@RestController@Slf4j//实时更新配置中心的配置项@RefreshScopepublic class MainController { //注入配置文件中的配置项,从nacos配置中心实时拉取 @Value("${test}") private String test; @GetMapping("helloK8s") public String helloK8s() { String ip = getIp(); System.out.println(test); return "provider--helloK8s test:" test " ip: " ip; } //获取本机ip地址 private String getIp(){ InetAddress ia=null; try { ia=InetAddress.getLocalHost(); String localname=ia.getHostName(); String localip=ia.getHostAddress(); System.out.println("本机名称是:" localname); System.out.println("本机的ip是 :" localip); return localip; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return ""; }}

consumer的controller代码:

@RestController@Slf4j//实时更新配置中心的配置项@RefreshScopepublic class ConsumerController { @Autowired private RestTemplate restTemplate; //注入配置文件中的配置项,从nacos配置中心实时拉取 @Value("${test}") private String test; @GetMapping("/echo/appName") public String echoProviderName(){ //通过nacos注册中心中的服务名first-provider去访问提供者api String url = "http://first-provider/helloK8s"; String res = restTemplate.getForObject(url,String.class); log.info("res is {}",res); return "consumer---config: " test " content:" res; }}

然后就是pom docker打包上传的核心配置部分:

<properties> <java.version>1.8</java.version> <project.docker.name>wanglining1987</project.docker.name> <image.name>test-k8s</image.name> <!-- 私有仓库配置,需要settings.xml文件配合serverId对应存储的是docker仓库中心账号密码 --> <docker.serverId>docker-repository</docker.serverId> <!-- docker仓库中心的地址--> <docker.registry>docker.io</docker.registry> <docker.host>http://192.168.1.105:3375</docker.host> <docker.java.opts>-Xms128m -Xmx128m</docker.java.opts> <docker.volumes>/tmp</docker.volumes> <spring.cloud.version>Hoxton.SR8</spring.cloud.version> <spring.cloud.alibaba.version>2.2.1.RELEASE</spring.cloud.alibaba.version></properties><build> <plugins> <!-- docker插件 --> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>1.2.0</version> <configuration> <!-- 私有仓库配置,需要settings.xml文件配合serverId对应的服务地址 --> <serverId>${docker.serverId}</serverId> <!-- docker私服地址 --> <registryUrl>${docker.registry}</registryUrl> <!-- 指定docker server的地址--> <dockerHost>${docker.host}</dockerHost> <imageName>${project.docker.name}/${image.name}:${project.version}</imageName> <baseImage>java</baseImage> <imageTags> <imageTag>${project.version}</imageTag> </imageTags> <volumes>${docker.volumes}</volumes> <env> <JAVA_OPTS>${docker.java.opts}</JAVA_OPTS> </env> <entryPoint>["sh","-c","java $JAVA_OPTS -jar /${project.build.finalName}.jar"]</entryPoint> <!-- docker的构建目录(构建上下文),包含Dockerfile --> <!--<dockerDirectory>target</dockerDirectory>--> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> <executions> <!-- package之前清除上一次构建的image --> <execution> <id>remove-image</id> <phase>package</phase> <goals> <goal>removeImage</goal> </goals> <configuration> <imageName> ${project.docker.name}/${image.name} </imageName> <imageTags> <imageTag>${project.version}</imageTag> <imageTag>latest</imageTag> </imageTags> </configuration> </execution> <execution> <id>remove-tag-image</id> <phase>package</phase> <goals> <goal>removeImage</goal> </goals> <configuration> <imageName> ${docker.registry}/${project.docker.name}/${image.name} </imageName> <imageTags> <imageTag>${project.version}</imageTag> <imageTag>latest</imageTag> </imageTags> </configuration> </execution> <!-- 将docker:build绑定到package这个phase --> <execution> <id>build-image</id> <phase>package</phase> <goals> <goal>build</goal> </goals> <configuration> <!-- imageName中若不指定tag,则会打上latest --> <imageName>${project.docker.name}/${image.name}:${project.version}</imageName> <!-- 可以使用<imageTags>标签打其他额外的tag --> </configuration> </execution> <!-- 将docker:tag绑定到package这个phase --> <execution> <id>tag-image</id> <phase>package</phase> <goals> <goal>tag</goal> </goals> <configuration> <!-- docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG] --> <!-- images与IMAGE[:TAG]对应,必须在build阶段已经构建了 --> <image>${project.docker.name}/${image.name}:${project.version}</image> <!-- newName与tag命令的第二个参数对应 --> <newName> ${docker.registry}/${project.docker.name}/${image.name}:${project.version} </newName> </configuration> </execution> <execution> <id>tag-image-latest</id> <phase>deploy</phase> <goals> <goal>tag</goal> </goals> <configuration> <!-- docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG] --> <!-- images与IMAGE[:TAG]对应,必须在build阶段已经构建了 --> <image>${project.docker.name}/${image.name}:${project.version}</image> <!-- newName与tag命令的第二个参数对应 --> <newName> ${docker.registry}/${project.docker.name}/${image.name}:latest </newName> </configuration> </execution> <!-- 将docker:push绑定到deploy这个phase --> <execution> <id>push-image</id> <phase>deploy</phase> <goals> <goal>push</goal> </goals> <configuration> <imageName> ${docker.registry}/${project.docker.name}/${image.name}:${project.version} </imageName> </configuration> </execution> </executions> </plugin> </plugins> </build>

这里主要是用maven中docker的插件进行镜像的打包,并且上传到镜像中心。

里面的配置我不做过多讲解了,可以自行百度。我这里主要说明一点,里面的配置项

<docker.host>http://192.168.1.105:3375</docker.host>

这里面的地址配置的是访问本地docker deamon的地址,docker的原理其实就是当你执行docker命令的时候,它会把命令发送给docker deamon程序来执行具体的动作。

Daemon是Docker的守护进程,Docker Client通过命令行与Docker Damon通信,完成Docker相关操作

在mac上默认是访问不到docker deamon的,需要一个代理服务转发下,如下:

Docker代理服务(shipyard/docker-proxy)

这是一个非常轻量级的容器,它只是将请求从TCP转发到Docker监听的Unix套接字。

运行如下命令来开启代理转发:

docker run -p 3375:2375 -v /var/run/docker.sock:/var/run/docker.sock-d -e PORT=2375 shipyard/docker-proxy

然后你的代理地址就是本机ip 3375,配置到pom文件中即可。

开始操作

第一步:编译应用程序打包成jar,并编译成docker镜像,然后push到docker仓库中心。

第一步点击package打包,maven插件会自动把代码打成jar包,然后把之前的docker同名镜像删除再重新build新的docker镜像。执行结果如下:

这里的wanglining1987/test-k8s:provider build出来的镜像

第二步点击push,会把上一步打包好的应用镜像上传到仓库中。执行结果如下:

然后同理对consumer工程也执行这个步骤,最终jar包镜像都上传到了docker镜像中心,如下图:

这两个镜像就是上面编译出来的。分别是消费者和生产者镜像。

第二步,启动minikube,并且启动dashboard

启动minikube,并指定阿里镜像中心

minikube start --registry-mirror=https://j3wvoj70.mirror.aliyuncs.com

启动dashboard,执行命令:minikube dashboard

执行结果如下:

执行成功后,会自动跳转到dashboard界面,如下图:

通过dashboard可以方便地管理k8s,比如操作Service,Deployment, Pod等,每一步操作都会提示对应那个命令,很好用,提供了很大的方便。

第三步,编写k8s的Deplyment对应的yaml文件,来部署应用到Pod中

yaml文件如下:

apiVersion: apps/v1kind: Deploymentmetadata: name: first-deployment-2 labels: app: first-app-labelspec: # 启动三个实例 replicas: 3 selector: matchLabels: app: first-app template: metadata: labels: app: first-app spec: containers: - name: first-app # 指定要运行的镜像,启动后,会从镜像中心下载该镜像并运行 image: wanglining1987/test-k8s:provider ports: - containerPort: 8080---apiVersion: apps/v1kind: Deploymentmetadata: name: consumer labels: app: consumer-labelspec: replicas: 1 selector: matchLabels: app: consumer-app template: metadata: labels: app: consumer-app spec: containers: - name: consumer-app image: wanglining1987/test-k8s:consumer-2 ports: - containerPort: 8082

创建完成文件名:first-deployment.yaml

启动nacos

运行应用镜像之前,记住先把nacos注册中心启动一下,不然就启动失败了,按照最开始的架构,注册中心是放到k8s外面运行,直接在本机电脑上运行启动nacos即可,不放到k8s中。

运行起来后,可以通过 http://localhost:8848/nacos/去访问nacos管理页面

运行镜像

然后执行如下命令,来把provider和consumer对应的镜像运行起来。

kubectl create -f first-deployment.yaml

运行起来后,在dashboard中看下效果,如下:

上面两个是启动起来的Deployment。点进去可以查看Deployment的详情,具体什么是Deployment就不展开说了,k8s里的概念,有兴趣去百度即可。这里也可以用命令去查看运行的Deployment,没有dashboard来的方便直接。

然后可以去查看Deployment部署的Pod,并且去查看Pod内应用运行的日志:

这里consumer运行了一个实例,provider运行了三个,这个实例数量,也是在上面的first-deployment.yaml文件中指定了。查看pod日志:

还是很方便的。可以看到应用起来了。

检查下nacos注册中心中是否已经注册上应用了,打开注册中心管理界面地址如下图:

可以看到有两个服务注册进去了first-provider和first-consumer,其中first-provider是三个实例,first-consumer是一个,跟前面配置的一致,在进去看下服务详情如下:

观察其IP地址,是k8s分配的内部地址,成功注册到外部的nacos上了,这里需要注意,k8s内的服务是可以访问k8s外部的ip地址的,只是外部的机器没有办法访问k8s内部的服务ip。

剩下最后一步了就是通过浏览器访问下消费者,看看能不能成功调用提供者接口,并返回结果到页面。

部署service暴露出来

接下来要通过浏览器访问消费者,怎么访问呢?直接通过ip肯定不行,都是k8s内部的ip地址。

k8s解决了这个问题,都很多方法,可以是ingress,或者直接通过Service直接暴露出来。

这里简单把k8s官网的对service和pod定义拉下来,供参考:

Kubernetes Pods是有生命周期的。他们可以被创建,而且销毁不会再启动。 如果您使用Deployment来运行您的应用程序,则它可以动态创建和销毁 Pod。

一个Kubernetes的Service是一种抽象,它定义了一组Pods的逻辑集合和一个用于访问它们的策略 - 有的时候被称之为微服务。一个Service的目标Pod集合通常是由Label Selector 来决定的(下面有讲一个没有选择器的Service 有什么用处)。

举个例子,想象一个处理图片的后端运行了三个副本。这些副本都是可以替代的 - 前端不关心它们使用的是哪一个后端。尽管实际组成后端集合的Pod可能会变化,前端的客户端却不需要知道这个变化,也不需要自己有一个列表来记录这些后端服务。Service抽象能让你达到这种解耦。

对于熟悉springcloud微服务的,你就可以把k8s里的这个service和springcloud中的微服务概念去对应,道理是差不多的。k8s可以把service通过几种方式暴露出来,供外部访问,有四种方式如下:

ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认的 ServiceType。

NodePort:通过每个节点上的 IP 静态端口(NodePort)暴露服务。 NodePort 服务会路由到自动创建的 ClusterIP 服务。 通过请求 <节点 IP>:<节点端口>,你可以从集群的外部访问一个 NodePort 服务。

LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。

ExternalName:通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型的代理。

这里选择最方便的NodePort方式,对应的Service yaml文件如下:

apiVersion: v1kind: Servicemetadata: name: consumer-service labels: app: consumer-app role: masterspec: type: NodePort#type: LoadBalancer ports: - port: 8083 targetPort: 8082 selector: # 这里的consumer-app会去匹配first-deployment.yaml的template部分定义的lable名 # 从而和具体的Pod关联起来了 app: consumer-app

命名:first-service.yaml,然后通过如下命令启动Service

kubectl create -ffirst-service.yaml

启动成功后,查看dashboard,如下:

可以看到consumer-service启动成功,跟yaml中定义的一致。

然后运行命令:

$minikube service consumer-service --urlhttp://192.168.64.2:32376

会把访问服务的ip地址和端口打印出来,通过http://192.168.64.2:32376就可以访问到consumer-service后面实际对应的应用程序了。

在最开始consumer项目对应的controller中定义了api /echo/appName,把路径拼起来,去浏览器访问:http://192.168.64.2:32376/echo/appName

对照前面provider和consumer代码看下,k8s内部的consumer通过外部的nacos注册中的服务名成功的调用到了provider的接口,并打印输出接口。

代码中还把配置文件中的test变量打印了出来,这个主要是为了测试nacos配置中心的变化能不能更新到k8s内部的服务中的。如下部分

@Value("${test}")private String test;

nacos配置中心中的配置:

文件内容

consumer配置文件

provider配置文件

打印结果中,标红部分是配置中心的配置,也是成功了。

consumer---config: ffff content:provider--helloK8s test:eeeaafff ip: 172.17.0.7

总结

我们的方案使用springcloud全部组件和特性,对开发者开发调试更容易,开发方式也更熟悉。部署的时候只需要部署应用部分,发挥k8s部署扩容方便的优势。结合了两者共同的优势

--后续我会持续输出高质量的文章,我工作中更多实战的东西,以及源码部分的讲解,希望大家多多关注 关注 私信给代码,希望多多支持

推荐

首页/电脑版/网名
© 2025 NiBaKu.Com All Rights Reserved.