概述
Nacos官方文档:https://nacos.io/zh-cn/docs/what-is-nacos.html
Nacos 致力于发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
服务(Service)是 Nacos 世界的一等公民。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理:
- Kubernetes Service
- gRPC & Dubbo RPC Service
- Spring Cloud RESTful Service
Nacos 的关键特性包括:
- 服务发现和服务健康监测
Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后,服务消费者可以使用DNS TODO 或HTTP&API查找和发现服务。
Nacos 提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助根据健康状态管理服务的可用性及流量。
- 动态配置服务
动态配置服务以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。
动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。
配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
Nacos 提供了一个简洁易用的UI 管理所有的服务和应用的配置。Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,可以更安全地在生产环境中管理配置变更和降低配置变更带来的风险。
- 动态 DNS 服务
动态 DNS 服务支持权重路由,可以更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能更容易地实现以 DNS 协议为基础的服务发现,以消除耦合到厂商私有服务发现 API 上的风险。
Nacos 提供了一些简单的 DNS APIs TODO 管理服务的关联域名和可用的 IP:PORT 列表.
- 服务及其元数据管理
Nacos 能从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。
nacos 整合
参考:Spring cloud alibaba--Nacos Config配置管理
依赖引入
<!-- nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
注意:
- 若使用示例的依赖版本号,Spring Boot 版本要低于 2.4,否则启动应用会报错。
- SpringBoot、SpringCloud 和 nacos 集成版本对应关系对照(版本若对应不上,应用可能会启动报错):
配置 Nacos
项目中默认配置文件是 application.properties ,Nacos 配置加在此配置文件中的话,应用启动会报连接 Nacos 失败,需要创建 bootstrap.properties 或 bootstrap.yml 配置文件(添加任意一个即可),下面以 bootstrap.yml 为例:
spring:
application:
# 项目(微服务)名称
name: apm-mobile-android
cloud:
nacos:
# nacos用户名
username: nacos
# nacos用户密码
password: nacos
# nacos服务器地址
server-addr: 10.0.7.115:18117
# nacos配置中心相关
config:
# 开启nacos作为配置中心,默认值:true
enabled: true
# 作为配置中心的nacos服务器地址,默认值:${spring.cloud.nacos:server-addr}
#server-addr: 10.0.7.115:18117
# 配置文件读取的nacos命名空间ID,默认值:public
namespace: PROD
# 配置文件在nacos命名空间中的分组,默认值:DEFAULT_GROUP
group: apm
# 配置文件的文件前缀(配置文件名称),默认值:${spring.application.name}
prefix: ${spring.application.name}
# 配置文件的文件后缀(文件类型),默认值:properties
file-extension: properties
# 配置内容的编码方式,默认值:UTF-8
encode: UTF-8
# 获取配置的超时时间,单位:ms,默认值:3000
timeout: 3000
# 开启监听和自动刷新,动态感知配置变化,默认值:true
refresh-enabled: true
# AccessKey
#access-key: 123
# SecretKey
#secret-key: 123
# 引入共享配置(同一分组)
shared-configs:
# 配置支持共享的 Data Id
- data-id: comm.properties
# 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP
group: DEFAULT_GROUP
# 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false
refresh: true
# 引入扩展配置(同一分组或不同分组)
extension-configs:
# 配置支持共享的 Data Id
- data-id: comm.properties
# 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP
group: DEFAULT_GROUP
# 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false
refresh: true
# nacos注册中心相关
discovery:
# 开启nacos作为服务注册中心,默认值:true
enabled: true
# 作为注册中心的nacos服务器地址,默认值:${spring.cloud.nacos:server-addr}
#server-addr: 10.0.7.115:18117
# 服务注册在nacso上的命名空间ID,默认值:public
namespace: PROD
加载 Nacos 配置中心配置项
在初始化类中添加 @EnableDiscoveryClient
注解即可:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient // 开启服务发现客户端,加载Nacos配置中心配置项
@SpringBootApplication
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
new BootstrapManager();
}
}
Nacos 配置中心配置项动态生效
前置条件:
bootstrap 配置文件中 nacos 的配置项 refresh-enabled: true(默认开启)
# 开启监听和自动刷新,动态感知配置变化,默认值:true spring.cloud.nacos.config.refresh-enabled=true
方式一:@Value + @RefreshScope 获取最新值
@RestController
@RefreshScope // 配置项动态生效
public class TestController {
@NacosValue(value = "${test.data}", autoRefreshed = true)
private String data;
@Value(value = "${test.data}")
private String datas;
@GetMapping("test")
public String test() {
return "data :" + data + ",datas="+datas;
}
}
方式二:通过 applicationContext.getEnvironment.getProperty 获取最新值
@SpringBootApplication
public class NacosConfigSimpleApplication {
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext applicationContext =
SpringApplication.run(NacosConfigSimpleApplication.class, args);
//取到Spring的配置环境
while(true){
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String username = environment.getProperty("user.name");
String age = environment.getProperty("user.age");
System.out.println("username:"+username+" | age:"+age);
TimeUnit.SECONDS.sleep(1);
}
}
}
方式三:通过 @NacosValue 获取最新值
standalone 使用,@NacosValue 获取最新值 nacos,配置信息需要写在配置类上
@Configuration
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848"))
@NacosPropertySource(dataId = "example", group="test",autoRefreshed = true)
public class NacosConfiguration { }
@Controller
public class ConfigController {
@NacosValue(value = "${test.data}", autoRefreshed = true)
private boolean data;
@RequestMapping(value = "/test", method = GET)
@ResponseBody
public boolean get() { return data; }
}
引入公共的配置文件
默认读取的配置文件是与服务名相同的配置文件的配置;若是还有其它公共的配置需要读取进来,就涉及到多个配置文件的读取。
在 bootstrap 配置文件中引入公共配置文件的方式:
方式一:拓展配置方式
可以引入与默认配置文件同一分组或不同分组的公共配置文件
使用 extension-configs 按数组的方式引入配置,设置data_id、group、refresh值
# 配置支持共享的 Data Id spring.cloud.nacos.config.extension-configs[0].data-id=comm.properties # 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP # 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false spring.cloud.nacos.config.extension-configs[0].refresh=true
- 使用 ext-configs 按数组的方式引入配置,设置data_id、group、refresh值
(ext-config 官方标记废弃;取而代之的是 extension-configs)
方式二:共享配置方式
只能引入与默认配置文件同一分组的公共配置文件
使用 shared-configs 按数组的方式引入配置,设置data_id、group、refresh值
# 配置支持共享的 Data Id spring.cloud.nacos.config.shared-configs[0].data-id=comm.properties # 配置 Data Id 所在分组,缺省默认 DEFAULT_GROUP spring.cloud.nacos.config.shared-configs[0].group=DEFAULT_GROUP # 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false spring.cloud.nacos.config.shared-configs[0].refresh=true
使用 shared-dataids 来指定要读取共享配置文件的 DataID ,多个文件用
,
分隔使用 refreshable-dataids 指定共享配置文件,支持自动刷新(shared-dataids 官方标记废弃;取而代之的是 shared-configs)
spring.cloud.nacos.config.shared-dataids=shareconfig1.yml,shareconfig2.yml spring.cloud.nacos.config.refreshable-dataids=shareconfig1.yml,shareconfig2.yml
配置文件读取优先级:
- profile > 默认 > extension-configs(数组下标越大优先级越大) > shared-configs(数组下标越大优先级越大)
- 数组下标越大优先级越大 即 数组中后面引入的会覆盖前面引入的相同项
支持 profile 粒度的配置
- spring-cloud-starter-alibaba-nacos-config 在加载配置的时候,
不仅加载了以 dataid 为
${spring.application.name}.${file-extension:properties}
为前缀的文件,还加载了 dataid 为
${spring.application.name}-${profile}.${file-extension:properties}
的基础配置。 在日常开发环境中,若是遇到多套配置的情况,可以使用 spring 提供的
${spring.profiles.active}
这个配置项配置选择哪个配置#选择加载的文件为application-dev.properties spring.profiles.active=dev
注意:
- 只有默认的配置文件才能结合 profile 使用;除了默认配置文件,其它配置文件(例如:dev)都需要写上后缀。并且后缀必须跟配置的 boostrap.properties 中配置的扩展名(spring.cloud.nacos.config.file-extension=?)一致
- 默认配置文件:跟服务名spring.application.name相同的DataId的配置文件(无文件扩展名),称之为默认配置文件
部署 nacos
docker stack 部署 nacos 集群
docker stack 部署 nacos 集群的部署模板:docker-compose-swarm-nacos.yml
注:
- @nacos_image、@mysql_image 替换为实际的镜像
- @nacos_logs_path、@nacos_data_path替换为实际的存储路径
version: "3.5"
services:
nacos1:
hostname: nacos1
container_name: nacos1
image: @nacos_image
volumes:
- @nacos_logs_path/nacos1:/home/nacos/logs
ports:
# 8848端口是Nacos对客户端提供服务的端口
- "32101:8848"
expose:
# 7848是Nacos集群通信端口,用于Nacos集群间进行选举,检测等
- "7848"
environment:
NACOS_REPLICAS: 3
MYSQL_SERVICE_HOST: nacos-mysql
MYSQL_SERVICE_DB_NAME: nacos_devtest
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_USER: nacos
MYSQL_SERVICE_PASSWORD: nacos
MODE: cluster
NACOS_SERVER_PORT: 8848
PREFER_HOST_MODE: hostname
NACOS_SERVERS: nacos1:8848 nacos2:8848 nacos3:8848
restart: always
networks:
- apps_net
depends_on:
- nacos-mysql
deploy:
labels:
name: nacos1
placement:
constraints:
- node.role == manager
nacos2:
hostname: nacos2
image: @nacos_image
container_name: nacos2
volumes:
- @nacos_logs_path/nacos2:/home/nacos/logs
expose:
- "8848"
- "7848"
environment:
NACOS_REPLICAS: 3
MYSQL_SERVICE_HOST: nacos-mysql
MYSQL_SERVICE_DB_NAME: nacos_devtest
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_USER: nacos
MYSQL_SERVICE_PASSWORD: nacos
MODE: cluster
NACOS_SERVER_PORT: 8848
PREFER_HOST_MODE: hostname
NACOS_SERVERS: nacos1:8848 nacos2:8848 nacos3:8848
restart: always
networks:
- apps_net
depends_on:
- nacos-mysql
deploy:
labels:
name: nacos2
placement:
constraints:
- node.role == worker
nacos3:
hostname: nacos3
image: @nacos_image
container_name: nacos3
volumes:
- @nacos_logs_path/nacos3:/home/nacos/logs
expose:
- "8848"
- "7848"
environment:
NACOS_REPLICAS: 3
MYSQL_SERVICE_HOST: nacos-mysql
MYSQL_SERVICE_DB_NAME: nacos_devtest
MYSQL_SERVICE_PORT: 3306
MYSQL_SERVICE_USER: nacos
MYSQL_SERVICE_PASSWORD: nacos
MODE: cluster
NACOS_SERVER_PORT: 8848
PREFER_HOST_MODE: hostname
NACOS_SERVERS: nacos1:8848 nacos2:8848 nacos3:8848
restart: always
networks:
- apps_net
depends_on:
- nacos-mysql
deploy:
labels:
name: nacos3
placement:
constraints:
- node.role == worker
nacos-mysql:
hostname: nacos-mysql
container_name: nacos-mysql
image: @mysql_image
volumes:
- @nacos_data_path:/var/lib/mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: nacos_devtest
MYSQL_USER: nacos
MYSQL_PASSWORD: nacos
restart: always
networks:
- apps_net
deploy:
labels:
name: mysql
placement:
constraints:
- node.role == manager
networks:
apps_net:
# 引用已创建的网络。手动创建命令:docker network create -d overlay --attachable apps_net
external: true
k8s 部署 nacos 集群
使用 k8s 的 helm 工具部署 nacos 集群的部署模板:
values.yaml
mysqlImage: "harbor.paic.com.cn/library/nacos-mysql:latest"
nacosImage: "harbor.paic.com.cn/library/nacos-service:1.4.1"
busyboxImage: "harbor.paic.com.cn/library/busybox:1.30.0"
mysqlHost: "mysql"
mysqlPort: "3306"
mysqlDatabase: "nacos_devtest"
mysqlRootPassword: password
mysqlUser: nacos
mysqlPassword: nacos
nfsPath: /home/nacos/
nacosDataPath: /home/nacos/data
service_name: nacos
namespace: public
Chart.yaml
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: nacos
version: 0.1.0
maintainers:
- name: name
templates 文件夹下 configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nacos-cm
namespace: {{ .Values.namespace }}
data:
mysql.host: mysql
mysql.port: "3306"
mysql.db.name: {{ .Values.mysqlDatabase }}
mysql.user: {{ .Values.mysqlUser }}
mysql.password: {{ .Values.mysqlPassword }}
templates 文件夹下 mysql.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: mysql
namespace: {{ .Values.namespace }}
labels:
name: mysql
spec:
replicas: 1
selector:
name: mysql
template:
metadata:
labels:
name: mysql
spec:
containers:
- name: mysql
image: {{ .Values.mysqlImage }}
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: {{ .Values.mysqlRootPassword }}
- name: MYSQL_DATABASE
value: {{ .Values.mysqlDatabase }}
- name: MYSQL_USER
value: {{ .Values.mysqlUser }}
- name: MYSQL_PASSWORD
value: {{ .Values.mysqlPassword }}
volumes:
- name: mysql-data
hostPath:
path: {{ .Values.nacosDataPath }}
nodeSelector:
nacos: nacos-mysql
---
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: {{ .Values.namespace }}
labels:
name: mysql
spec:
ports:
- port: 3306
targetPort: 3306
selector:
name: mysql
templates 文件夹下 nacos.yaml
###使用自建数据库;使用Ingress发布配置后台###
---
apiVersion: v1
kind: Service
metadata:
name: nacos-headless
namespace: {{ .Values.namespace }}
labels:
caas_service: nacos-headless
spec:
ports:
- port: 8848
name: server
protocol: TCP
targetPort: 8848
- port: 7848
name: rpc
targetPort: 7848
clusterIP: None
sessionAffinity: None
type: ClusterIP
publishNotReadyAddresses: true
selector:
caas_service: {{ .Values.service_name }}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ .Values.service_name }}
namespace: {{ .Values.namespace }}
spec:
serviceName: nacos-headless
replicas: 3
template:
metadata:
labels:
caas_service: {{ .Values.service_name }}
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "caas_service"
operator: In
values:
- nacos-headless
topologyKey: "kubernetes.io/hostname"
containers:
- name: k8snacos
imagePullPolicy: Always
image: {{ .Values.nacosImage }}
resources:
requests:
memory: "2Gi"
cpu: "500m"
ports:
- containerPort: 8848
name: client
- containerPort: 7848
name: rpc
livenessProbe:
httpGet:
path: /nacos
port: 8848
initialDelaySeconds: 10
periodSeconds: 3
timeoutSeconds: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /nacos
port: 8848
initialDelaySeconds: 10
periodSeconds: 3
timeoutSeconds: 1
failureThreshold: 5
env:
- name: NACOS_REPLICAS
value: "3"
- name: MYSQL_SERVICE_HOST
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.host
- name: MYSQL_SERVICE_DB_NAME
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.db.name
- name: MYSQL_SERVICE_PORT
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.port
- name: MYSQL_SERVICE_USER
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.user
- name: MYSQL_SERVICE_PASSWORD
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.password
- name: MODE
value: "cluster"
- name: NACOS_SERVER_PORT
value: "8848"
- name: PREFER_HOST_MODE
value: "hostname"
- name: NACOS_SERVERS
value: {{ .Values.service_name }}-0.nacos-headless.{{ .Values.namespace }}.svc.cluster.local:8848 {{ .Values.service_name }}-1.nacos-headless.{{ .Values.namespace }}.svc.cluster.local:8848 {{ .Values.service_name }}-2.nacos-headless.{{ .Values.namespace }}.svc.cluster.local:8848
initContainers:
- command:
- sh
- -c
- sleep 10; mkdir /wls/logs/nacos-0 /wls/logs/nacos-1 /wls/logs/nacos-2 -p;chown -R 798:5682 /wls/logs/nacos-0 /wls/logs/nacos-1 /wls/logs/nacos-2 ; echo init finished
env:
image: {{ .Values.busyboxImage }}
imagePullPolicy: IfNotPresent
name: init
volumeMounts:
- mountPath: /wls/logs/
name: logs
volumes:
- name: logs
hostPath:
path: {{ .Values.nfsPath }}/logs
selector:
matchLabels:
caas_service: {{ .Values.service_name }}
---
# ------------------- App Service ------------------- #
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.service_name }}-nodeport
annotations:
{{- range $key, $value := .Values.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
labels:
caas_service: {{ .Values.service_name }}
spec:
ports:
- name: "nodeport"
port: 8848
protocol: TCP
targetPort: 8848
nodePort: 32101
publishNotReadyAddresses: true
selector:
caas_service: {{ .Values.service_name }}
type: NodePort
检查 nacos 集群是否健康启动
nacos 集群启动后,可能会出现容器状态是 health/running,但实际并未健康启动的情况,会导致微服务注册有问题,需要手动检查下
进入到nacos实例的容器内后执行命令以下,查看响应信息
curl -X PUT '127.0.0.1:8848/nacos/v1/ns/instance/beat?serviceName=nacos'
若nacos实例未健康启动,重启nacos的实例容器后再次检查(一般重启一次后就能健康启动)
# nacos实例健康启动的响应信息 {"clientBeatInterval":5000,"code":20404}