[nacos] 基于Docker安装Nacos

0 序

  • Nacos(配置中心、注册中心)是分布式系统的核心子系统之一,是国内分布式系统/微服务领域的事实标准、事实上的唯一选择。

  • 环境信息
  • centos : 7.9
  • docker : 25.0.4
  • mysql : 5.7.x
  • nacos-server : 2.1.1 (部署模式: nacos standalone / 单机mysql5.7)
    注:调研了多个版本后的结论————不同nacos-server版本之间的差异较大,容器内的目录结构、脚本等变化较大。
    注:v2.1.1 与 v2.0.3 的区别:1、镜像版本不同;2、mysql数据库脚本不同;(容器内的文件、目录结构,尚未发现差异点)

1 安装步骤(nacos/nacos-server镜像方式)

Step1 拉取镜像

docker pull nacos/nacos-server:v2.1.1

docker images

Step2 创建、并运行 Nacos Server Demo 容器

  • 创建、并运行 Nacos Demo 容器
docker run --name demo-nacos-server \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--privileged=true \
--restart=always \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-d nacos/nacos-server:v2.1.1

docker ps -a

  • 访问 Nacos Server Demo 容器

http://127.0.0.1:8848/nacos/index.html
账号 / 密码 【默认】 : nacos / nacos

Step3 在宿主机中创建持久化文件目录(日志/核心配置)

  • 在宿主机中创建日志、配置目录
mkdir -p /data/nacos/logs
 
mkdir -p /data/nacos/conf
  • 从Demo容器中拷贝默认配置到宿主机中
docker exec -it demo-nacos-server sh -c "ls -la /home/nacos/conf"

docker cp demo-nacos-server:/home/nacos/conf/application.properties /data/nacos/conf/

# 拷贝 mysql 8 的 ddl sql : schema.sql (本教程中,实际不会使用)
docker cp demo-nacos-server:/home/nacos/conf/schema.sql /data/nacos/conf/
# nacos-server : 2.1.1 的docker镜像中漏掉了 mysql 5.7 的 ddl sql : nacos-mysql | 详情参加: https://github.com/alibaba/nacos/releases/tag/2.1.1 的 release 包

# 拷贝 ipv6的补丁 ddl sql : 1.4.0-ipv6_support-update.sql
docker cp demo-nacos-server:/home/nacos/conf/1.4.0-ipv6_support-update.sql /data/nacos/conf/

docker cp demo-nacos-server:/home/nacos/conf/nacos-logback.xml /data/nacos/conf/nacos-logback.xml

ls -la /data/nacos/conf/

nacos-server-2.1.1.zip 中存在 mysql 5.7 的 ddl 脚本

Step4 MySQL(5.7)数据库初始化

创建数据库

CREATE DATABASE nacos DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

创建用户

CREATE USER nacos@'%';
ALTER USER nacos@'%' IDENTIFIED BY 'nacosPassword';
GRANT ALL PRIVILEGES ON nacos.* TO nacos@'%';
FLUSH PRIVILEGES;

SHOW GRANTS FOR 'nacos'@'%'; -- localhost

use nacos; -- 使用 nacos 数据库

创建表

  • 执行 : nacos-mysql.sql

脚本内容来源于 : https://github.com/alibaba/nacos/releases/tag/2.1.1nacos-server-2.1.1.zip 包的conf目录的nacos-mysql.sql脚本文件
或参见:https://github.com/alibaba/nacos/blob/2.1.1/distribution/conf/nacos-mysql.sql

/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info   */
/******************************************/
CREATE TABLE `config_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) DEFAULT NULL,
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  `c_desc` varchar(256) DEFAULT NULL,
  `c_use` varchar(64) DEFAULT NULL,
  `effect` varchar(64) DEFAULT NULL,
  `type` varchar(64) DEFAULT NULL,
  `c_schema` text,
  `encrypted_data_key` text NOT NULL COMMENT '秘钥',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_aggr   */
/******************************************/
CREATE TABLE `config_info_aggr` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) NOT NULL COMMENT 'group_id',
  `datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
  `content` longtext NOT NULL COMMENT '内容',
  `gmt_modified` datetime NOT NULL COMMENT '修改时间',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';


