SpringCloud微服务部署环境参数动态设置?-灵析社区

今天吃什么你说吧

## SpringCloud微服务运维最佳实践,如何动态定义各种启动参数? > > 笔者是一名Java服务端程序员,学习微服务后,在部署时发现过程复杂,且做的重复工作非常多,因此学习基本运维。目标是,使用Kubernetes和容器技术进行微服务编排和部署。 本文只讨论服务容器化,不涉及K8s和Jenkins相关内容。 ### 问题概述 在服务容器化时,一些参数必须动态传入,以适应不同的部署环境。变化的参数包括: * 部署环境(开发环境、测试环境、预发环境、生成环境) * 服务版本(0.0.1、1.0.2、1.0.1-beta) * 服务发现(部署时将服务注册到注册中心如Nacos) 我的Java项目(Example)由多个微服务构成:如Example-core,Example-auth,Example-gateway等。以Example-auth这个微服务作为切入点,打包为Docker容器的代码为: # 使用官方的OpenJDK 17作为基础镜像 FROM openjdk:17 # 镜像环境变量: # 开发环境:dev # 测试环境:test # 预发环境:staging # 生产环境:prod, 默认生产环境 ARG ENVIRONMENT=prod # Jar包的版本,默认0.0.1-SNAPSHOT ARG JAR_VERSION=0.0.1-SNAPSHOT # 注册中心服务地址 ARG SERVER_NAME=www.nacos-server.cn # 维护者 MAINTAINER xlxing@bupt.edu.cn # 拷贝文件到Docker容器中 COPY target/auth-${JAR_VERSION}.jar /app/auth-${JAR_VERSION}.jar # 暴露服务端口 EXPOSE 8999 # 容器启动项 ENTRYPOINT ["java", "-jar", "/app/auth-${JAR_VERSION}.jar", "--spring.profiles.active=${ENVIRONMENT}", "--spring.cloud.nacos.discovery.ip=${SERVER_NAME}"] 在构建容器时可以动态传入参数(jar包版本为1.1.0,环境是dev,服务发现地址www.my-auth.cn,构建的容器命名为myapp/example:auth-dev-1.1.0): docker build --build-arg JAR_VERSION=1.1.0 ENVIRONMENT=dev SERVER_NAME=www.my-auth.cn -t myapp/example:auth-dev-1.1.0 . 该内容产生了以下两个具体问题: ### 1\. 如何动态获取JAR_VERSION 在构建项目时,仍然需要手动传入项目版本,实际上该信息存在于项目中,如该微服务auth的pom.xml文件: auth 0.0.1-SNAPSHOT auth 权限微服务 理想情况是:后端程序员来定义项目的版本,而流水线只需要依赖项目即可。 但是在当前情况下:当auth项目的version变更时,构建Docker镜像的指令也需要同步变化。该问题可以总结为,**构建容器时如何从项目中获取JAR_VERSION?** docker build --build-arg JAR_VERSION=1.1.0 ENVIRONMENT=dev SERVER_NAME=www.my-auth.cn -t myapp/example:auth-dev-1.1.0 . ### 2\. 如何自动获取宿主机的SERVER_NAME 微服务启动时,将该服务注册到Nacos上,声明自己的服务地址。当部署到具体的服务器上时,该地址才被绑定,如auth服务可以部署在server-a上,也可以部署到server-b上。如何在**运行时** 确定服务地址而不是**构建时** 。 补充内容: auth服务的配置文件 spring: application: name: auth # Nacos配置 cloud: nacos: server-addr: www.nacos-server.cn # 服务发现 discovery: cluster-name: BEIJING # 设置为非临时实例 ephemeral: true # 设置命名空间 namespace: e35500e1-2441-4001-b60f-3f7d55bxxxxx # 配置中心 config: file-extension: yaml # 文件后缀名 namespace: e35500e1-2441-4001-b60f-3f7d55bxxxxx group: DEFAULT_GROUP dubbo: # 将Dubbo注册到Nacos中,这样可供消费者直接使用 application: name: dubbo-auth protocol: name: dubbo port: -1 registry: address: nacos://www.nacos-server.cn?namespace=228df068-54b7-405e-9e32-72c759d79ed9 group: DEV_DUBBO_GROUP ### 拓展问题:使用K8s和容器在分布式环境下部署微服务项目最佳实践? 其中涉及到很多配置相关的问题,缺乏一个系统性的文章探讨如何部署和运维。 ### 一些尝试: 1. 我有两台服务器A和B,我在部署前已经计划将auth服务部署到B上,因此构建时参数SERVER_NAME=B,构建好的容器无法直接在其他服务器上启动。 一条可行的方法是,在构建容器时不指定SERVER_NAME,在`docker run`时动态传入参数。 `ARG SERVER_NAME=www.nacos-server.cn` 改为`ENV SERVER_NAME=www.nacos-server.cn` 2. JAR_VERSION目前是手动更改。 * * * 第一个问题: 我们在构建开发流水线的时候,会写一个脚本(比如Jenkins)。类似于这样的一个过程: 1. 从pom.xml中获取version值,记为VERSION 2. 使用脚本更改Dockerfile中的JAR_VERSION,此时Dockerfile中的JAR_VERSION=${VERSION} 3. 生成新的Docker Image,例如 `docker build -t myapp/auth:${VERSION} .` 总的来说,我们无法直接在Dockerfile中获取pom.xml中的VERSION,但是可以在流水线脚本中往Dockerfile中插入静态值。 第二个问题: 我们可以将流水线部署分为两个部分,**构建** 和**部署** 你的回复没有解决**部署** 时的环境变量问题,SERVER_NAME是与所属的服务器相关的,在使用K8s时构建的配置文件如k8s-prod.yaml中,env也是一些动态参数。使用环境变量只是解决了可以在运行时变更参数的作用。 比如k8s架构有3个子节点k8s-node01、k8s-node02、k8s-node03,在部署的时候将auth这个微服务部署到哪一个节点,对运维工程师来说是透明的。 此时想到了一个笨重的方法:在k8s-node01、k8s-node02、k8s-node03的系统中提前准备好环境变量MY_IPV4,然后在`docker run`的时候参数除了在k8s的yaml文件中定义,也可以从服务器的环境变量中获取。例如,gateway将auth服务路由到`lb://auth`,而`lb://auth`信息是从注册中心拿到的,如`http://111.229.38.208:8999`。 在传统的微服务部署中,我可以基本使用nacos作为注册中心使用。K8s中的注册中心原理是DNS服务器。我现在尝试将传统微服务半自动化部署迁移到k8s部署上来,对于服务注册与发现可能有一些理解不充分。希望前辈指正。 主要问题是:传统部署微服务时,微服务启动时总是将自身(自身所在节点的ip)注册到Nacos,如何使得微服务部署是无状态的。 spring: application: name: gateway cloud: # SpringCloudGateway配置 gateway: default-filters: # http请求的Header中增加 k, v - AddRequestHeader=gateway-env, gateway-dev # 前端个请求uri会拼接一个版本前缀发送到特定的服务 routes: - id: auth uri: lb://auth filters: # 服务版本前缀 - PrefixPath=/api/v1 predicates: - Path=/auth/** - id: multimedia uri: lb://multimedia filters: - PrefixPath=/api/v1 predicates: - Path=/multimedia/** - id: core uri: lb://core filters: - PrefixPath=/api/v1 predicates: - Path=/core/** # Gateway不使用Servlet而是WebFlux main: web-application-type: reactive # 微服务分布式鉴权专用Redis # Gateway和Auth共享一个权限管理Redis数据库 data: redis: client-type: lettuce database: 0 host: www.xingxiaolin.cn port: 6379 lettuce: pool: enabled: true max-active: 8 max-idle: 8

