去年帮客户把一个跑了两年的AWX从内置数据库迁到外置PostgreSQL,那次经历让我彻底改了习惯。他们原来的AWX用的是默认的内置PostgreSQL,结果K8s节点一次磁盘故障,整个任务历史、Inventory、凭据全没了,恢复花了两天。后来我重新用Helm部署AWX 2.19.1,把数据库彻底外置到一套独立的PostgreSQL 15.23实例上,从根上把应用和数据解耦了。这个方案我现在推荐给所有准备在生产环境长期跑AWX的团队。
这篇文章会完整拆解这套方案的落地过程:为什么用Helm部署AWX,还要单独搞外置PostgreSQL 15.23;数据库端怎么初始化和调优;AWX这边怎么配置到外置库;以及我在真实环境里踩过的坑和排查思路。无论是刚开始接触AWX,还是已经在用内置库想迁移的运维同学,这篇文章都能给你一套可复用的方案。
1. 方案设计与选型:Helm + AWX 2.19.1 + 外置PostgreSQL
1.1 为什么弃用内置PostgreSQL
AWX默认的部署方式是内置一个PostgreSQL容器,由awx-operator统一管理。这个方案对测试环境完全够用,但放到生产环境问题就出来了。
先说数据可靠性。内置库的Pod如果被调度到别的节点,或者节点宕机、PVC被误删,整个数据库状态就没了。虽然operator默认会创建PVC,但这些PVC的备份、恢复、监控都得自己额外处理,没有一套成熟的数据库运维流程支撑,风险很高。
再说资源问题。AWX跑起来之后,任务历史、事件记录、Inventory同步记录都会持续写入数据库,数据量增长比大多数人预想的快。我把内置库资源和应用混在一起,Pod频繁重启或者资源配额一紧,数据库先遭殃,应用也跟着抖。
最难受的是备份和升级。内置库的备份要进入Pod里执行pg_dump,或者依赖K8s的Volume快照。而PostgreSQL一旦要升级大版本,就得连容器镜像一起换,操作路径很长。
外置PostgreSQL 15.23之后,这些问题基本都解决了。数据库独立部署,DBA团队现有那套备份、监控、高可用方案可以直接复用;AWX的Pod再怎么重建,数据都在外面;数据库升级也和应用升级解耦,互不影响。
1.2 版本选型的考量
AWX 2.19.1这个版本,官方支持的PostgreSQL版本主要是12、13、14。我为什么在外置场景里选了15.23?因为外置数据库一旦上线,后续大版本升级的周期很长,选一个相对新且补丁完善的版本更稳。PostgreSQL 15.23在稳定性、安全更新和性能方面都比12/14更有优势,实测下来AWX 2.19.1连接15.x完全没问题,但有几个扩展和权限细节需要手动处理,后面会讲。
另外,如果你所在的团队已经有PostgreSQL基础设施,建议优先复用已有的数据库集群。比如公司已经有Patroni高可用集群,AWX作为其中一个实例接进去,运维复杂度会下降一个量级。如果没有,那就独立部署一套单机PostgreSQL 15.23起步,后续再考虑高可用。
1.3 内嵌数据库与外置数据库的对比
| 对比维度 | 内嵌PostgreSQL(Operator默认) | 外置PostgreSQL 15.23 |
|---|---|---|
| 数据持久性 | 依赖K8s PVC和节点状态 | 独立存储,与K8s集群解耦 |
| 备份恢复 | 需进Pod操作,链路长 | 可用pg_dump/PGBackRest等标准工具 |
| 高可用方案 | 需额外搭建,比较冷门 | 可接入Patroni/repmgr等成熟方案 |
| 升级维护 | 跟随AWX版本升级,耦合度高 | 独立升级,不影响AWX应用 |
| 性能监控 | 需单独部署监控,较麻烦 | 可直接接入已有PG监控体系 |
| 资源隔离 | 与AWX应用争抢节点资源 | 数据库独立资源,更稳定 |
这个表格基本回答了“为什么我不建议内置库跑生产”的问题。如果读者只是临时搭个测试环境,那内置库省事很多;但既然你选择了Helm部署AWX 2.19.1,目标大概率是生产可用,外置数据库是更合理的选择。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 部署前置检查:环境与依赖梳理
2.1 需要准备的基础环境
开始安装之前,先把下面的环境清单确认一遍,避免装到一半才发现缺东西。
- Kubernetes集群:版本1.23到1.28我都实测过,AWX 2.19.1兼容性不错。集群至少要有2个可用节点,生产建议3个以上。
- kubectl:能正常访问目标集群,建议和集群版本差异在一个小版本以内。
- Helm:3.x版本,本文中的操作全部基于Helm 3。
- StorageClass:AWX的Projects和资源需要PVC,所以集群里必须有一个可用的StorageClass。可以选择NFS、CephFS或者云厂商的块存储,生产环境优先选支持ReadWriteMany的存储类,因为AWX的Project同步和任务执行会同时读写同一份代码目录。
- Ingress Controller:比如nginx-ingress或traefik。AWX可以通过配置指定ingress_class_name。
- DNS解析:如果你计划通过域名访问AWX,提前把域名解析到Ingress的入口地址。
2.2 数据库连接信息和网络打通
外置PostgreSQL 15.23要能从K8s节点访问。这里有两个容易忽略的点:安全组和pg_hba.conf。云环境下,K8s节点所在的安全组必须放行到数据库5432端口的入站流量。如果你用自建机房,要确认节点和数据库之间的网络路由是通的。
建议在部署AWX之前,先在任意一个K8s节点上用psql客户端测试连通性:
bash复制psql -h <postgres-ip> -p 5432 -U awx -d awx -c "SELECT version();"
这一步能提前暴露大部分网络问题,免得AWX装完再回头排查。
2.3 资源规划
AWX本身不是一个轻量应用。awx-operator会启动awx-web、awx-task、awx-ee这几个核心组件,我把生产环境的建议资源列出来,读者可以直接参考:
| 组件 | CPU请求/上限 | 内存请求/上限 | 副本数 |
|---|---|---|---|
| awx-web | 1核 / 2核 | 2Gi / 4Gi | 2 |
| awx-task | 1核 / 4核 | 2Gi / 8Gi | 2 |
| awx-ee | 250m / 1核 | 512Mi / 2Gi | 1 |
| awx-operator-controller-manager | 200m / 500m | 256Mi / 1Gi | 1 |
外置PostgreSQL 15.23建议至少2核4G,生产环境直接上4核8G加SSD。AWX的数据量增长快,磁盘IO往往是性能瓶颈。
3. 外置PostgreSQL 15.23的部署与调优
3.1 安装PostgreSQL 15.23
在Ubuntu 22.04/24.04上,最简单的办法是用官方apt源。如果系统自带的源版本不是15,可以手动添加官方源:
bash复制sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/scripts/apt.postgresql.org.sh
sudo apt update
sudo apt install -y postgresql-15 postgresql-contrib
sudo systemctl enable --now postgresql
安装完成后,PostgreSQL 15.23的配置目录在/etc/postgresql/15/main/,数据目录在/var/lib/postgresql/15/main/。
另外,如果你偏好容器化部署,也可以用Docker跑一个独立的PostgreSQL 15.23容器,但要注意把数据卷挂到宿主机或独立存储上,否则容器删掉数据就没了。
3.2 核心参数调整
PostgreSQL默认配置是为开发环境设计的,生产使用必须调一遍。我用ALTER SYSTEM来调整,这样不会污染官方配置文件,重启后自动加载。
sql复制ALTER SYSTEM SET listen_addresses = '*';
ALTER SYSTEM SET max_connections = 200;
ALTER SYSTEM SET shared_buffers = '1GB';
ALTER SYSTEM SET effective_cache_size = '3GB';
ALTER SYSTEM SET maintenance_work_mem = '256MB';
ALTER SYSTEM SET work_mem = '16MB';
ALTER SYSTEM SET wal_level = logical;
ALTER SYSTEM SET archive_mode = on;
ALTER SYSTEM SET archive_command = 'test ! -f /backup/wal/%f && cp %p /backup/wal/%f';
然后重启PostgreSQL:
bash复制sudo systemctl restart postgresql
sudo -u postgres psql -c "SHOW max_connections;"
参数含义简单解释一下:max_connections=200是为了避免AWX的并发任务把连接数打满,AWX默认会开多个worker连接数据库;shared_buffers建议设置为系统内存的1/4,我这里1GB对应4G内存的机器;wal_level=logical是为后续做逻辑复制或高可用提前留好的口子,如果你不打算做这些,改成replica也可以。
3.3 创建AWX专用账号、数据库和扩展
AWX需要一个独立的数据库账号,不要用postgres超级用户跑应用,最小权限原则在这里同样适用。
bash复制sudo -u postgres psql
sql复制CREATE ROLE awx WITH LOGIN PASSWORD '你的安全密码';
CREATE DATABASE awx OWNER awx ENCODING 'UTF8' LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8';
ALTER DATABASE awx OWNER TO awx;
\c awx
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS citext;
GRANT ALL ON SCHEMA public TO awx;
这里要特别强调:pg_trgm和uuid-ossp是AWX必须的扩展。AWX的搜索功能依赖pg_trgm提供的模糊匹配,UUID生成依赖uuid-ossp。如果这两个扩展没装,AWX启动后访问某些页面时会直接报relation does not exist或function does not exist。citext也是AWX环境里需要用到的,最开始我漏掉之后,就遇到过一次type "citext" does not exist的报错。
PostgreSQL 15和14之前的版本有一个行为差异:public schema默认不再允许所有用户创建表。虽然上面已经GRANT ALL ON SCHEMA public TO awx,但如果你的PG版本是15.23,建议再检查一下:
sql复制SELECT nspname, nspowner FROM pg_namespace WHERE nspname = 'public';
确认public schema的owner是awx,或者已经授权给awx,否则AWX初始化数据库时会报权限不足。
3.4 配置认证方式
PostgreSQL 15默认的密码认证方式已经是scram-sha-256,比md5安全。AWX的psycopg2驱动对scram-sha-256支持良好,所以默认配置即可。打开pg_hba.conf,找到类似下面的配置:
code复制host all awx 0.0.0.0/0 scram-sha-256
如果数据库只给K8s节点使用,建议把0.0.0.0/0改成K8s节点的网段,例如192.168.1.0/24,减少暴露面。
如果你在AWX的Secret里配置了sslmode=require,那么PostgreSQL这边还要开启SSL。自建环境如果暂时不打算上SSL,就把sslmode设为prefer或disable。正式生产环境建议上SSL,但证书要注意在Secret中配置正确。
4. 用Helm部署AWX 2.19.1并接入外置数据库
4.1 安装awx-operator
AWX官方推荐通过awx-operator管理部署。在Helm场景下,用Helm安装awx-operator是社区里主流的做法,版本由Helm管理,后续升级和回滚都方便。
bash复制helm repo add awx-operator https://ansible.github.io/awx-operator/
helm search repo awx-operator
helm install awx-operator awx-operator/awx-operator --version 2.19.1 -n awx --create-namespace
等operator的Pod运行起来:
bash复制kubectl -n awx get pods
你应该能看到类似awx-operator-controller-manager-xxxx的Pod处于Running状态。这个组件负责监听AWX自定义资源,实际AWX应用Pod由它来创建和管理。
4.2 创建外置数据库连接Secret
AWX通过一个Secret来读取外置数据库的连接信息,字段名是固定的小写:host、port、database、username、password、sslmode。我用stringData而不是data来定义Secret,可以避免手写base64的麻烦。
yaml复制apiVersion: v1
kind: Secret
metadata:
name: awx-postgres-config
namespace: awx
type: Opaque
stringData:
host: 10.0.0.15
port: "5432"
database: awx
username: awx
password: "你的安全密码"
sslmode: prefer
保存为awx-postgres-secret.yaml,然后执行:
bash复制kubectl apply -f awx-postgres-secret.yaml
注意:如果密码中含有
$、&、\这类特殊字符,在YAML里一定要用单引号把整个密码包起来,比如password: 'p@ss$word'。我遇到过不少次密码带特殊符号导致Secret解析异常,AWX连库的时候报connection string is invalid,排查半天才发现是YAML转义的问题。
4.3 编写AWX自定义资源
接下来最关键的一步:定义AWX资源,核心是让operator知道用外置数据库,而不是再起一个内置PostgreSQL。
yaml复制apiVersion: awx.ansible.com/v1beta1
kind: AWX
metadata:
name: awx
namespace: awx
spec:
service_type: ClusterIP
ingress_type: ingress
ingress_class_name: nginx
hostname: awx.example.local
# 关键配置:外置数据库信息都从Secret读取
postgres_configuration_secret: awx-postgres-config
# 管理用户
admin_user: admin
# 资源持久化:项目代码存储在PVC中,避免Pod重启后重新拉取
projects_persistence: true
projects_storage_access_mode: ReadWriteMany
projects_storage_class: nfs-client
# 资源设置(按实际环境调整)
web_replicas: 2
task_replicas: 2
ee_replicas: 1
web_resource_requirements:
requests:
cpu: 1
memory: 2Gi
limits:
cpu: 2
memory: 4Gi
task_resource_requirements:
requests:
cpu: 1
memory: 2Gi
limits:
cpu: 4
memory: 8Gi
几个关键配置单独说一下:
postgres_configuration_secret:这是把AWX指向外置库的唯一入口。只要这个字段配了,operator就不会创建内置PostgreSQL。projects_persistence: true:一定要开。AWX会把Project的代码同步到PVC里,如果不开,每次Pod重启都要重新拉代码,CI/CD流程会慢很多。如果你用NFS存储,projects_storage_access_mode要配成ReadWriteMany。ingress_class_name:根据自己的Ingress Controller类型填,nginx controller就填nginx,traefik就填traefik。admin_password_secret:如果不设置,operator会随机生成admin密码。我建议设置一个自己的Secret来固定密码,避免后面还要去找默认密码。
执行:
bash复制kubectl apply -f awx.yaml
然后观察operator的工作:
bash复制kubectl -n awx get awx awx -w
如果一切正常,过几分钟AWX相关的Pod会逐个创建出来。
4.4 验证Pod和应用启动状态
bash复制kubectl -n awx get pods
正常状态你会看到几个Pod:awx-web、awx-task、awx-ee。注意:如果配置了外置数据库,通常不会看到awx-postgres这个Pod。如果出现了,说明你的AWX CR里没有正确读到外置Secret,或者没有设置postgres_configuration_secret。
等所有Pod都变成Running和Ready状态后,获取admin密码。如果你没有自定义,可以这样取:
bash复制kubectl -n awx get secret awx-admin-password -o jsonpath='{.data.password}' | base64 -d
测试访问:
bash复制kubectl -n awx port-forward svc/awx-web 8080:80
本地浏览器打开http://localhost:8080,用admin账号登录。如果配好了Ingress域名,直接用域名访问。
4.5 确认AWX确实在用外置数据库
很多读者困惑“我怎么知道AWX是不是真的连到了外部库”。最简单的方法:去外置PostgreSQL里查一下连接和表。
bash复制sudo -u postgres psql -h localhost -p 5432 -U awx -d awx -c "SELECT datname, usename FROM pg_stat_database WHERE datname='awx';"
sudo -u postgres psql -h localhost -p 5432 -U awx -d awx -c "\dt"
如果AWX已经在初始化数据库,你会看到awx数据库里出现大量表,比如main_job、main_inventory、main_host等。这就说明AWX的数据确实在写外置库了。
5. 生产环境常见问题与排查实战
5.1 问题速查表
先给一张速查表,方便读者直接对照:
| 故障现象 | 可能原因 | 排查与解决 |
|---|---|---|
| awx-task Pod CrashLoopBackOff | 无法连接外置PostgreSQL | 检查Secret字段、网络连通性、pg_hba.conf |
| 登录AWX报500,web日志有pg_trgm相关错误 | 外置库未安装pg_trgm扩展 | 在数据库执行CREATE EXTENSION pg_trgm; |
| 日志提示password authentication failed for user "awx" | 密码或认证方式错误 | 检查pg_hba.conf的scram-sha-256配置和密码 |
| Django报type "citext" does not exist | 未安装citext扩展 | 执行CREATE EXTENSION citext; |
| AWX页面能开,但任务日志一直pending | 数据库连接池或任务并发问题 | 检查max_connections、task副本数 |
| Secret中密码含特殊字符导致连库失败 | YAML转义错误 | 用kubectl或单引号包裹密码 |
| 项目同步到一半失败,PVC报Pending | StorageClass不存在或无RWX支持 | 检查StorageClass和accessMode |
| operator没有创建任何AWX Pod | AWX CR没被识别 | 检查kubectl get awx -A和operator日志 |
5.2 实战排查:awx-task连接超时
这个场景我遇到过至少三次。现象是awx-operator装好后,awx-task一直在CrashLoopBackOff,日志里面反复出现:
code复制django.db.utils.OperationalError: could not connect to server: Connection timed out
排查步骤:
第一,看Secret里的host、port、database、username、password是不是都正确。很多时候问题就出在host填成数据库容器名而不是外部IP。
第二,看网络。在K8s节点上直接telnet数据库端口:
bash复制telnet 10.0.0.15 5432
如果超时,说明网络没通,检查安全组、防火墙、路由。
第三,看pg_hba.conf。即使网络通了,如果pg_hba.conf里没有放行K8s节点网段,PostgreSQL会直接拒绝连接,日志会提示no pg_hba.conf entry for host。
5.3 实战排查:登录500但数据库连接正常
AWX管理员能进登录页面,但输入账号密码后页面直接500,后台web日志里类似:
code复制django.db.utils.ProgrammingError: type "citext" does not exist
这个原因很明确:AWX初始化数据库时,需要创建带citext类型的字段,但外置PostgreSQL里没有这个扩展。解决办法:
bash复制sudo -u postgres psql -d awx -c "CREATE EXTENSION IF NOT EXISTS citext;"
顺便把另外两个也补上:
bash复制sudo -u postgres psql -d awx -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
sudo -u postgres psql -d awx -c 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp";'
装完扩展之后,重启AWX相关Pod:
bash复制kubectl -n awx delete pod -l app.kubernetes.io/name=awx
让operator重建Pod,AWX会自动完成后续迁移。
5.4 时区问题与数据库连接池优化
AWX和Django默认使用UTC时区,如果你的PostgreSQL设置为本地时区,在某些定时任务或任务历史显示上会出现时间偏移。建议把PostgreSQL也设置为UTC,保持一致:
sql复制ALTER SYSTEM SET timezone = 'UTC';
另外,AWX的并发任务会占用数据库连接。任务一多,连接数可能打满。为此把max_connections调到200还不够,还要注意AWX这边awx-task的副本数。如果只有一个task副本,Job队列容易积压。我在生产环境通常保持2个task副本,数据库端再配合max_connections=200,目前没有遇到连接瓶颈。
5.5 一个容易被忽略的小问题:operator升级后数据库自动迁移
AWX升级时,operator会自动执行数据库migration。外置PostgreSQL和内置库不同,AWX对它有完整的写权限,所以迁移一般能正常完成。但要注意,如果外置库当前连接数已经很高,或者磁盘空间不足,migration可能卡住。
遇到这种情况,不要直接强行重启Pod。先看awx-task日志里migration卡在哪一步,再检查外置库的活跃连接和磁盘。
sql复制SELECT count(*) FROM pg_stat_activity WHERE datname = 'awx';
SELECT pg_size_pretty(pg_database_size('awx'));
如果连接数异常,可以手动杀掉几个空闲连接:
sql复制SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='awx' AND state='idle';
空间不足的话,清理一下WAL或旧数据,再让awx-task继续跑。
写在最后的运维建议
这套方案跑了一年多,给我最大的体会是“外置数据库值得一开始就做,而不是出了问题再迁移”。如果你现在还处于AWX搭建阶段,直接按本文的流程部署,省掉后面数据迁移的折腾。如果你已经用了内置库,也别慌,迁移的思路其实就是:在新实例上初始化好数据库和扩展,把AWX的Secret指过去,重启Pod等数据迁移完成。
另外想提醒一点:外置PostgreSQL的监控一定要提前配好。AWX的数据库一旦CPU打满,任务队列会明显变慢,页面操作也会卡顿。数据库CPU、磁盘、连接数这三项指标建议放到你现有的监控系统里,告警阈值可以先按CPU 80%、磁盘使用率70%、连接数达到max_connections的80%来设置。
最后再分享一个小技巧:每次对AWX做版本升级或数据库调整前,先执行一次pg_dump做逻辑备份,再动手。这样即使升级过程出现意外,也能快速回滚到升级前的状态。自从养成这个习惯,我再也没有在AWX升级的时候凌晨爬起来救火了。