/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_beta   */
/******************************************/
CREATE TABLE `config_info_beta` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  `encrypted_data_key` text NOT NULL COMMENT '秘钥',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_tag   */
/******************************************/
CREATE TABLE `config_info_tag` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_tags_relation   */
/******************************************/
CREATE TABLE `config_tags_relation` (
  `id` bigint(20) NOT NULL COMMENT 'id',
  `tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
  `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `nid` bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`nid`),
  UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = group_capacity   */
/******************************************/
CREATE TABLE `group_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';

/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = his_config_info   */
/******************************************/
CREATE TABLE `his_config_info` (
  `id` bigint(20) unsigned NOT NULL,
  `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `data_id` varchar(255) NOT NULL,
  `group_id` varchar(128) NOT NULL,
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL,
  `md5` varchar(32) DEFAULT NULL,
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `src_user` text,
  `src_ip` varchar(50) DEFAULT NULL,
  `op_type` char(10) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  `encrypted_data_key` text NOT NULL COMMENT '秘钥',
  PRIMARY KEY (`nid`),
  KEY `idx_gmt_create` (`gmt_create`),
  KEY `idx_gmt_modified` (`gmt_modified`),
  KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';


/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = tenant_capacity   */
/******************************************/
CREATE TABLE `tenant_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';


CREATE TABLE `tenant_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `kp` varchar(128) NOT NULL COMMENT 'kp',
  `tenant_id` varchar(128) default '' COMMENT 'tenant_id',
  `tenant_name` varchar(128) default '' COMMENT 'tenant_name',
  `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
  `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
  `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
  `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';

CREATE TABLE `users` (
	`username` varchar(50) NOT NULL PRIMARY KEY,
	`password` varchar(500) NOT NULL,
	`enabled` boolean NOT NULL
);

CREATE TABLE `roles` (
	`username` varchar(50) NOT NULL,
	`role` varchar(50) NOT NULL,
	UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);

CREATE TABLE `permissions` (
    `role` varchar(50) NOT NULL,
    `resource` varchar(255) NOT NULL,
    `action` varchar(8) NOT NULL,
    UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);

INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);

INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');

  • 1.4.0-ipv6_support-update.sql
ALTER TABLE `config_info_tag`
MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`;

ALTER TABLE `his_config_info`
MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL AFTER `src_user`;

ALTER TABLE `config_info`
MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`;