阅读量:184

点赞量:0

问AI
更新: 我大概理解了你的问题了。 1. 你的问题应该是服务注册到nacos的时候,需要把实例的IP端口信息提交上去。问题出在每次部署的时候你需要手动指定这个上报的IP端口,比较麻烦。 这里可能会有问题: 1. gateway在转发请求到其他服务时,应该走“内网”传输。也就是服务应该使用内网IP去注册。所以你可以在启动的时候直接拿到节点的IP/端口(这个是完全可行的,和是不是传统部署都没有关系)。 2. 不知道你是不是使用了自动获取的方式去获取实例的IP,但是注册的时候出了问题,注册的服务都是docker容器内部的172.x开头的地址,这个问题导致了gateway没法正常工作。所以你才想到了去手动指定实例的IP。通过手动指定的方式也可以实现正常注册,但是没有必要多绕一圈。 如果你要用传统的方式部署微服务 1. 使用docker启动容器的时候,建议把网络模式改成host,这样在容器内,能直接获取到节点的IP,注册服务、在多节点的环境,gateway在转发时也都能正常工作。启动实例的时候直接 "docker run --network host -d image:tag" ,就不需要指定额外的参数了。 2. docker 默认情况下是只能单个节点运行的,即使 docker-compose 也是一样的。所以在每个节点上,不管怎么样都要考虑到端口分配问题,它需要手动去维护,所以是会有一些麻烦在。 3. 还是建议你试试用 docker swarm 来做容器编排,简单方便,虽然现在貌似用的不多,但还是docker官方推荐的生产环境部署方式之一。:"https://docs.docker.com/get-started/orchestration/" (https://link.segmentfault.com/?enc=dY9DacIdWDW6Agb54bXx7Q%3D%3D.5SnCeqDY69KtUb5V3ZX%2FFeXSrQbaHT3%2FCX1NX%2FGKVxUljxPkw5qYOTM0xvQDNv9S%2FfieJ0Fip41I%2BEL5xC0VJg%3D%3D) *** 问题1: 如何动态获取 VERSION 通常情况下,在构建的时候就能拿到版本号,通过脚本读取pom.xml文件(或者通过插件,但是实际上也是读取pom.xml) 问题2: 如何自动获取宿主机的SERVER_NAME 可以通过 “环境变量” *** 我举一个使用 docker swarm 部署微服务的例子(k8s也类似): docker-compose.yml ,会启动 gateway / auth / core 三个服务 services: gateway: image: example/gateway:v1 environment: # 给这个服务添加环境变量 - ENVIRONMENT=prod #当前环境/服务名字/nacos注册信息 - SERVER_NAME=gateway - NACOS_SERVER=nacos.domain.com - NACOS_namespace=e35500e1-2441-4001-b60f-3f7d55bxxxxx ports: - "8080:8080" # 暴露网关的端口 deploy: mode: replicated replicas: 2 # 给这个服务部署两个实例,下面的操作都类似。 auth: image: example/auth:v1 environment: - ENVIRONMENT=prod - SERVER_NAME=auth - NACOS_SERVER=nacos.domain.com - NACOS_namespace=e35500e1-2441-4001-b60f-3f7d55bxxxxx deploy: mode: replicated replicas: 2 core: image: example/core:v1 environment: - ENVIRONMENT=prod - SERVER_NAME=core - NACOS_SERVER=nacos.domain.com - NACOS_namespace=e35500e1-2441-4001-b60f-3f7d55bxxxxx deploy: mode: replicated replicas: 2 环境变量,通常都是在“运行时”获取的,但是你可以把一些默认的环境变量,在“构建时”时写入到镜像中。 使用“ARG”也是可以的,但是感觉不太方便。 如果你已经用了微服务,就不建议用 纯docker 去手动管理容器和服务了,你应该把部署的工作交给 “容器编排系统” 来做,比如 k8s / docker-swarm 。 *** nacos读取配置文件的时候,是可以直接从环境变量中取值的,可以不用手动传参数。 *** 补充一个k8s部署的例子: apiVersion: apps/v1 kind: Deployment metadata: name: gateway spec: selector: matchLabels: app: gateway template: metadata: labels: app: gateway spec: containers: - name: gateway image: example/gateway:v1 resources: limits: memory: "128Mi" cpu: "500m" env: - name: ENVIRONMENT value: prod - name: SERVER_NAME value: gateway ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: auth spec: selector: matchLabels: app: auth template: metadata: labels: app: auth spec: containers: - name: auth image: example/auth:v1 resources: limits: memory: "128Mi" cpu: "500m" env: - name: ENVIRONMENT value: prod - name: SERVER_NAME value: auth ports: - containerPort: 8000 --- apiVersion: apps/v1 kind: Deployment metadata: name: core spec: selector: matchLabels: app: core template: metadata: labels: app: core spec: containers: - name: core image: example/core:v1 resources: limits: memory: "128Mi" cpu: "500m" env: - name: ENVIRONMENT value: prod - name: SERVER_NAME value: core ports: - containerPort: 8000 --- apiVersion: v1 kind: Service metadata: name: gateway spec: selector: app: gateway ports: - port: 8000 targetPort: 8000 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress labels: name: example-ingress spec: rules: - host: example.domain.com http: paths: - pathType: Prefix path: "/" backend: service: name: gateway port: number: 8000