ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Nacos+PostgreSQL容器化部署:从驱动定制到生产级实践

Nacos+PostgreSQL容器化部署:从驱动定制到生产级实践 去年底我拿到一个微服务改造项目技术栈里有一个硬性要求注册中心和配置中心必须用 Nacos但底层存储不能再用默认的 Derby 或 MySQL得落到 PostgreSQL 上而且整套东西要交付给运维用 Docker 部署。当时我第一反应是“Nacos PostgreSQL 能行吗”毕竟 Nacos 从 2.x 开始默认只带 Derby生产环境大多数是 MySQLPG 算是冷门搭配。真做下来发现只要把驱动、初始化脚本、配置项三个坑填平这套组合是完全可以跑的而且稳定性不比 MySQL 差。这篇文章就完整记录我的部署过程从 PostgreSQL 容器准备、Nacos 镜像定制到 docker-compose 一键拉起、客户端联调验证再补上生产环境必须注意的安全加固和备份方案。如果你正准备在项目里用 PostgreSQL 作为 Nacos 的存储层或者想把 Nacos 完整容器化交付这篇文章可以直接拿来当操作手册。1. 为什么我最终定了这套组合1.1 项目诉求和选型分析项目本身不是“非 PG 不可”但客户的运维体系里已经有统一的 PostgreSQL 集群所有业务系统的元数据、配置数据都要求集中管理不允许再单独部署一套 MySQL。这个诉求在传统企业里越来越常见一套数据库服务由 DBA 统一运维业务团队只管使用减少数据库种类就是减少运维成本。Nacos 这边官方文档对数据库的支持写得比较保守默认 Derby 适合单机试用MySQL 是生产首选。但 Nacos 从 2.2.0 开始做了数据源抽象层扩展插件里已经能支持 PostgreSQL、达梦这类数据库2.3.x 版本对 PG 的兼容性比早期版本好很多。我用了 2.3.2整体验证下来注册中心、配置中心、命名空间、权限控制这些核心功能都能正常工作。选型时还考虑过另一个方案Nacos 容器里继续用 MySQL通过外部工具把 MySQL 数据同步到 PostgreSQL。这个方案太绕同步延迟和数据一致性问题会让配置中心失去意义直接否掉。1.2 组件版本如何对应容器化部署最怕版本不匹配导致的诡异问题我这次锁定的版本组合是组件版本说明Nacos2.3.2稳定版对 PostgreSQL 支持相对成熟PostgreSQL16.x (alpine)镜像小、性能好满足生产需要Docker Engine24.0 以上低版本对 compose 健康检查支持不够友好Docker Composev2.x直接用 compose 文件编排不用手写 docker run需要提醒的是Nacos 2.3.2 自带的 Docker 镜像分两个变体一个是基于 Alpine Linux 的精简版一个是基于 Ubuntu 的完整版。生产环境建议用完整版镜像体积大几十 MB但排查问题时能省很多事精简版连 curl 都没有健康检查都得额外想办法。PostgreSQL 我选 alpine 版本因为这个容器只做数据存储不承担其他任务glibc 和 musl 的差异在这里不影响使用。如果你的 PG 版本已经是 14、15也完全没问题Nacos 的 JDBC 驱动对 PG 的版本兼容性很好。2. PostgreSQL 数据侧的准备工作2.1 用 Docker 快速拉起 PostgreSQLPostgreSQL 容器本身没有太多坑关键是把数据目录持久化。我个人习惯先写 docker-compose 的一个独立服务等 PG 验证通过后再把 Nacos 加进来。services: postgres: image: postgres:16-alpine container_name: nacos-postgres restart: always environment: POSTGRES_USER: nacos POSTGRES_PASSWORD: nacos2024 POSTGRES_DB: nacos TZ: Asia/Shanghai ports: - 5432:5432 volumes: - pg_data:/var/lib/postgresql/data - ./init-sql:/docker-entrypoint-initdb.d:ro healthcheck: test: [CMD-SHELL, pg_isready -U nacos -d nacos] interval: 10s timeout: 5s retries: 5 volumes: pg_data:这里做了三件重要的事通过POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB三个环境变量让 PG 容器在首次启动时自动创建nacos用户和nacos数据库。这个组合很重要如果只设置了 POSTGRES_USER那默认数据库名会跟用户名一致后面 Nacos 连接时容易搞混。数据目录挂载到命名卷pg_data容器删了重建数据还在。这一步漏掉的话所有配置、服务实例注册信息都会在容器重建后灰飞烟灭。把初始化 SQL 文件目录挂载到/docker-entrypoint-initdb.dPG 容器第一次启动时会按文件名顺序执行里面的 SQL。这就是我用来建表的方式比手工 exec 进容器执行靠谱得多。启动后先用 psql 验证一下docker exec -it nacos-postgres psql -U nacos -d nacos -c select version();。能正常输出 PG 版本信息说明容器本身没问题。2.2 初始化 Nacos 所需库表Nacos 不能用空数据库启动。很多人在这里卡住容器日志报nacos_tenant表不存在或者config_info表不存在就是因为少建了表结构。Nacos 2.3.2 的发行包里已经带了 PostgreSQL 的初始化脚本路径通常在distribution/conf/目录下文件名是nacos-postgresql.sql。如果你本机没有完整安装包从官方 GitHub 仓库对应 tag 的源码目录里也能找到。我习惯把这个 SQL 文件放到项目目录的init-sql/文件夹里由 PG 容器启动时自动执行。脚本内容不用背但有几个结构要点值得知道因为后续排查问题会用到config_info配置主表存配置内容、MD5、租户 ID、分组等是配置中心最核心的表。his_config_info配置历史表记录每次变更用于配置回滚。users、roles、permissions控制台登录账号和权限管理相关。tenant_info命名空间表。config_tags_relation配置标签关联表。group_capacity、tenant_capacity容量管理相关生产环境限制了单个分组或命名空间能创建的配置条数。有一点和 MySQL 不一样PG 的表、字段默认是小写的Nacos 的 SQL 也全部按小写书写所以正常情况下不会出现大小写匹配问题。万一你见过有人用双引号把大写的表名包起来导致报错就不用理他Nacos 官方脚本没这种写法。脚本执行成功的标志是 PG 容器日志里没有 ERROR或者执行完后在数据库里能看到这几张表docker exec -it nacos-postgres psql -U nacos -d nacos -c \dt我见过config_info表建出来了但users表没有的情况几乎都是初始化脚本只执行了一半人为中断了容器启动。解决方式很简单清掉数据卷docker compose down -v重新启动容器让脚本完整执行一遍执行过程中不要碰容器。2.3 数据持久化与权限细节PostgreSQL 的持久化除了数据目录还有日志和归档。开发环境挂数据目录就够生产环境建议把pg_wal目录也单独考虑。好在这套方案里 PG 是承载 Nacos 的专用实例数据量不大单卷即可。权限方面容易踩坑的是Nacos 连接 PG 用的账号nacos虽然能连库但不一定有建表权限。某些版本下如果初始化脚本是在另一个管理员账号下执行的表的所有者不是 nacosNacos 后续写入时会报 permission denied。最稳妥的做法是初始化脚本全部用 nacos 账号执行也就是让 POSTGRES_USER 指定的账号成为数据库和表的 owner避免跨账号权限问题。另外PG 的密码如果包含特殊字符比如、#、$在 JDBC URL 里需要做 URL 编码否则连接串会解析错位。我在项目里直接规避掉密码用纯字母数字加下划线省掉一整套转义问题。3. Nacos 服务的容器化与关键配置3.1 定制基础镜像解决 PG 驱动问题Nacos 官方镜像在 2.3.x 已经内置了 PostgreSQL 的兼容逻辑但不同小版本的镜像里JDBC 驱动不一定齐全。我开始时直接用官方nacos/nacos-server:v2.3.2启动结果日志报org.postgresql.Driver找不到原因就是镜像 lib 目录下只有 MySQL 驱动没有 PG 驱动。解决办法两个一是把 PG 驱动 jar 挂载进容器的plugins目录二是自己写 Dockerfile 打一个定制镜像。我推荐第二种因为交付给运维时镜像本身就是完整的不用额外传驱动文件。FROM nacos/nacos-server:v2.3.2 ENV POSTGRESQL_DRIVER_VERSION42.7.1 RUN mkdir -p /home/nacos/plugins/postgresql ADD https://repo1.maven.org/maven2/org/postgresql/postgresql/${POSTGRESQL_DRIVER_VERSION}/postgresql-${POSTGRESQL_DRIVER_VERSION}.jar \ /home/nacos/plugins/postgresql/postgresql-${POSTGRESQL_DRIVER_VERSION}.jar构建命令docker build -t nacos-server:2.3.2-pg .如果你所在网络访问 Maven 中央仓库不稳定可以先在本地下载好驱动 jar再用COPY指令拷进镜像效果一样。驱动版本建议用 42.5.0 以上的新版适配 PG 16 没有任何问题。这里额外说一句Nacos 加载数据库驱动的机制是从 classpath 扫描plugins目录会被加载但不是所有版本都支持从任意子目录加载。如果挂载进去没生效可以再看一眼官方文档确认插件加载路径。我实测 2.3.2 放在/home/nacos/plugins/postgresql/下是有效的。3.2 application.properties 里的三个关键位置Nacos 默认读conf/application.properties来决定连接什么数据库。用官方镜像时不需要进容器改文件可以通过环境变量或挂载配置文件的两种方式覆盖。我用的是挂载配置文件可维护性最好。挂载前先看官方镜像里默认配置长什么样把它拷贝出来改docker run --rm nacos/nacos-server:v2.3.2 cat /home/nacos/conf/application.properties application.properties改三个地方最关键spring.datasource.platform要从默认值改成postgresql这是选择数据源类型的开关。如果不改Nacos 即使发现了 PG 驱动也会按 Derby 的逻辑走出现各种诡异行为。db.num保持 1表示只有一个数据库实例。后面如果做 PG 主从或集群这个值要相应调整配置格式也要改成 db.url.0、db.url.1 这种。db.url.0是重中之重。默认值指向127.0.0.1:3306必须改成 PostgreSQL 的 JDBC URL。在 docker-compose 网络里主机名直接写 PostgreSQL 的 service 名比如spring.datasource.platformpostgresql db.num1 db.url.0jdbc:postgresql://postgres:5432/nacos?tcpKeepAlivetruereconnecttruestringtypeunspecified db.usernacos db.passwordnacos2024URL 里我特意加了stringtypeunspecified这个参数作用是让 PG JDBC 驱动在遇到类型推断不明确时使用字符串类型处理能避免一些序列化/写入报错。这个是 PG 适配里比较实用的参数MySQL 用户刚开始可能不熟悉。另外db.user.0和db.password.0这种带索引的配置名在新版本 Nacos 里也能用。建议按官方模板写不要混用。3.3 docker-compose 联调和健康检查Nacos 和 PostgreSQL 放到同一个 compose 文件里编排起来最清爽。完整示例services: postgres: image: postgres:16-alpine container_name: nacos-postgres restart: always environment: POSTGRES_USER: nacos POSTGRES_PASSWORD: nacos2024 POSTGRES_DB: nacos TZ: Asia/Shanghai volumes: - pg_data:/var/lib/postgresql/data - ./init-sql:/docker-entrypoint-initdb.d:ro healthcheck: test: [CMD-SHELL, pg_isready -U nacos -d nacos] interval: 10s timeout: 5s retries: 5 nacos: image: nacos-server:2.3.2-pg container_name: nacos-server restart: always depends_on: postgres: condition: service_healthy environment: MODE: standalone JVM_XMS: 512m JVM_XMX: 512m JVM_XMN: 256m NACOS_APPLICATION_PORT: 8848 ports: - 8848:8848 - 9848:9848 - 9849:9849 volumes: - ./conf/application.properties:/home/nacos/conf/application.properties:ro - nacos_logs:/home/nacos/logs healthcheck: test: [CMD-SHELL, curl -f http://localhost:8848/nacos/ || exit 1] interval: 30s timeout: 10s retries: 3 volumes: pg_data: nacos_logs:这里有三个容易忽视的细节第一depends_on里的condition: service_healthy确保 Nacos 只在 PG 完全就绪后启动。没有这个配置两个容器同时启动Nacos 连不上 PG 会直接退出。虽然有 restart: always 兜底但每次重启都会多等几分钟没意义。第二端口不要只开 8848。Nacos 2.x 的客户端和 gRPC 通信固定用 8848 加 1000 的端口偏移也就是 9848客户端 gRPC和 9849服务端间通信。我见过太多人只暴露 8848服务注册服务发现都正常但配置中心动态监听就是不工作其实就是 9848 端口被防火墙挡了。第三MODEstandalone是单机模式必需的。如果不设置Nacos 默认按集群模式启动在没有 VIP 或外部地址的情况下会一直报错。启动命令docker compose up -d docker compose logs -f nacos看到类似Nacos started successfully的日志控制台能访问http://localhost:8848/nacos默认账号密码nacos/nacos能登录这套组合基本就算跑通了。4. 上线前的联调与踩坑实录4.1 注册发现和动态配置怎么验证服务端起来只是第一步整个链路要通还得用客户端项目验证。我准备了一个 Spring Boot 3.x Spring Cloud Alibaba 的示例项目正常引入依赖dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency客户端配置文件bootstrap.yml关键内容spring: application: name: demo-service cloud: nacos: server-addr: localhost:8848 discovery: namespace: public group: DEFAULT_GROUP config: file-extension: yaml namespace: public group: DEFAULT_GROUP启动项目在 Nacos 控制台的“服务管理 - 服务列表”里能看到demo-service注册上来说明注册发现链路没问题。动态配置的验证方法是在控制台“配置管理 - 配置列表”里新建一条配置Data ID 用demo-service.yamlGroup 用DEFAULT_GROUP。代码里加一个RefreshScopeValue的示例接口修改配置并发布后不重启应用接口返回的值就变了。PG 存储下这种动态刷新依赖 9848 端口的 gRPC 长连接所以那个端口没开放时配置中心页面能看到配置但客户端收不到变更通知这点观察指标最明显。4.2 我遇到的5个高频问题按踩坑频率排序以下是部署 Nacos 2.3.2 PostgreSQL 时最容易遇到的问题问题现象原因解决办法找不到 PG 驱动启动日志出现ClassNotFoundException: org.postgresql.Driver官方镜像未内置 PG 驱动按 3.1 节定制镜像把驱动放到 plugins 目录连不上 PGConnection refused或UnknownHostException: postgrescompose 里 service 名写错或 Nacos 启动早于 PG统一 service 名加 depends_on health 条件表不存在relation config_info does not exist没执行初始化 SQL或初始化脚本建到了别的库重新初始化 PG 数据卷确认连接库名是 nacos配置中心正常但客户端动态刷新失效控制台改配置客户端不感知9848/9849 端口未暴露或被防火墙拦截开放端口客户端确认 server-addr 端口可连通修改默认密码后登录异常控制台能登录但提示权限错误users 表有缓存或未正确更新更新 users 表的同时清理 Nacos 本地缓存目录这里重点吐槽一下最后一个问题。很多人在 PG 里直接执行UPDATE users SET password 新密码加密串 WHERE username nacos然后重启 Nacos发现密码根本没生效。Nacos 控制台的用户信息有本地缓存修改数据库后需要清理 Nacos 容器里的data缓存目录或者等缓存过期。更稳妥的做法是在控制台的“权限控制 - 用户管理”界面里改让 Nacos 自己处理缓存刷新。另外PG 的 MD5 加密结果和 MySQL 完全一样算法也是 MD5 加盐这点不用额外处理。如果手动改库密码串必须按 Nacos 的加密规则生成不是简单的md5(明文)别在这里浪费时间。4.3 几个只有 PG 才会踩的细碎坑除了上面的大问题PG 特有的一些细节也值得单独记一笔。第一个是 PG 事务和锁的行为。MySQL 默认隔离级别是 REPEATABLE READPG 是 READ COMMITTED。Nacos 在写配置和发布配置时事务逻辑是为 MySQL 设计的换成 PG 后并发情况下可能出现配置更新丢失或者读取到旧版本的问题。我实测下来单实例部署场景下这个问题出现概率极低但如果要上 PG 主从或集群必须压测配置发布并发场景确认没有锁等待超时。第二个是 PG 的varchar长度语义和 MySQL 不一样。MySQL 的varchar(255)里 255 是字符数PG 也是字符数这点没问题但 PG 对超长写入会直接报错MySQL 在非严格模式下会截断。Nacos 的 SQL 脚本已经把长度定义得比较宽裕正常使用不会触发。可如果你在配置内容里塞了超长的 JSONMySQL 可能悄悄截断PG 会直接报错反过来说明 PG 更严格。第三个是时区。PG 的timestamp类型不带时区Nacos 写入时用的是 JDBC 的默认时区。如果 Nacos 容器的 TZ 和 PG 容器、客户端应用不在同一个时区控制台显示的配置发布时间会比实际差 8 小时。我的方案是三个地方全部统一为Asia/Shanghai避免排查问题时被时间误导。5. 从单机到生产还能怎么演进5.1 安全加固三板斧开发和测试环境可以不管安全一旦要交付生产Nacos 的默认配置是肯定不能直接用的。第一开启鉴权。Nacos 2.2.1 之后社区版默认不再自动开启控制台登录而是要求显式配置鉴权开关。在application.properties里加nacos.core.auth.enabledtrue nacos.core.auth.plugin.nacos.token.secret.key你的自定义密钥长度至少32字节 nacos.core.auth.server.identity.keyserverIdentityKey nacos.core.auth.server.identity.valueserverIdentityValue注意token.secret.key这个配置社区版要求 Base64 编码的密钥长度不够会启动报错。生成方法可以用echo -n 你的随机字符串 | base64。第二修改默认密码。PG 数据库初始化脚本里自带的nacos用户密码是nacos这是公开的必须第一时间改掉。PG 里的nacos用户密码也要改因为它是数据库账号不只是控制台登录账号。两套密码别混用。第三命名空间隔离。不同环境开发、测试、生产用不同命名空间权限控制粒度更细。配置上客户端指定 namespace 就能隔离数据和访问权限Nacos 的权限系统也支持对命名空间级别做粗粒度管控。5.2 持久化与备份以及向集群扩展单机模式可以支撑小规模团队和测试环境但生产环境的注册中心通常要上集群。Nacos 集群模式下多个 Nacos 实例共享同一个 PostgreSQL 数据库通过数据库实现数据一致性。集群的 compose 编排和单机差别不大主要变化是关闭MODEstandalone改用MODEcluster。给每个 Nacos 实例设置不同的NACOS_SERVERS地址列表例如nacos1:8848,nacos2:8848,nacos3:8848。application.properties里配置好数据库连接所有实例连同一个 PG。负载均衡用 Nginx 或 SLB客户端只连虚拟 IP。PostgreSQL 这边如果要做高可用可以部署主从复制或 Patroni 管理但那是另一个大话题。Nacos 对后端数据库的稳定性要求很高PG 单点故障会导致整个注册中心不可用所以数据库层面至少要有自动备份策略最好做主从。备份我用的是 PG 自带的pg_dump每天凌晨对 nacos 库做一次逻辑备份。恢复时直接在新 PG 实例执行备份文件再启动 Nacos 指向新实例即可。我个人在实际操作中的体会是Nacos PostgreSQL 的组合没有网上说的那么“邪门”只要初始化脚本到位、驱动放对位置、连接配置别写错整个过程比想象中顺。最后一件事是我每次搭建都会执行的登录 Nacos 控制台把默认的public命名空间改个显眼的名字防止手滑把配置写错位置。这些小习惯积攒下来能省掉不少半夜排查问题的精力。
返回列表