ALTER TABLE `config_info_beta`
MODIFY COLUMN `src_ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT 'source ip' AFTER `src_user`;

Step5 修改配置文件(application.properties)

  • vi /data/nacos/conf/application.properties

修改如下配置项

...

#spring.datasource.platform=${SPRING_DATASOURCE_PLATFORM:""}
spring.datasource.platform=${SPRING_DATASOURCE_PLATFORM:"mysql"}

...

db.num=${MYSQL_DATABASE_NUM:1}
# db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false
db.url.0=jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT:3306}/${MYSQL_SERVICE_DB_NAME:nacos}?${MYSQL_SERVICE_DB_PARAM:characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&auto
Reconnect=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true}
# [注释此行] db.url.1=jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT:3306}/${MYSQL_SERVICE_DB_NAME}?${MYSQL_SERVICE_DB_PARAM:characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&aut
oReconnect=true&useSSL=false}
db.user=${MYSQL_SERVICE_USER}
db.password=${MYSQL_SERVICE_PASSWORD}

...

#server.tomcat.basedir=
server.tomcat.basedir=file:.

...

Step6 重建正式的 Nacos Server 容器

  • 删除正在运行的 Nacos Demo 容器,重新用命令创建启动
docker stop demo-nacos-server

docker rm demo-nacos-server
  • 启动 正式的 Nacos Server 容器
docker run --name nacos-server \
-p 18848:8848 \
-p 19848:9848 \
-p 19849:9849 \
--privileged=true \
--restart=always \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e NACOS_AUTH_ENABLE=true \
-e MYSQL_SERVICE_HOST="172.17.0.1" \
-e "MYSQL_SERVICE_PORT=33060" \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e MYSQL_SERVICE_USER=nacos \
-e MYSQL_SERVICE_PASSWORD="123456" \
-e JVM_XMS=256m \
-e JVM_XMX=256m \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-v /data/nacos/logs:/home/nacos/logs \
-v /data/nacos/conf/:/home/nacos/conf/ \
-d nacos/nacos-server:v2.1.1
  • NACOS_AUTH_ENABLE=true : 开启身份鉴权(默认:不开启)。若不开启,有安全风险(可直接调用Nacos Web API,这是高危漏洞)
  • 172.17.0.1 : 在Linux系统中,可以使用特殊的IP地址172.17.0.1来代表宿主机,这个IP地址通常用于Docker默认的网络桥接模式中。

注:docker 容器内的 127.0.0.1 ≠ 宿主机中的 127.0.0.1

  • 查看 Nacos Server 容器
docker ps -a
docker logs nacos-server

ls -la /data/nacos/logs

docker exec -it nacos-server sh -c "ls -la /home/nacos/conf/application.properties"
docker exec -it nacos-server sh -c "cat /home/nacos/conf/application.properties"

# ↓ 此命令无效,需进入容器内部才能查看到真实情况
# docker exec -it nacos-server sh -c "echo $MYSQL_SERVICE_HOST"

docker exec -it nacos-server bash
> echo $MYSQL_SERVICE_HOST
> exit

# 宿主机中测验:
curl http://127.0.0.1:18848/nacos
  • 测验:调用Nacos Web API
# 通过Nacos Web API 获取登录accessToken (nacos server开启身份鉴权后,web api request中需依赖此accessToken)
curl -X POST 'http://xx.yy.zz.ff:18848/nacos/v1/auth/login' -d 'username=nacos&password=nacos'
# 注:demo response : {"accessToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTcxMTYzNzE3NX0.ul940V4eB3aQwsQLeGQqgbnkrL0n45bkeSYBfGvYM2w","tokenTtl":18000,"globalAdmin":true,"username":"nacos"}
# 注:demo 关键内容翻译 : accessToken : '{"alg":"HS256"}.{"sub":"nacos","exp":1711637175}.ul940V4eB3aQwsQLeGQqgbnkrL0n45bkeSYBfGvYM2w'

# 服务注册
# curl -X POST 'http://xx.yy.zz.ff:18848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'
curl -X POST 'http://xx.yy.zz.ff:18848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080&accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTcxMTYzNzE3NX0.ul940V4eB3aQwsQLeGQqgbnkrL0n45bkeSYBfGvYM2w'

# 服务发现
# curl -X GET 'http://xx.yy.zz.ff:18848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'
curl -X GET 'http://xx.yy.zz.ff:18848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName&accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTcxMTYzNzE3NX0.ul940V4eB3aQwsQLeGQqgbnkrL0n45bkeSYBfGvYM2w'

# 发布配置
# curl -X POST "http://xx.yy.zz.ff:18848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
curl -X POST "http://xx.yy.zz.ff:18848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld&accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTcxMTYzNzE3NX0.ul940V4eB3aQwsQLeGQqgbnkrL0n45bkeSYBfGvYM2w"

# 获取配置
# curl -X GET "http://xx.yy.zz.ff:18848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
curl -X GET "http://xx.yy.zz.ff:18848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTcxMTYzNzE3NX0.ul940V4eB3aQwsQLeGQqgbnkrL0n45bkeSYBfGvYM2w"

nacos ui

Y 扩展:nacos身份认证与鉴权

Y.1 Nacos启用身份认证/鉴权

  • 注意
  • Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。
  • Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。
  • 如果运行在不可信的网络环境或者有强鉴权诉求,请参考官方简单实现做替换增强。

Y.1.1 非Docker环境

  • 按照官方文档配置启动,默认是不需要登录的,这样会导致配置中心对外直接暴露。而启用鉴权之后,需要在使用用户名和密码登录之后,才能正常使用nacos。
  • 开启鉴权之前,application.properties中的配置信息为:
### If turn on auth system:
nacos.core.auth.enabled=false
  • 开启鉴权之后,application.properties中的配置信息为:
### If turn on auth system:
nacos.core.auth.system.type=nacos
nacos.core.auth.enabled=true
  • 【自定义密钥】
    开启鉴权之后,你可以自定义用于生成JWT令牌的密钥,application.properties中的配置信息为:

注意:
文档中提供的密钥为公开密钥,在实际部署时请更换为其他密钥内容,防止密钥泄漏导致安全风险。
在2.2.1版本后,社区发布版本将移除以文档如下值作为默认值,需要自行填充,否则无法启动节点。
密钥需要保持节点间一致,长时间不一致可能导致403 invalid token错误。

### The default token(Base64 String):
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789

自定义密钥时,推荐将配置项设置为Base64编码的字符串,且原始密钥长度不得低于32字符。例如下面的的例子:

### The default token(Base64 String):
nacos.core.auth.default.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=

注意:鉴权开关是修改之后立马生效的,不需要重启服务端。

Y.1.2 Docker环境

  • 官方镜像

如果使用官方镜像,请在启动docker容器时,添加如下环境变量

NACOS_AUTH_ENABLE=true

例如,可以通过如下命令运行开启了鉴权的容器:

docker run --env PREFER_HOST_MODE=hostname --env MODE=standalone --env NACOS_AUTH_ENABLE=true -p 8848:8848 nacos/nacos-server

除此之外,还可以添加其他鉴权相关的环境变量信息:

name description option
NACOS_AUTH_ENABLE 是否开启权限系统 默认:false
NACOS_AUTH_TOKEN_EXPIRE_SECONDS token 失效时间 默认:18000
NACOS_AUTH_TOKEN token 默认:SecretKey012345678901234567890123456789012345678901234567890123456789
NACOS_AUTH_CACHE_ENABLE 权限缓存开关 ,开启后权限缓存的更新默认有15秒的延迟 默认 : false

然后运行docker-compose构建命令,例如

docker-compose -f example/standalone-derby.yaml up
  • 自定义镜像

如果选择自定义镜像,请在构建镜像之前,修改nacos工程中的application.properties文件,
将下面这一行配置信息

nacos.core.auth.enabled=false

修改为

nacos.core.auth.system.type=nacos
nacos.core.auth.enabled=true

然后再配置nacos启动命令。

Y.2 客户端如何进行鉴权

Y.2.1 Java SDK鉴权

在构建“Properties”类时,需传入用户名和密码。

properties.put("username","${username}");
properties.put("password","${password}");
  • 示例代码
try {
    // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code.
	String serverAddr = "{serverAddr}";
	Properties properties = new Properties();
	properties.put("serverAddr", serverAddr);

    // if need username and password to login
        properties.put("username","nacos");
        properties.put("password","nacos");

	ConfigService configService = NacosFactory.createConfigService(properties);
} catch (NacosException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Y.2.2 其他语言的SDK鉴权

  • Open-API鉴权

首先需要使用用户名和密码登陆nacos。

curl -X POST '127.0.0.1:8848/nacos/v1/auth/login' -d 'username=nacos&password=nacos'

若用户名和密码正确,返回信息如下:

{"accessToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyOTE2Nn0.2TogGhhr11_vLEjqKko1HJHUJEmsPuCxkur-CfNojDo","tokenTtl":18000,"globalAdmin":true}

接下来进行配置信息或服务信息时,应当使用该accessToken鉴权,在url后添加参数accessToken={accessToken},其中{accessToken}为登录时返回的token信息,例如

curl -X GET '127.0.0.1:8848/nacos/v1/cs/configs?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&dataId=nacos.example.1&group=nacos_group'
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=n1'

Y.2.x 身份鉴权测试类 NacosConfigTest

  • 引入Maven依赖
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>

    <!-- <L3> test & code quality [start] -->
    <!-- junit : unit test -->
    <junit.version>4.13.1</junit.version>
    <!-- <L3> test & code quality [end] -->

    <!-- 1.4.3 / 2.1.1 -->
    <nacos-client.version>1.4.4</nacos-client.version>

    <slf4j.version>1.7.25</slf4j.version>
    <log4j.version>2.13.3</log4j.version>
    <guava.version>28.0-jre</guava.version>
    <jackson.version>2.13.4</jackson.version>

    <httpclient.version>4.5.13</httpclient.version>
    <prometheus.client.version>0.9.0</prometheus.client.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>

    <!-- nacos-client [start] -->
    <!-- https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-api -->
    <dependency>
      <groupId>com.alibaba.nacos</groupId>
      <artifactId>nacos-api</artifactId>
      <version>${nacos-client.version}</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba.nacos</groupId>
      <artifactId>nacos-common</artifactId>
      <version>${nacos-client.version}</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client -->
    <dependency>
      <groupId>com.alibaba.nacos</groupId>
      <artifactId>nacos-client</artifactId>
      <version>${nacos-client.version}</version>
    </dependency>
    <!-- nacos-client [end] -->

    <!-- log [start] -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-jul</artifactId>
      <!--<version>2.13.3</version>-->
      <version>${log4j.version}</version>
      <scope>compile</scope>
    </dependency>
    <!-- log [end] -->

    <!-- cache [start] -->
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>${guava.version}</version>
    </dependency>
    <!-- cache [end] -->

    <!-- jackson | start -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <!-- jackson | end -->


    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>${httpclient.version}</version> <!-- 使用最新的稳定版本 -->
    </dependency>

    <!-- prometheus | start -->
    <!-- java 读取Prometheus api/v1/query - 51cto - https://blog.51cto.com/u_16175470/6860623 -->
    <!-- prometheus使用三(自定义监控指标实现) - 博客园 - https://www.cnblogs.com/lpcyj/p/13408930.html -->
    <dependency>
      <groupId>io.prometheus</groupId>
      <artifactId>simpleclient</artifactId>
      <version>${prometheus.client.version}</version>
    </dependency>

    <dependency>
      <groupId>io.prometheus</groupId>
      <artifactId>simpleclient_httpserver</artifactId>
      <version>${prometheus.client.version}</version>
    </dependency>

    <dependency>
      <groupId>io.prometheus</groupId>
      <artifactId>simpleclient_pushgateway</artifactId>
      <version>${prometheus.client.version}</version>
    </dependency>
    <!-- prometheus | end -->

  </dependencies>
  • NacosConfigTest
package org.example;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import org.junit.Test;

import java.util.Properties;

/**
 * nacos 配置中心测试
 * @description NACOS Server 端 : 2.0.3 / NACOS 客户端 : 1.4.3
 * @reference-doc
 *  [1] NACOS 用户指南(JAVA SDK) - nacos - https://nacos.io/zh-cn/docs/sdk.html
 *  [2] https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client
 */
public class NacosConfigTest {
    /** NACOS 服务器地址 **/
    private final static String SERVER_ADDR = "https://config-uat.xxxx.com.cn"; // 形如 : 127.0.0.1:8848 或 http://127.0.0.1:8848

    private final static String USERNAME = "nacos";//nacos
    private final static String PASSWORD = "nacos";//nacos
    private final static String NAMESPACE = "xxx_team";//public
    private final static String GROUP = "DEFAULT_GROUP";//DEFAULT_GROUP
    private final static String DATA_ID = "cn.com.xxxx.business.parse";

    /**
     * NACOS 服务器端启用强制客户端输入密码,但客户端不提供密码
     * @sample [shell] curl -X GET 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.example&group=com.alibaba.nacos'
     * @descrption
     * [1] nacos server 2.0.3 : 执行成功
     * [2] nacos server 2.3.1 : 执行失败,ErrCode:403, ErrMsg:<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback
     */
    @Test
    public void nacosServerEnablePasswordButClientNotUsePasswordTest(){
        try {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, SERVER_ADDR);
            properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE);
            //properties.put(PropertyKeyConst.USERNAME, USERNAME);
            //properties.put(PropertyKeyConst.PASSWORD, PASSWORD);
            ConfigService configService = NacosFactory.createConfigService(properties);
            String content = configService.getConfig(DATA_ID, GROUP, 5000);
            System.out.println(content);
        } catch (NacosException e) {//读取配置超时或网络异常,抛出 NacosException 异常
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * NACOS 服务器端不启用强制客户端输入密码,且客户端也提供密码
     * @description
     * [1] nacos server 2.0.3 : 密码正确时,执行成功 ; 密码错误时, 报 403
     * [1] nacos server 2.3.1 : 密码正确时,执行成功 ; 密码错误时, 报 403
     */
    @Test
    public void nacosServerDisablePasswordAndClientUsePasswordTest(){
        try {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, SERVER_ADDR);
            properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE);
            properties.put(PropertyKeyConst.USERNAME, USERNAME);
            properties.put(PropertyKeyConst.PASSWORD, PASSWORD);
            ConfigService configService = NacosFactory.createConfigService(properties);
            String content = configService.getConfig(DATA_ID, GROUP, 5000);
            System.out.println(content);
        } catch (NacosException e) {//读取配置超时或网络异常,抛出 NacosException 异常
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * NACOS 服务器端不启用强制客户端输入密码,但客户端不提供密码
     * @sample [shell] curl -X GET 'http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.example&group=com.alibaba.nacos'
     * @description
     * [1] nacos server 2.0.3 : 请求成功
     * [2] nacos server 2.3.1 : 请求成功
     */
    @Test
    public void nacosServerDisablePasswordButClientNotUsePasswordTest(){
        try {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, SERVER_ADDR);
            properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE);
            //properties.put(PropertyKeyConst.USERNAME, USERNAME);
            //properties.put(PropertyKeyConst.PASSWORD, PASSWORD);
            ConfigService configService = NacosFactory.createConfigService(properties);
            String content = configService.getConfig(DATA_ID, GROUP, 5000);
            System.out.println(content);
        } catch (NacosException e) {//读取配置超时或网络异常,抛出 NacosException 异常
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * NACOS 服务器端启用强制客户端输入密码,且客户端也提供密码
     * @description 执行成功
     */
    @Test
    public void nacosServerEnablePasswordAndClientUsePasswordTest(){
        try {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, SERVER_ADDR);
            properties.put(PropertyKeyConst.NAMESPACE, NAMESPACE);
            properties.put(PropertyKeyConst.USERNAME, USERNAME);
            properties.put(PropertyKeyConst.PASSWORD, PASSWORD);
            ConfigService configService = NacosFactory.createConfigService(properties);
            String content = configService.getConfig(DATA_ID, GROUP, 5000);
            System.out.println(content);
        } catch (NacosException e) {//读取配置超时或网络异常,抛出 NacosException 异常
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

Y.3 开启服务身份识别功能

  • 开启鉴权功能后,服务端之间的请求也会通过鉴权系统的影响。考虑到服务端之间的通信应该是可信的,因此在1.2~1.4.0版本期间,通过User-Agent中是否包含Nacos-Server来进行判断请求是否来自其他服务端。

  • 但这种实现由于过于简单且固定,导致可能存在安全问题。因此从1.4.1版本开始,Nacos添加服务身份识别功能,用户可以自行配置服务端的Identity,不再使用User-Agent作为服务端请求的判断标准。

开启方式:

### 开启鉴权
nacos.core.auth.enabled=true

### 关闭使用user-agent判断服务端请求并放行鉴权的功能
nacos.core.auth.enable.userAgentAuthWhite=false

### 配置自定义身份识别的key(不可为空)和value(不可为空)
nacos.core.auth.server.identity.key=example
nacos.core.auth.server.identity.value=example

注意 : 所有集群均需要配置相同的server.identity信息,否则可能导致服务端之间数据不一致或无法删除实例等问题。

Y.4 旧版本升级

  • 考虑到旧版本用户需要升级,可以在升级期间,开启nacos.core.auth.enable.userAgentAuthWhite=true功能,待集群整体升级到1.4.1并稳定运行后,再关闭此功能。

H 扩展:应用场景合集

场景:指定NACOS注册中心中spring cloud微服务应用的IP

spring:
  cloud:
    nacos:
      discovery:
        ip: 127.0.0.1

修改完成、并重启服务之后在nacos查看的地址如下:

K 扩展:Nacos 2.x 新特性

K.1 新增端口

  • 2.x最大的变化就是端口。
  • 在默认主端口: 8848 之外又新增了三个端口,新增端口是在配置的主端口(server.port)基础上,进行一定偏移量自动生成:
  • 9848 (主端口+1000)客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求
  • 9849 (主端口+1001)服务端gRPC请求服务端端口,用于服务间同步等
  • 7848 (主端口-1000)Jraft请求服务端端口,用于处理服务端间的Raft相关请求
  • 注意事项

所以,需要注意的是,若需要对主端口做修改, 8848 在修改后一定要大于 1000
如果需要使用Nginx做转发或映射端口,还需要额外转发 主端口 +1000 端口。
使用Nginx请求时,需要配置成TCP转发,不能配置http2转发,否则连接会被Nginx断开。 98497848 端口为服务端之间的通信端口,请勿暴露到外部网络环境和客户端测。

  • nginx
  • 检查你的Nginx是否支持TCP转发:
sudo nginx -V | grep with-stream
  • 配置:
http{
    upstream nacos {
        server xxx.xxx.xxx.xxx:8848;
    }
    server{
        listen 8000;
        location / {
            proxy_pass http://nacos;
        }
    }
}
  • TCP转发:
stream{
    upstream nacosss {
        server xxx.xxx.xxx.xxx:9848;
    }
    server{
        listen 9000;
        proxy_pass nacosss;
    }
}

X 参考文献

  • nacos
  • docker

docker compose + standalone 方式

docker compose + standalone + mysql8 方式

posted @ 2024-03-27 17:20  千千寰宇  阅读(164)  评论(0编辑  收藏  举报