任务教程
- 1: Nginx:向外代理暴露Web服务
- 2: Certbot:申请公网HTTPS证书
- 3: Docker:启用容器与镜像代理
- 4: 使用 PostgreSQL 作为 Ansible 的配置清单与 CMDB
- 5: 使用 PostgreSQL 作为 Grafana 后端数据库
- 6: 使用 TimescaleDB + Promscale 存储 Prometheus 时序指标数据
- 7: 使用 Keepalived 为 Pigsty 节点集群配置二层 VIP
- 8: 使用 VIP-Manager 为 PostgreSQL 集群配置二层 VIP
- 9: Citus:部署原生高可用集群
- 10: 高可用演习:3坏2如何处理
1 - Nginx:向外代理暴露Web服务
Pigsty 的 Infra 模块默认会在节点上安装 Nginx,这是一个高性能的 Web 服务器。 Pigsty 使用 Nginx 作为所有本地 WebUI 服务的统一入口,并将其用作本地软件仓库,向内网其他节点提供服务。
当然,用户可以根据需求,调整配置,将 Nginx 用作标准的 Web 服务器,对外提供服务。 无论是作为反向代理,还是直接作为网站服务器,都可以通过适当的配置实现。 Pigsty 本身的文档站与仓库也是通过 Pigsty 自建的 Nginx 对外提供的。
配置概览
Nginx 服务器配置由 infra_portal
参数指定。
用户在这里声明所有需要通过 Nginx 代理的域名,以及对应的上游服务器端点(endpoint
)或本地目录路径(path
)。
例如,默认情况下,Pigsty 会这样配置 Nginx,下面的配置会使用 Nginx 对外暴露 Home,Grafana,Prometheus,Alertmanager 四项服务:
infra_portal: # domain names and upstream servers
home : { domain: h.pigsty }
grafana : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
prometheus : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
blackbox : { endpoint: "${admin_ip}:9115" }
loki : { endpoint: "${admin_ip}:3100" }
当安装 Pigsty 时,Pigsty 会自动根据以上配置生成 Nginx 的配置文件。
/etc/nginx/conf.d/haproxy/ # <--- 存放着 HAPROXY 管理界面的位置定义
/etc/nginx/conf.d/home.conf # <--- Pigsty 默认服务器定义(本地软件源,HAPROXY转发)
/etc/nginx/conf.d/grafana.conf # <--- 代理访问内网 Grafana 服务器
/etc/nginx/conf.d/prometheus.conf # <--- 代理访问内网 Prometheus 服务器
/etc/nginx/conf.d/alertmanager.conf # <--- 代理访问内网 Alertmanager 服务器
Nginx 默认服务于 80/443 端口,home
服务器是本地软件源,同时也是默认的 Nginx 服务器。
如果你想通过 Nginx 访问其他服务,只需要在 infra_portal
中添加相应的配置即可,任何带有 domain
参数的配置都会被 Nginx 自动代理。
配置剧本
当安装 Pigsty 时,这些配置会在默认的 install.yml
剧本,或者 infra.yml
剧本中自动生效。
但是用户也可以在 Pigsty 部署后使用 infra.yml
剧本中的 nginx
子任务重新初始化 Nginx 配置。
./infra.yml -t nginx # 重新配置 Nginx
./infra.yml -t nginx_config # 重新生成 Nginx 配置
./infra.yml -t nginx_launch # 重新启动 Nginx 服务
这意味着如果您想要调整 Nginx 服务器的配置,只需要修改 pigsty.yml
配置文件,并执行上面的 nginx
任务即可生效。
当然,你也可以选择先使用 nginx_config
子任务重新生成 Nginx 配置文件,人工检查后使用 nginx -s reload
重新在线加载配置。
配置详情
配置变量 infra_portal
通常定义在全局变量 all.vars
中,默认值如下所示:
all:
vars:
infra_portal: # domain names and upstream servers
home : { domain: h.pigsty }
grafana : { domain: g.pigsty ,endpoint: "${admin_ip}:3000" , websocket: true }
prometheus : { domain: p.pigsty ,endpoint: "${admin_ip}:9090" }
alertmanager : { domain: a.pigsty ,endpoint: "${admin_ip}:9093" }
blackbox : { endpoint: "${admin_ip}:9115" }
loki : { endpoint: "${admin_ip}:3100" }
默认配置意味着,用户默认可以通过:
h.pigsty
访问home
服务器,这是默认服务器,对应 Pigsty 的文件系统首页与本地软件源,通常指向本机上的/www
目录。g.pigsty
访问grafana
服务器,这是默认的 Grafana 服务,通常指向管理节点(admin_ip
)上的3000
端口。p.pigsty
访问prometheus
服务器,这是默认的 Prometheus 服务,通常指向管理节点(admin_ip
)上的9090
端口。a.pigsty
访问alertmanager
服务器,这是默认的 Alertmanager 服务,通常指向管理节点(admin_ip
)上的9093
端口。
注意这里的 blackbox
和 loki
没有配置 domain
参数,因此不会被添加到 Nginx 的配置中,因为它们没有配置 domain
参数。
但是这并不意味着用户可以直接把这两项定义移除掉,因为内网中的其他服务可能会引用这里的配置(例如日志 Agent 会引用 Loki endpoint 地址发送日志)
用户可以通过丰富的配置参数,为不同的服务配置不同的配置,如下所示:
服务器参数
每一条服务器记录都有一个独一无二的 name
作为 key
,一个配置字典作为 value
。在配置字典中,目前有以下几个可用配置项:
-
domain
:可选,指定代理的域名,如果不填写,则 Nginx 不会对外暴露此服务。对于那些需要知道
endpoint
地址,但不想对外暴露的服务(例如 Loki, Blackbox Exporter),可以不填写此参数 -
endpoint
:可选,指定上游服务的地址,可以是IP:PORT
或者DOMAIN:PORT
。- 当此服务器为上游服务时,可以指定此参数,Pigsty 会生成一个标准的反向代理配置,并将请求转发给上游的
endpoint
地址。 - 此参数与
path
参数是互斥的,不能同时存在:如果一个服务器是反向代理服务器,那么它不能同时是本地网页服务器。 - 在此参数值中,可以使用
${admin_ip}
占位符,Pigsty会填入admin_ip
的值。 - 如果指定了此参数,则 Pigsty 默认会使用
endpoint.conf
配置模板,这是反向代理的标准模板 - 如果同时指定了
conf
参数,则conf
参数指定的模板有更高的优先级。 - 如果上游强制要求 HTTPS 访问,你可以额外设置
scheme: https
参数。
- 当此服务器为上游服务时,可以指定此参数,Pigsty 会生成一个标准的反向代理配置,并将请求转发给上游的
-
path
:可选,指定本地 Web 服务器的根目录,可以是绝对路径或者相对路径。- 当此服务器为本地 Web 服务器时,可以指定此参数,Pigsty 会生成一个标准的本地 Web 服务器配置,并将请求转发给本地的
path
目录。 - 此参数与
endpoint
参数是互斥的,不能同时存在,如果一个服务器是本地网页服务器,那么它不能同时是上游代理服务器。 - 如果指定了此参数,则 Pigsty 默认会使用
path.conf
配置模板,这是本地 Web 服务器的标准模板 - 如果同时指定了
conf
参数,则conf
参数指定的模板有更高的优先级。 - 如果你希望 Nginx 自动生成文件列表索引,可以设置
index: true
参数,默认是不打开的。
- 当此服务器为本地 Web 服务器时,可以指定此参数,Pigsty 会生成一个标准的本地 Web 服务器配置,并将请求转发给本地的
-
conf
: 可选,如果指定,则将会使用templates/nginx/
中定义的配置模板。- 当你想要任意定制 Nginx 配置时可以指定此参数,指定一个存在于
templates/nginx
目录中的模板文件名。 - 如果没有指定此参数,Pigsty 将根据服务器的类型(Home, Proxy, Path)自动应用相应的 默认模板:
home.conf
: 默认服务器的模板(用于 home)。endpoint.conf
: 上游服务代理的模板(例如:用于Grafana,Prometheus 等)path.conf
:本地 Web 服务器的模板(默认没有使用,但home
是特殊的本地服务器)
- 当你想要任意定制 Nginx 配置时可以指定此参数,指定一个存在于
-
certbot
: 可选,指定此服务器的certbot
证书名称,Pigsty 会自动使用certbot
生成的证书。- 如果您的证书是使用 certbot 生成的,你可以指定此参数为
certbot
生成的证书名称,Pigsty 会自动使用此证书。 - 您应当填入 certbot 生成证书的域名部分。例如
certbot: pigsty.cc
会自动使用- 证书:
/etc/letsencrypt/live/pigsty.cc/fullchain.pem
,但可以被显式指定的cert
参数(完整路径)覆盖。 - 私钥:
/etc/letsencrypt/live/pigsty.cc/privkey.pem
,但可以被显式指定的key
参数(完整路径)覆盖。
- 证书:
- certbot 证书名称通常与
domain
参数相同,例如g.pigsty
的证书名称为g.pigsty.cc
,但也有特例:当你同时申请多个证书时,certbot 会生成一个捆绑证书,名称为申请列表中的第一个域名。
- 如果您的证书是使用 certbot 生成的,你可以指定此参数为
-
cert
: 可选,指定此服务器的 SSL 证书文件名,需要给出完整路径。 -
key
: 可选,指定此服务器的 SSL 私钥文件名,需要给出完整路径。 -
domains
: 可选,除了默认的domain
域名,您还可以为此服务器指定多个额外的域名 -
scheme
: 可选,指定此服务器的协议(http
/https
),留空则默认使用 http,通常用于强制要求 HTTPS 访问的上游 Web 服务。 -
index
: 可选,如果设置为true
, Nginx 会为目录自动生成文件列表索引页面,方便浏览文件,对于软件仓库类通常可以打开,对于网站通常应当关闭。 -
log
: 可选,如果指定,则日志将打印到<value>.log
,而非默认的access.log
中。- 作为特例,上游代理服务器的日志总是会打印到
<name>.log
中。
- 作为特例,上游代理服务器的日志总是会打印到
复杂的配置样例
Pigsty 自带的配置模板 conf/demo.yml
有一个更详细的案例,给出了 Pigsty 文档站的配置样例。
是的,Pigsty 的文档站也是在一台普通云服务器上使用 Pigsty 本身建设的,其配置如下所示:
infra_portal:
home : { domain: home.pigsty.cc }
grafana : { domain: demo.pigsty.cc ,endpoint: "${admin_ip}:3000" ,websocket: true ,cert: /etc/cert/demo.pigsty.cc.crt ,key: /etc/cert/demo.pigsty.cc.key }
prometheus : { domain: p.pigsty.cc ,endpoint: "${admin_ip}:9090" }
alertmanager : { domain: a.pigsty.cc ,endpoint: "${admin_ip}:9093" }
cc : { domain: pigsty.cc ,path: "/www/pigsty.cc" ,cert: /etc/cert/pigsty.cc.crt ,key: /etc/cert/pigsty.cc.key }
blackbox : { endpoint: "${admin_ip}:9115" }
loki : { endpoint: "${admin_ip}:3100" }
minio : { domain: m.pigsty.cc ,endpoint: "${admin_ip}:9001" ,scheme: https ,websocket: true }
postgrest : { domain: api.pigsty.cc ,endpoint: "127.0.0.1:8884" }
pgadmin : { domain: adm.pigsty.cc ,endpoint: "127.0.0.1:8885" }
pgweb : { domain: cli.pigsty.cc ,endpoint: "127.0.0.1:8886" }
bytebase : { domain: ddl.pigsty.cc ,endpoint: "127.0.0.1:8887" }
jupyter : { domain: lab.pigsty.cc ,endpoint: "127.0.0.1:8888", websocket: true }
gitea : { domain: git.pigsty.cc ,endpoint: "127.0.0.1:8889" }
wiki : { domain: wiki.pigsty.cc ,endpoint: "127.0.0.1:9002" }
noco : { domain: noco.pigsty.cc ,endpoint: "127.0.0.1:9003" }
supa : { domain: supa.pigsty.cc ,endpoint: "10.10.10.10:8000" ,websocket: true }
dify : { domain: dify.pigsty.cc ,endpoint: "10.10.10.10:8001" ,websocket: true }
odoo : { domain: odoo.pigsty.cc ,endpoint: "127.0.0.1:8069" ,websocket: true }
mm : { domain: mm.pigsty.cc ,endpoint: "10.10.10.10:8065" ,websocket: true }
这个文档站的 Nginx 配置要比默认的配置复杂一些:
home
服务器使用了一个真实的公网域名home.pigsty.cc
grafana
服务器使用了一个真实的公网域名demo.pigsty.cc
,并配置了websocket: true
以支持 WebSocket 连接。cc
服务器是 Pigsty 的文档站,它使用了真实公网域名pigsty.cc
,并指向了本地的/www/pigsty.cc
目录。- 下面还定义了一系列 Docker App 服务器,将这些应用的 Web 界面通过域名对外暴露。
cc
与grafana
直接通过cert
与key
参数指定了 HTTPS 证书。
配置域名
您可以通过 IP:Port 直接访问特定服务,例如 IP:3000 访问 Grafana,IP:9090 访问 Prometheus。 但这样的行为通常并不可取,常规安全最佳实践要求您通过域名访问服务,而不是通过 IP:Port 直接访问。 通过域名访问意味着你只需要对外暴露一个 Nginx 服务,减小攻击面,并便于统一添加访问控制!
小知识:Nginx 是如何区分不同服务器的?
Nginx 通过浏览器设置的 HOST
首部中的域名,来区分不同的服务。
所以如果您有多种服务,尽管都使用同一个 IP 地址的 80/443 端口,但 Nginx 仍然可以区分它们。
但限制条件就是,您 必须 通过域名访问服务,而不是通过 IP:Port 直接访问。如果您直接用 IP 来访问 80/443 端口,那么您只能访问到默认的 home
服务器。
使用域名访问 Pigsty WebUI 时,您需要配置 DNS 解析,有以下几种方式:
- 使用真域名,通过云厂商/域名厂商的 DNS 解析服务,将公网域名指向你的服务器公网IP
- 使用内网域名,在内网 DNS 服务器上添加内网域名,并指向你服务器的内网IP地址
- 使用本机域名,在你浏览器所在主机的(
/etc/hosts
)添加一条静态解析记录
通过本机访问
如果你是唯一的用户,那么可以直接修改本地 /etc/hosts
文件(Linux/MacOS),
在 Windows 系统上,您需要修改 C:\Windows\System32\drivers\etc\hosts
文件。
无论是什么系统,修改此文件通常需要管理员权限。
你可以添加以下静态解析记录,将 Pigsty 的默认域名指向你的服务器IP地址:
<ip_address> h.pigsty a.pigsty p.pigsty g.pigsty
如果你会用到其他服务,也可以添加其他服务对应的域名解析记录:
10.10.10.10 h.pigsty a.pigsty p.pigsty g.pigsty
10.10.10.10 api.pigsty ddl.pigsty adm.pigsty cli.pigsty lab.pigsty
10.10.10.10 supa.pigsty noco.pigsty odoo.pigsty dify.pigsty
通过办公网访问
如果您的服务需要在办公网共享访问,例如让所有同事都可以通过域名访问,那么除了让同事都在自己的电脑上添加上面的静态解析记录之外,更正规的做法是使用内网DNS服务器。
您可以要求网络管理员在公司内部 DNS 服务器中添加相应的解析记录,将其指向 Nginx 服务器所在的 IP 地址。
当然还有另一种选项,Pigsty 默认安装也会在 53 端口提供一个 DNS 服务器,您可以通过配置 /etc/resolv.conf
文件或图形化界面配置 DNS 服务器,来使用内网域名访问部署在办公网中的服务。
通过互联网访问
如果您的服务需要直接暴露在互联网网上,通常您需要通过 DNS 服务商(Cloudflare,Aliyun DNS 等)解析互联网域名。 当然,你依然可以使用本地 DNS 服务器或本地静态解析记录来访问服务,但这样你的服务(除了默认的home服务器)就没法被互联网上的其他用户访问了。
您可以将所有需要暴露的域名都解析到 Nginx 服务器所在的 IP 地址。或者更简单的将 @
和 *
A 记录都解析到 Nginx 服务器所在的 IP 地址。
当你申请好新域名并将其指向你的服务器公网IP后,你还需要修改 Pigsty 的 infra_portal
,将域名填入各个服务器条目的 domain
字段中。
通过公网访问的域名,最佳实践是申请 HTTPS 证书,并始终使用 HTTPS 访问。我们将在下一节介绍这个主题。
配置HTTPS
HTTPS 是当代 Web 服务的主流配置,然而并非所有用户都熟悉 HTTPS 的配置方法。因此 Pigsty 默认为用户启用 HTTPS 支持。
如果你的 Nginx 只是对内网,办公网提供服务,那么 HTTPS 是一个 可选项; 如果你的 Nginx 需要对 互联网 提供服务,那么我们 强烈建议 您使用真实的域名与真正的 HTTPS 证书。 使用 HTTPS 不仅能够加密您的网络流量避免非法窥探篡改,而且能够避免访问 “未备案” 域名时恼人的体验。
本地域名与自签名证书
因为 Pigsty 默认使用的域名都是本地域名(x.pigsty
),无法申请真正的域名 HTTPS 证书,所以 Pigsty 默认使用自签名证书。
Pigsty 会使用自签名的 CA 为所有的 infra_portal
中的域名签发证书。当然此证书并非权威证书,在浏览器中会提示证书不可信。你可以选择:
- “我知道不安全,继续访问”
- 使用 Chrome 浏览器时,你也可以使用敲击键入
thisisunsafe
来绕过证书验证 - 将 Pigsty 自动生成的
pigsty-ca
CA 证书加入浏览器所在电脑的 信任的根 CA 列表。 - 回退到 HTTP 或者 IP:Port 访问,不使用 HTTPS (不推荐)
- 不使用本地域名与自签名证书,而是使用真正的域名与真正的 HTTPS 证书。
默认生成的自签名 CA 公钥和私钥位于 pigsty 本地目录的:files/pki/ca/ca.crt
和 files/pki/ca/ca.key
。
真实域名与真证书
HTTPS 证书通常是一项收费服务,但是您可以使用诸如 certbot
这类工具申请免费的 Let’s Encrypt 证书。
使用 Certbot 申请真正 HTTPS 证书的教程将在下一篇 Certbot教程:申请免费HTTPS证书 中详细介绍。
2 - Certbot:申请公网HTTPS证书
Pigsty 自带了 Certbot 工具,并默认于 Infra 节点上安装启用。
这意味着你可以直接通过 certbot
命令行工具,为你的 Nginx 服务器与公网域名申请真正的 Let’Encrypt 免费 HTTPS 证书,而不是使用 Pigsty 自签名的 HTTPS 证书。
为了做到这一点,你需要:
- 确定哪些域名需要证书
- 将这些域名指向您的服务器
- 使用 Certbot 申请证书
- 将Nginx配置文件纳入管理
- 配置更新证书的定时任务
- 申请证书的一些注意事项
以下是如何去做的详细说明:
确定哪些域名需要证书
首先,您需要决定哪些 “上游服务” 需要真正的公网证书
infra_portal:
home : { domain: h.pigsty.cc }
grafana : { domain: g.pigsty.cc ,endpoint: "${admin_ip}:3000" ,websocket: true }
prometheus : { domain: p.pigsty.cc ,endpoint: "${admin_ip}:9090" }
alertmanager : { domain: a.pigsty.cc ,endpoint: "${admin_ip}:9093" }
blackbox : { endpoint: "${admin_ip}:9115" }
loki : { endpoint: "${admin_ip}:3100" }
minio : { domain: m.pigsty.cc ,endpoint: "${admin_ip}:9001" ,scheme: https ,websocket: true }
web : { domain: pigsty.cc ,path: "/www/web.cc" }
repo : { domain: repo.pigsty.cc ,path: "/www/repo" }
例如在 infra_portal
中,假设我们要对外暴露以下五项服务:
- Grafana 可视化监控面板的
g.pigsty.cc
域名 - Prometheus 时序数据库的
p.pigsty.cc
域名 - AlertManager 告警面板的
a.pigsty.cc
域名 - Pigsty 文档站的
pigsty.cc
域名,指向本地文档目录 - Pigsty 软件仓库的
repo.pigsty.cc
域名,指向软件仓库
这里的例子里特意没有选择为 home
主页申请真的 Let’s Encrypt 证书,原因见最后一节。
将这些域名指向您的服务器
接下来,您需要将上面选定的域名指向您服务器的 公网IP地址。
例如,Pigsty CC 站点的 IP 地址是 47.83.172.23
,则可在域名注册商(如阿里云DNS控制台)上设置以下域名解析 A 记录:
47.83.172.23 pigsty.cc
47.83.172.23 g.pigsty.cc
47.83.172.23 p.pigsty.cc
47.83.172.23 a.pigsty.cc
47.83.172.23 repo.pigsty.cc
修改完之后,可以使用
使用 Certbot 申请证书
第一次申请的时候,certbot
会提示你输入邮箱,并是否同意协议,按提示输入即可。
$ certbot --nginx -d pigsty.cc -d repo.pigsty.cc -d g.pigsty.cc -d p.pigsty.cc -d a.pigsty.cc
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): rh@vonng.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.4-April-3-2024.pdf. You must agree in
order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N
Account registered.
Requesting a certificate for pigsty.cc and 4 more domains
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/pigsty.cc/fullchain.pem
Key is saved at: /etc/letsencrypt/live/pigsty.cc/privkey.pem
This certificate expires on 2025-05-18.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for pigsty.cc to /etc/nginx/conf.d/web.conf
Successfully deployed certificate for repo.pigsty.cc to /etc/nginx/conf.d/repo.conf
Successfully deployed certificate for g.pigsty.cc to /etc/nginx/conf.d/grafana.conf
Successfully deployed certificate for p.pigsty.cc to /etc/nginx/conf.d/prometheus.conf
Successfully deployed certificate for a.pigsty.cc to /etc/nginx/conf.d/alertmanager.conf
Congratulations! You have successfully enabled HTTPS on https://pigsty.cc, https://repo.pigsty.cc, https://g.pigsty.cc, https://p.pigsty.cc, and https://a.pigsty.cc
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
首次申请之后,以后申请就可以省略这些步骤,直接使用这些命令了。
更简单的办法是直接以非交互式的方法调用,直接使用以下命令,传入你的邮箱和要注册的域名。
certbot --nginx --agree-tos --email rh@vonng.com -n -d supa.pigsty.cc
将Nginx配置文件纳入管理
使用 certbot
签发证书后,默认会修改 Nginx 的配置文件,将 HTTP 服务器重定向到 HTTPS 服务器,而这可能并非你想要的。
你可以通过修改 Pigsty 配置文件中的 infra_portal
参数,将 Certbot 已经 成功签发证书的域名配置到 Nginx 的配置文件中。
infra_portal:
home : { domain: h.pigsty.cc }
grafana : { domain: g.pigsty.cc ,endpoint: "${admin_ip}:3000" ,websocket: true , certbot: pigsty.cc }
prometheus : { domain: p.pigsty.cc ,endpoint: "${admin_ip}:9090" , certbot: pigsty.cc }
alertmanager : { domain: a.pigsty.cc ,endpoint: "${admin_ip}:9093" , certbot: pigsty.cc }
blackbox : { endpoint: "${admin_ip}:9115" }
loki : { endpoint: "${admin_ip}:3100" }
minio : { domain: m.pigsty.cc ,endpoint: "${admin_ip}:9001" ,scheme: https ,websocket: true }
web : { domain: pigsty.cc ,path: "/www/web.cc" , certbot: pigsty.cc }
repo : { domain: repo.pigsty.cc ,path: "/www/repo" , certbot: pigsty.cc }
这里,修改签发证书的服务器定义项,添加 certbot: <domain-name>
,这里的 <domain-name>
指的是 certbot 签发的文件名。
通常与 domain
一样,但如果你同时申请多个域名证书,certbot 会将其合并为一个证书,比如这里合并为两个文件:
Certificate is saved at: /etc/letsencrypt/live/pigsty.cc/fullchain.pem
Key is saved at: /etc/letsencrypt/live/pigsty.cc/privkey.pem
因此将证书中间的 pigsty.cc
抽出来填入 certbot
,然后重新运行:
./infra.yml -t nginx_config,nginx_launch
即可让 Pigsty 重新生成 Nginx 配置文件,回退 Certbot 对配置进行的其他修改,只保留申请的证书。
以后需要续期更新证书的时候就不需要重复这个过程了,直接使用 certbot renew
即可。
配置更新证书的定时任务
默认情况下,申请的证书有效期为三个月,所以如果在证书有效期到期之前,你应该使用 certbot renew
对证书进行续期。
如果你需要更新证书,执行以下命令即可。
certbot renew
在真正执行之前,你可以使用 DryRun 模式来测试续期是否正常:
certbot renew --dry-run
如果你修改过 Nginx 配置文件,请务必确保 certbot 的修改不会影响你的配置文件。
你可以将这个命令配置为 crontab ,在每个月的第一天凌晨执行续期并打印日志。
3 - Docker:启用容器与镜像代理
Pigsty 提供了 DOCKER
模块,但默认并不安装。
您可以使用 docker.yml
剧本在指定节点上安装并启用 Docker。
./docker.yml -l <ip|group|cls> # 在指定的节点、分组、集群上安装并启用 Docker
如何建配置代理服务器?
本文不会介绍如何“翻墙”,而是假设你已经有了一个可用的 HTTP(s) 代理服务器,应该如何配置,让 Docker 可以通过代理服务器,访问 docker hub 或 quay.io 等镜像站点:
你的代理服务器软件应该会提供一个形如:
http://<ip|domain>:<port>
或者https://[user]:[pass]@<ip|domain>:<port>
的代理地址
例如,假设您使用的代理服务器在 127.0.0.1:12345
上提供服务,那么你可以通过以下环境变量来使用它:
export ALL_PROXY=http://192.168.0.106:8118
export HTTP_PROXY=http://192.168.0.106:8118
export HTTPS_PROXY=http://192.168.0.106:8118
export NO_PROXY="localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.myqcloud.com,*.tsinghua.edu.cn"
您可以使用 curl
命令,检验代理服务器是否可以正常工作,例如成功可以访问 Google,通常说明代理服务器工作正常。
curl -x http://192.168.0.106:8118 -I http://www.google.com
如何为Docker Daemon配置代理服务器?
如果您希望 Docker 在 Pull 镜像时使用代理服务器,那么应当在 pigsty.yml
配置文件的全局变量中,指定 proxy_env
参数:
all:
vars:
proxy_env: # global proxy env when downloading packages
no_proxy: "localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.myqcloud.com,*.tsinghua.edu.cn"
http_proxy: http://192.168.0.106:8118
all_proxy: http://192.168.0.106:8118
https_proxy: http://192.168.0.106:8118
那么当 Docker 剧本执行,时,这些配置会被渲染为 /etc/docker/daemon.json
中的代理配置:
{
"proxies": {
"http-proxy": "{{ proxy_env['http_proxy'] }}",
"https-proxy": "{{ proxy_env['http_proxy'] }}",
"no-proxy": "{{ proxy_env['no_proxy'] }}"
}
}
请注意,Docker Daemon 不使用
all_proxy
参数
如果您希望手工指定代理服务器,可以选则直接修改 /etc/docker/daemon.json
中的 proxies
配置;
或者也可以修改 /lib/systemd/system/docker.service
(Debian/Ubuntu) 与 /usr/lib/systemd/system/docker.service
的服务定义,在 [Service]
一节中添加环境变量声明,并重启生效:
[Service]
Environment="HTTP_PROXY=http://192.168.0.106:8118"
Environment="HTTPS_PROXY=http://192.168.0.106:8118"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,192.168.0.0/16,*.pigsty,*.aliyun.com,mirrors.*,*.myqcloud.com,*.tsinghua.edu.cn"
重启后生效:
systemctl restart docker
如何使用其他镜像站点?
您可以在 docker_registry_mirrors
参数中指定其他镜像站点,例如阿里云、腾讯云、清华大学等镜像站点:
[ "https://mirror.ccs.tencentyun.com" ] # tencent cloud mirror, intranet only
["https://registry.cn-hangzhou.aliyuncs.com"] # aliyun cloud mirror, login required
不过目前来看,所有位于中国大陆的 DockerHub 公有镜像站都已经被封禁了,建议使用代理服务器直接访问 Docker Hub
如果您需要使用其他镜像站,例如 quay.io
,可以首先执行:
docker login quay.io
username #> # input your username
password #> # input your password
4 - 使用 PostgreSQL 作为 Ansible 的配置清单与 CMDB
您可以使用 PostgreSQL 作为 Pigsty 的配置源,替代静态 YAML 配置文件。
使用 CMDB 作为 Ansible 的动态 配置清单 具有一些优点:元数据以高度结构化的方式以数据表的形式呈现,并通过数据库约束确保一致性。 同时,使用 CMDB 允许您使用第三方的工具来编辑管理 Pigsty 元数据,便于与外部系统相互集成。
Ansible配置原理
Pigsty 默认的配置文件路径在 ansible.cfg
中指定为:inventory = pigsty.yml
修改该参数,可以更改默认使用的配置文件路径。如果您将其指向一个可执行的脚本文件,那么 Ansible 会使用动态 Inventory 机制,执行该脚本,并期待该脚本返回一份配置文件。
修改配置源实质上是编辑Pigsty目录下的 ansible.cfg
实现的:
---
inventory = pigsty.yml
+++
inventory = inventory.sh
而 inventory.sh
则是一个从 PostgreSQL CMDB 的记录中,生成等效 YAML/JSON 配置文件的简单脚本。
你可以使用 bin/inventory_cmdb
切换到动态的 CMDB 清单,
使用 bin/inventory_conf
返回到本地配置文件。
你还需要使用 bin/inventory_load
将当前的配置文件清单加载到 CMDB。
加载配置
Pigsty CMDB的模式会在pg-meta
元数据库初始化时自动创建(files/cmdb.sql
),位于meta
数据库的pigsty
模式中。使用bin/inventory_load
可以将静态配置文件加载至CMDB中。
必须在元节点完整执行
infra.yml
安装完毕后,方可使用 CMDB
usage: inventory_load [-h] [-p PATH] [-d CMDB_URL]
load config arguments
optional arguments:
-h, --help show this help message and exit„
-p PATH, --path PATH config path, ${PIGSTY_HOME}/pigsty.yml by default
-d DATA, --data DATA postgres cmdb pgurl, ${METADB_URL} by default
默认情况下,不带参数执行该脚本将会把$PIGSTY_HOME/pigsty.yml
的名称载入默认CMDB中。
bin/inventory_load
bin/inventory_load -p conf/demo.yml
bin/inventory_load -p conf/prod.yml -d postgresql://dbuser_meta:DBUser.Meta@10.10.10.10:5432/meta
当原有配置文件加载至 CMDB 作为初始数据后,即可配置 Ansible 使用 CMDB 作为配置源:
bin/inventory_cmdb
您可以切换回静态配置文件:
bin/inventory_conf
5 - 使用 PostgreSQL 作为 Grafana 后端数据库
您可以使用 PostgreSQL 作为 Grafana 后端使用的数据库。
这是了解Pigsty部署系统使用方式的好机会,完成此教程,您会了解:
- 如何创建新数据库集群
- 如何在已有数据库集群中创建新业务用户
- 如何在已有数据库集群中创建新业务数据库
- 如何访问Pigsty所创建的数据库
- 如何管理Grafana中的监控面板
- 如何管理Grafana中的PostgreSQL数据源
- 如何一步到位完成Grafana数据库升级
太长不看
vi pigsty.yml # 取消注释DB/User定义:dbuser_grafana grafana
bin/pgsql-user pg-meta dbuser_grafana
bin/pgsql-db pg-meta grafana
psql postgres://dbuser_grafana:DBUser.Grafana@meta:5436/grafana -c \
'CREATE TABLE t(); DROP TABLE t;' # 检查连接串可用性
vi /etc/grafana/grafana.ini # 修改 [database] type url
systemctl restart grafana-server
创建数据库集群
我们可以在pg-meta
上定义一个新的数据库grafana
,也可以在新的机器节点上创建一个专用于Grafana的数据库集群:pg-grafana
定义集群
如果需要创建新的专用数据库集群pg-grafana
,部署在10.10.10.11
,10.10.10.12
两台机器上,可以使用以下配置文件:
pg-grafana:
hosts:
10.10.10.11: {pg_seq: 1, pg_role: primary}
10.10.10.12: {pg_seq: 2, pg_role: replica}
vars:
pg_cluster: pg-grafana
pg_databases:
- name: grafana
owner: dbuser_grafana
revokeconn: true
comment: grafana primary database
pg_users:
- name: dbuser_grafana
password: DBUser.Grafana
pgbouncer: true
roles: [dbrole_admin]
comment: admin user for grafana database
创建集群
使用以下命令完成数据库集群pg-grafana
的创建:pgsql.yml
。
bin/createpg pg-grafana # 初始化pg-grafana集群
该命令实际上调用了Ansible Playbook pgsql.yml
创建数据库集群。
./pgsql.yml -l pg-grafana # 实际执行的等效Ansible剧本命令
定义在 pg_users
与 pg_databases
中的业务用户与业务数据库会在集群初始化时自动创建,因此使用该配置时,集群创建完毕后,(在没有DNS支持的情况下)您可以使用以下连接串访问数据库(任一即可):
postgres://dbuser_grafana:DBUser.Grafana@10.10.10.11:5432/grafana # 主库直连
postgres://dbuser_grafana:DBUser.Grafana@10.10.10.11:5436/grafana # 直连default服务
postgres://dbuser_grafana:DBUser.Grafana@10.10.10.11:5433/grafana # 连接串读写服务
postgres://dbuser_grafana:DBUser.Grafana@10.10.10.12:5432/grafana # 主库直连
postgres://dbuser_grafana:DBUser.Grafana@10.10.10.12:5436/grafana # 直连default服务
postgres://dbuser_grafana:DBUser.Grafana@10.10.10.12:5433/grafana # 连接串读写服务
因为默认情况下Pigsty安装在单个元节点上,接下来的步骤我们会在已有的pg-meta
数据库集群上创建Grafana所需的用户与数据库,而并非使用这里创建的pg-grafana
集群。
创建Grafana业务用户
通常业务对象管理的惯例是:先创建用户,再创建数据库。
因为如果为数据库配置了owner
,数据库对相应的用户存在依赖。
定义用户
要在pg-meta
集群上创建用户dbuser_grafana
,首先将以下用户定义添加至pg-meta
的集群定义中:
添加位置:all.children.pg-meta.vars.pg_users
- name: dbuser_grafana
password: DBUser.Grafana
comment: admin user for grafana database
pgbouncer: true
roles: [ dbrole_admin ]
如果您在这里定义了不同的密码,请在后续步骤中将相应参数替换为新密码
创建用户
使用以下命令完成dbuser_grafana
用户的创建(任一均可)。
bin/pgsql-user pg-meta dbuser_grafana # 在pg-meta集群上创建`dbuser_grafana`用户
实际上调用了Ansible Playbook pgsql-createuser.yml
创建用户
./pgsql-user.yml -l pg-meta -e pg_user=dbuser_grafana # Ansible
dbrole_admin
角色具有在数据库中执行DDL变更的权限,这正是Grafana所需要的。
创建Grafana业务数据库
定义数据库
创建业务数据库的方式与业务用户一致,首先在pg-meta
的集群定义中添加新数据库grafana
的定义。
添加位置:all.children.pg-meta.vars.pg_databases
- { name: grafana, owner: dbuser_grafana, revokeconn: true }
创建数据库
使用以下命令完成grafana
数据库的创建(任一均可)。
bin/pgsql-db pg-meta grafana # 在`pg-meta`集群上创建`grafana`数据库
实际上调用了Ansible Playbook pgsql-createdb.yml
创建数据库
./pgsql-db.yml -l pg-meta -e pg_database=grafana # 实际执行的Ansible剧本
使用Grafana业务数据库
检查连接串可达性
postgres://dbuser_grafana:DBUser.Grafana@meta:5432/grafana # 直连
postgres://dbuser_grafana:DBUser.Grafana@meta:5436/grafana # default服务
postgres://dbuser_grafana:DBUser.Grafana@meta:5433/grafana # primary服务
这里,我们将使用通过负载均衡器直接访问主库的 Default服务访问数据库。
首先检查连接串是否可达,以及是否有权限执行DDL命令。
psql postgres://dbuser_grafana:DBUser.Grafana@meta:5436/grafana -c \
'CREATE TABLE t(); DROP TABLE t;'
直接修改Grafana配置
为了让Grafana使用 Postgres 数据源,您需要编辑 /etc/grafana/grafana.ini
,并修改配置项:
[database]
;type = sqlite3
;host = 127.0.0.1:3306
;name = grafana
;user = root
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
;password =
;url =
将默认的配置项修改为:
[database]
type = postgres
url = postgres://dbuser_grafana:DBUser.Grafana@meta/grafana
随后重启Grafana即可:
systemctl restart grafana-server
从监控系统中看到新增的 grafana
数据库已经开始有活动,则说明Grafana已经开始使用Postgres作为首要后端数据库了。
但一个新的问题是,Grafana中原有的Dashboards与Datasources都消失了!这里需要重新导入监控面板与Postgres数据源
管理Grafana监控面板
您可以使用管理用户前往 Pigsty 目录下的files/ui
目录,执行grafana.py init
重新加载Pigsty监控面板。
cd ~/pigsty/files/ui
./grafana.py init # 使用当前目录下的Dashboards初始化Grafana监控面板
执行结果:
vagrant@meta:~/pigsty/files/ui
$ ./grafana.py init
Grafana API: admin:pigsty @ http://10.10.10.10:3000
init dashboard : home.json
init folder pgcat
init dashboard: pgcat / pgcat-table.json
init dashboard: pgcat / pgcat-bloat.json
init dashboard: pgcat / pgcat-query.json
init folder pgsql
init dashboard: pgsql / pgsql-replication.json
init dashboard: pgsql / pgsql-table.json
init dashboard: pgsql / pgsql-activity.json
init dashboard: pgsql / pgsql-cluster.json
init dashboard: pgsql / pgsql-node.json
init dashboard: pgsql / pgsql-database.json
init dashboard: pgsql / pgsql-xacts.json
init dashboard: pgsql / pgsql-overview.json
init dashboard: pgsql / pgsql-session.json
init dashboard: pgsql / pgsql-tables.json
init dashboard: pgsql / pgsql-instance.json
init dashboard: pgsql / pgsql-queries.json
init dashboard: pgsql / pgsql-alert.json
init dashboard: pgsql / pgsql-service.json
init dashboard: pgsql / pgsql-persist.json
init dashboard: pgsql / pgsql-proxy.json
init dashboard: pgsql / pgsql-query.json
init folder pglog
init dashboard: pglog / pglog-instance.json
init dashboard: pglog / pglog-analysis.json
init dashboard: pglog / pglog-session.json
该脚本会侦测当前的环境(安装时定义于~/pigsty
),获取Grafana的访问信息,并将监控面板中的URL连接占位符域名(*.pigsty
)替换为真实使用的域名。
export GRAFANA_ENDPOINT=http://10.10.10.10:3000
export GRAFANA_USERNAME=admin
export GRAFANA_PASSWORD=pigsty
export NGINX_UPSTREAM_YUMREPO=yum.pigsty
export NGINX_UPSTREAM_CONSUL=c.pigsty
export NGINX_UPSTREAM_PROMETHEUS=p.pigsty
export NGINX_UPSTREAM_ALERTMANAGER=a.pigsty
export NGINX_UPSTREAM_GRAFANA=g.pigsty
export NGINX_UPSTREAM_HAPROXY=h.pigsty
题外话,使用grafana.py clean
会清空目标监控面板,使用grafana.py load
会加载当前目录下所有监控面板,当Pigsty的监控面板发生变更,可以使用这两个命令升级所有的监控面板。
管理Postgres数据源
当使用 pgsql.yml
创建新PostgreSQL集群,或使用pgsql-createdb.yml
创建新业务数据库时,Pigsty会在Grafana中注册新的PostgreSQL数据源,您可以使用默认的监控用户通过Grafana直接访问目标数据库实例。应用pgcat
的绝大部分功能有赖于此。
要注册Postgres数据库,可以使用pgsql.yml
中的register_grafana
任务:
./pgsql.yml -t register_grafana # 重新注册当前环境中所有Postgres数据源
./pgsql.yml -t register_grafana -l pg-test # 重新注册 pg-test 集群中所有的数据库
一步到位更新Grafana
您可以直接通过修改Pigsty配置文件,更改Grafana使用的后端数据源,一步到位的完成切换Grafana后端数据库的工作。编辑pigsty.yml
中grafana_database
与grafana_pgurl
参数,将其修改为:
grafana_database: postgres
grafana_pgurl: postgres://dbuser_grafana:DBUser.Grafana@meta:5436/grafana
然后重新执行 infral.yml
中的grafana
任务,即可完成 Grafana升级
./infra.yml -t grafana
6 - 使用 TimescaleDB + Promscale 存储 Prometheus 时序指标数据
虽然这并不是推荐的行为,但这是了解Pigsty部署系统使用方式的好机会。
注意,使用 Promscale 存储 Prometheus 指标占用的存储空间大约是 Prometheus 的 4 倍,但是可以使用 SQL 来查询分析 Prometheus 监控指标。
准备Postgres数据库
vi pigsty.yml # 取消注释DB/User定义:dbuser_prometheus prometheus
pg_databases: # define business users/roles on this cluster, array of user definition
- { name: prometheus, owner: dbuser_prometheus , revokeconn: true, comment: prometheus primary database }
pg_users: # define business users/roles on this cluster, array of user definition
- {name: dbuser_prometheus , password: DBUser.Prometheus ,pgbouncer: true , createrole: true, roles: [dbrole_admin], comment: admin user for prometheus database }
创建 Prometheus 业务数据库与业务用户。
bin/createuser pg-meta dbuser_prometheus
bin/createdb pg-meta prometheus
检查数据库可用性并创建扩展
psql postgres://dbuser_prometheus:DBUser.Prometheus@10.10.10.10:5432/prometheus -c 'CREATE EXTENSION timescaledb;'
配置Promscale
在元节点上执行以下命令安装 promscale
yum install -y promscale
如果默认软件包中没有,可以直接下载:
wget https://github.com/timescale/promscale/releases/download/0.6.1/promscale_0.6.1_Linux_x86_64.rpm
sudo rpm -ivh promscale_0.6.1_Linux_x86_64.rpm
编辑 promscale
的配置文件 /etc/sysconfig/promscale.conf
PROMSCALE_DB_HOST="127.0.0.1"
PROMSCALE_DB_NAME="prometheus"
PROMSCALE_DB_PASSWORD="DBUser.Prometheus"
PROMSCALE_DB_PORT="5432"
PROMSCALE_DB_SSL_MODE="disable"
PROMSCALE_DB_USER="dbuser_prometheus"
最后启动promscale,它会访问安装有 timescaledb
的数据库实例,并创建所需的schema
# launch
cat /usr/lib/systemd/system/promscale.service
systemctl start promscale && systemctl status promscale
配置Prometheus
Prometheus可以使用Remote Write/ Remote Read的方式,通过Promscale,使用Postgres作为远程存储。
编辑Prometheus配置文件:
vi /etc/prometheus/prometheus.yml
添加以下记录:
remote_write:
- url: "http://127.0.0.1:9201/write"
remote_read:
- url: "http://127.0.0.1:9201/read"
重启Prometheus后,监控数据即可放入Postgres中。
systemctl restart prometheus
7 - 使用 Keepalived 为 Pigsty 节点集群配置二层 VIP
您可以在节点集群上绑定一个可选的 L2 VIP —— 前提条件是:集群中的所有节点都在一个二层网络中。
在节点集群(任何一个 Ansible Group,包括数据库集群定义都可以视作一个节点集群)上,启用 vip_enabled
参数,即可在节点集群上启用 Keepalived ,绑定一个2层 VIP。
proxy:
hosts:
10.10.10.29: { nodename: proxy-1 } # 您可以显式指定初始的 VIP 角色:MASTER / BACKUP
10.10.10.30: { nodename: proxy-2 } # , vip_role: master }
vars:
node_cluster: proxy
vip_enabled: true
vip_vrid: 128
vip_address: 10.10.10.99
vip_interface: eth1
使用以下命令,刷新节点的 Keepalived 配置,并生效:
./node.yml -l proxy -t node_vip # 首次启用 VIP
./node.yml -l proxy -t vip_refresh # 刷新 vip 配置(例如指定 master)
专用的场景
针对 PostgreSQL 高可用场景, Pigsty 提供了基于 vip-manager 的 L2 VIP 解决方案。
vip-manager 是一个独立的组件,它读取 etcd 中的 PostgreSQL 集群领导者,并在领导者所在节点上绑定一个 L2 VIP。
因此我们建议您使用 vip-manager 来实现 PostgreSQL 的高可用性,而不是使用 Keepalived 来实现。请参考 PGSQL VIP 了解更多。
不适用的场景
在诸如 AWS,阿里云这样的云环境中,通常不支持使用 L2 VIP。
在这种情况下,我们建议您使用四层负载均衡器来实现类似的功能。
例如 Pigsty 提供了 HAProxy 的配置支持。
8 - 使用 VIP-Manager 为 PostgreSQL 集群配置二层 VIP
您可以在 PostgreSQL 集群上绑定一个可选的 L2 VIP —— 前提条件是:集群中的所有节点都在一个二层网络中。
这个 L2 VIP 强制使用 Master - Backup 模式,Master 始终指向在数据库集群主库实例所在的节点。
这个 VIP 由 VIP-Manager 组件管理,它会从 DCS (etcd) 中直接读取由 Patroni 写入的 Leader Key,从而判断自己是否是 Master。
启用VIP
在 PostgreSQL 集群上定义 pg_vip_enabled
参数为 true
,即可在集群上启用 VIP 组件。当然您也可以在全局配置中启用此配置项。
# pgsql 3 node ha cluster: pg-test
pg-test:
hosts:
10.10.10.11: { pg_seq: 1, pg_role: primary } # primary instance, leader of cluster
10.10.10.12: { pg_seq: 2, pg_role: replica } # replica instance, follower of leader
10.10.10.13: { pg_seq: 3, pg_role: replica, pg_offline_query: true } # replica with offline access
vars:
pg_cluster: pg-test # define pgsql cluster name
pg_users: [{ name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] }]
pg_databases: [{ name: test }]
# 启用 L2 VIP
pg_vip_enabled: true
pg_vip_address: 10.10.10.3/24
pg_vip_interface: eth1
请注意,pg_vip_address
必须是一个合法的 IP 地址,带有网段,且在当前二层网络中可用。
请注意,pg_vip_interface
必须是一个合法的网络接口名,并且应当是与 inventory 中使用 IPv4 地址一致的网卡。
如果集群成员的网卡名不一样,用户应当为每个实例显式指定 pg_vip_interface
参数,例如:
pg-test:
hosts:
10.10.10.11: { pg_seq: 1, pg_role: primary , pg_vip_interface: eth0 }
10.10.10.12: { pg_seq: 2, pg_role: replica , pg_vip_interface: eth1 }
10.10.10.13: { pg_seq: 3, pg_role: replica , pg_vip_interface: ens33 }
vars:
pg_cluster: pg-test # define pgsql cluster name
pg_users: [{ name: test , password: test , pgbouncer: true , roles: [ dbrole_admin ] }]
pg_databases: [{ name: test }]
# 启用 L2 VIP
pg_vip_enabled: true
pg_vip_address: 10.10.10.3/24
#pg_vip_interface: eth1
使用以下命令,刷新 PG 的 vip-manager 配置并重启生效:
./pgsql.yml -t pg_vip
9 - Citus:部署原生高可用集群
Citus 是一个 PostgreSQL 扩展,可以将 PostgreSQL 原地转换为一个分布式数据库,并实现在多个节点上水平扩展,以处理大量数据和大量查询。
Patroni 在 v3.0 后,提供了对 Citus 原生高可用的支持,简化了 Citus 集群的搭建,Pigsty 也对此提供了原生支持。
Citus集群
Pigsty 原生支持 Citus。可以参考 conf/citus.yml
,以及更复杂的 十节点集群。
这里使用 Pigsty 四节点沙箱,定义了一个 Citus 集群 pg-citus
,其中包括一个两节点的协调者(Coordinator)集群 pg-citus0
,以及两个工作者(Worker)集群 pg-citus1
,pg-citus2
。
pg-citus:
hosts:
10.10.10.10: { pg_group: 0, pg_cluster: pg-citus0 ,pg_vip_address: 10.10.10.2/24 ,pg_seq: 1, pg_role: primary }
10.10.10.11: { pg_group: 0, pg_cluster: pg-citus0 ,pg_vip_address: 10.10.10.2/24 ,pg_seq: 2, pg_role: replica }
10.10.10.12: { pg_group: 1, pg_cluster: pg-citus1 ,pg_vip_address: 10.10.10.3/24 ,pg_seq: 1, pg_role: primary }
10.10.10.13: { pg_group: 2, pg_cluster: pg-citus2 ,pg_vip_address: 10.10.10.4/24 ,pg_seq: 1, pg_role: primary }
vars:
pg_mode: citus # pgsql cluster mode: citus
pg_version: 16 # citus does not have pg16 available
pg_shard: pg-citus # citus shard name: pg-citus
pg_primary_db: citus # primary database used by citus
pg_vip_enabled: true # enable vip for citus cluster
pg_vip_interface: eth1 # vip interface for all members
pg_dbsu_password: DBUser.Postgres # all dbsu password access for citus cluster
pg_extensions: [ citus, postgis, pgvector, topn, pg_cron, hll ] # install these extensions
pg_libs: 'citus, pg_cron, pg_stat_statements' # citus will be added by patroni automatically
pg_users: [{ name: dbuser_citus ,password: DBUser.Citus ,pgbouncer: true ,roles: [ dbrole_admin ] }]
pg_databases: [{ name: citus ,owner: dbuser_citus ,extensions: [ citus, vector, topn, pg_cron, hll ] }]
pg_parameters:
cron.database_name: citus
citus.node_conninfo: 'sslmode=require sslrootcert=/pg/cert/ca.crt sslmode=verify-full'
pg_hba_rules:
- { user: 'all' ,db: all ,addr: 127.0.0.1/32 ,auth: ssl ,title: 'all user ssl access from localhost' }
- { user: 'all' ,db: all ,addr: intra ,auth: ssl ,title: 'all user ssl access from intranet' }
相比标准 PostgreSQL 集群,Citus 集群的配置有一些特殊之处,如下所示:
首先,你需要确保 Citus 扩展被下载,安装,加载并启用,这涉及到以下四个参数
repo_packages
:必须包含citus
扩展,或者你需要使用带有 Citus 扩展的 PostgreSQL 离线安装包。pg_extensions
:必须包含citus
扩展,即你必须在每个节点上安装citus
扩展。pg_libs
:必须包含citus
扩展,而且首位必须为citus
,但现在 Patroni 会自动完成这件事了。pg_databases
: 这里要定义一个首要数据库,该数据库必须安装citus
扩展。
其次,你需要确保 Citus 集群的配置正确:
pg_mode
: 必须设置为citus
,从而告知 Patroni 使用 Citus 模式。pg_primary_db
:必须指定一个首要数据库的名称,该数据库必须安装citus
扩展,这里名为citus
。pg_shard
:必须指定一个统一的名称,字符串,作为所有水平分片PG集群的集群名称前缀,这里为pg-citus
。pg_group
:必须指定一个分片号,从零开始依次分配的整数,0
号固定代表协调者集群,其他为 Worker 集群。pg_cluster
必须与pg_shard
和pg_group
组合后的结果对应。pg_dbsu_password
:必须设置为非空的纯文本密码,否则 Citus 无法正常工作。pg_parameters
:建议设置citus.node_conninfo
参数,强制要求 SSL 访问并要求节点间验证客户端证书。
配置完成后,您可以像创建普通 PostgreSQL 集群一样,使用 pgsql.yml
剧本部署 Citus 集群。
管理Citus集群
定义好 Citus 集群后,部署 Citus 集群同样使用的剧本 pgsql.yml
:
./pgsql.yml -l pg-citus # 部署 Citus 集群 pg-citus
使用任意成员的 DBSU(postgres
)用户,都能通过 patronictl
(alias: pg
) 列出 Citus 集群的状态:
$ pg list
+ Citus cluster: pg-citus ----------+---------+-----------+----+-----------+--------------------+
| Group | Member | Host | Role | State | TL | Lag in MB | Tags |
+-------+-------------+-------------+---------+-----------+----+-----------+--------------------+
| 0 | pg-citus0-1 | 10.10.10.10 | Leader | running | 1 | | clonefrom: true |
| | | | | | | | conf: tiny.yml |
| | | | | | | | spec: 20C.40G.125G |
| | | | | | | | version: '16' |
+-------+-------------+-------------+---------+-----------+----+-----------+--------------------+
| 1 | pg-citus1-1 | 10.10.10.11 | Leader | running | 1 | | clonefrom: true |
| | | | | | | | conf: tiny.yml |
| | | | | | | | spec: 10C.20G.125G |
| | | | | | | | version: '16' |
+-------+-------------+-------------+---------+-----------+----+-----------+--------------------+
| 2 | pg-citus2-1 | 10.10.10.12 | Leader | running | 1 | | clonefrom: true |
| | | | | | | | conf: tiny.yml |
| | | | | | | | spec: 10C.20G.125G |
| | | | | | | | version: '16' |
+-------+-------------+-------------+---------+-----------+----+-----------+--------------------+
| 2 | pg-citus2-2 | 10.10.10.13 | Replica | streaming | 1 | 0 | clonefrom: true |
| | | | | | | | conf: tiny.yml |
| | | | | | | | spec: 10C.20G.125G |
| | | | | | | | version: '16' |
+-------+-------------+-------------+---------+-----------+----+-----------+--------------------+
您可以将每个水平分片集群视为一个独立的 PGSQL 集群,使用 pg
(patronictl
) 命令管理它们。
但是务必注意,当你使用 pg
命令管理 Citus 集群时,需要额外使用 --group
参数指定集群分片号
pg list pg-citus --group 0 # 需要使用 --group 0 指定集群分片号
Citus 中有一个名为 pg_dist_node
的系统表,用于记录 Citus 集群的节点信息,Patroni 会自动维护该表。
PGURL=postgres://postgres:DBUser.Postgres@10.10.10.10/citus
psql $PGURL -c 'SELECT * FROM pg_dist_node;' # 查看节点信息
nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards
--------+---------+-------------+----------+----------+-------------+----------+-----------+-------------+----------------+------------------
1 | 0 | 10.10.10.10 | 5432 | default | t | t | primary | default | t | f
4 | 1 | 10.10.10.12 | 5432 | default | t | t | primary | default | t | t
5 | 2 | 10.10.10.13 | 5432 | default | t | t | primary | default | t | t
6 | 0 | 10.10.10.11 | 5432 | default | t | t | secondary | default | t | f
此外,你还可以查看用户认证信息(仅限超级用户访问):
$ psql $PGURL -c 'SELECT * FROM pg_dist_authinfo;' # 查看节点认证信息(仅限超级用户访问)
然后,你可以使用普通业务用户(例如,具有 DDL 权限的 dbuser_citus
)来访问 Citus 集群:
psql postgres://dbuser_citus:DBUser.Citus@10.10.10.10/citus -c 'SELECT * FROM pg_dist_node;'
使用Citus集群
在使用 Citus 集群时,我们强烈建议您先阅读 Citus 官方文档,了解其架构设计与核心概念。
其中核心是了解 Citus 中的五种表,以及其特点与应用场景:
- 分布式表(Distributed Table)
- 参考表(Reference Table)
- 本地表(Local Table)
- 本地管理表(Local Management Table)
- 架构表(Schema Table)
在协调者节点上,您可以创建分布式表和引用表,并从任何数据节点查询它们。从 11.2 开始,任何 Citus 数据库节点都可以扮演协调者的角色了。
我们可以使用 pgbench 来创建一些表,并将其中的主表(pgbench_accounts
)分布到各个节点上,然后将其他小表作为引用表:
PGURL=postgres://dbuser_citus:DBUser.Citus@10.10.10.10/citus
pgbench -i $PGURL
psql $PGURL <<-EOF
SELECT create_distributed_table('pgbench_accounts', 'aid'); SELECT truncate_local_data_after_distributing_table('public.pgbench_accounts');
SELECT create_reference_table('pgbench_branches') ; SELECT truncate_local_data_after_distributing_table('public.pgbench_branches');
SELECT create_reference_table('pgbench_history') ; SELECT truncate_local_data_after_distributing_table('public.pgbench_history');
SELECT create_reference_table('pgbench_tellers') ; SELECT truncate_local_data_after_distributing_table('public.pgbench_tellers');
EOF
执行读写测试:
pgbench -nv -P1 -c10 -T500 postgres://dbuser_citus:DBUser.Citus@10.10.10.10/citus # 直连协调者 5432 端口
pgbench -nv -P1 -c10 -T500 postgres://dbuser_citus:DBUser.Citus@10.10.10.10:6432/citus # 通过连接池,减少客户端连接数压力,可以有效提高整体吞吐。
pgbench -nv -P1 -c10 -T500 postgres://dbuser_citus:DBUser.Citus@10.10.10.13/citus # 任意 primary 节点都可以作为 coordinator
pgbench --select-only -nv -P1 -c10 -T500 postgres://dbuser_citus:DBUser.Citus@10.10.10.11/citus # 可以发起只读查询
更严肃的生产部署
要将 Citus 用于生产环境,您通常需要为 Coordinator 和每个 Worker 集群设置流复制物理副本。
例如,在 simu.yml
中定义了一个 10 节点的 Citus 集群。
pg-citus: # citus group
hosts:
10.10.10.50: { pg_group: 0, pg_cluster: pg-citus0 ,pg_vip_address: 10.10.10.60/24 ,pg_seq: 0, pg_role: primary }
10.10.10.51: { pg_group: 0, pg_cluster: pg-citus0 ,pg_vip_address: 10.10.10.60/24 ,pg_seq: 1, pg_role: replica }
10.10.10.52: { pg_group: 1, pg_cluster: pg-citus1 ,pg_vip_address: 10.10.10.61/24 ,pg_seq: 0, pg_role: primary }
10.10.10.53: { pg_group: 1, pg_cluster: pg-citus1 ,pg_vip_address: 10.10.10.61/24 ,pg_seq: 1, pg_role: replica }
10.10.10.54: { pg_group: 2, pg_cluster: pg-citus2 ,pg_vip_address: 10.10.10.62/24 ,pg_seq: 0, pg_role: primary }
10.10.10.55: { pg_group: 2, pg_cluster: pg-citus2 ,pg_vip_address: 10.10.10.62/24 ,pg_seq: 1, pg_role: replica }
10.10.10.56: { pg_group: 3, pg_cluster: pg-citus3 ,pg_vip_address: 10.10.10.63/24 ,pg_seq: 0, pg_role: primary }
10.10.10.57: { pg_group: 3, pg_cluster: pg-citus3 ,pg_vip_address: 10.10.10.63/24 ,pg_seq: 1, pg_role: replica }
10.10.10.58: { pg_group: 4, pg_cluster: pg-citus4 ,pg_vip_address: 10.10.10.64/24 ,pg_seq: 0, pg_role: primary }
10.10.10.59: { pg_group: 4, pg_cluster: pg-citus4 ,pg_vip_address: 10.10.10.64/24 ,pg_seq: 1, pg_role: replica }
vars:
pg_mode: citus # pgsql cluster mode: citus
pg_version: 16 # citus does not have pg16 available
pg_shard: pg-citus # citus shard name: pg-citus
pg_primary_db: citus # primary database used by citus
pg_vip_enabled: true # enable vip for citus cluster
pg_vip_interface: eth1 # vip interface for all members
pg_dbsu_password: DBUser.Postgres # enable dbsu password access for citus
pg_extensions: [ citus, postgis, pgvector, topn, pg_cron, hll ] # install these extensions
pg_libs: 'citus, pg_cron, pg_stat_statements' # citus will be added by patroni automatically
pg_users: [{ name: dbuser_citus ,password: DBUser.Citus ,pgbouncer: true ,roles: [ dbrole_admin ] }]
pg_databases: [{ name: citus ,owner: dbuser_citus ,extensions: [ citus, vector, topn, pg_cron, hll ] }]
pg_parameters:
cron.database_name: citus
citus.node_conninfo: 'sslrootcert=/pg/cert/ca.crt sslmode=verify-full'
pg_hba_rules:
- { user: 'all' ,db: all ,addr: 127.0.0.1/32 ,auth: ssl ,title: 'all user ssl access from localhost' }
- { user: 'all' ,db: all ,addr: intra ,auth: ssl ,title: 'all user ssl access from intranet' }
我们将在后续教程中覆盖一系列关于 Citus 的高级主题
- 读写分离
- 故障处理
- 一致性备份与恢复
- 高级监控与问题诊断
- 连接池
10 - 高可用演习:3坏2如何处理
如果经典3节点高可用部署同时出现两台(多数主体)故障,系统通常无法自动完成故障切换,需要人工介入:
首先判断另外两台服务器的情况,如果短时间内可以拉起,优先选择拉起另外两台服务。否则进入 紧急止血流程
紧急止血流程假设您的管理节点故障,只有单台普通数据库节点存活,在这种情况下,最快的恢复操作流程为:
- 调整 HAProxy 配置,将流量指向主库。
- 关闭 Patroni,手动提升 PostgreSQL 从库为主库。
调整HAProxy配置
如果你通过其他方式绕开 HAProxy 访问集群,那么可以跳过这一步。 如果你通过 HAProxy 方式访问数据库集群,那么你需要调整负载均衡配置,将读写流量手工指向主库。
- 编辑
/etc/haproxy/<pg_cluster>-primary.cfg
配置文件,其中<pg_cluster>
为你的 PostgreSQL 集群名称,例如pg-meta
。 - 将健康检查配置选项注释,停止进行健康鉴擦好
- 将服务器列表中,其他两台故障的机器注释掉,只保留当前主库服务器。
listen pg-meta-primary
bind *:5433
mode tcp
maxconn 5000
balance roundrobin
# 注释掉以下四行健康检查配置
#option httpchk # <---- remove this
#option http-keep-alive # <---- remove this
#http-check send meth OPTIONS uri /primary # <---- remove this
#http-check expect status 200 # <---- remove this
default-server inter 3s fastinter 1s downinter 5s rise 3 fall 3 on-marked-down shutdown-sessions slowstart 30s maxconn 3000 maxqueue 128 weight 100
server pg-meta-1 10.10.10.10:6432 check port 8008 weight 100
# 注释掉其他两台故障的机器
#server pg-meta-2 10.10.10.11:6432 check port 8008 weight 100 <---- comment this
#server pg-meta-3 10.10.10.12:6432 check port 8008 weight 100 <---- comment this
配置调整完成后,先不着急执行 systemctl reload haproxy
重载生效,等待后续主库提升后一起执行。
以上配置的效果是,HAProxy 将不再进行主库健康检查(默认使用 Patroni),而是直接将写入流量指向当前主库
手工提升备库
登陆目标服务器,切换至 dbsu 用户,执行 CHECKPOINT
刷盘后,关闭 Patroni,重启 PostgreSQL 并执行 Promote。
sudo su - postgres # 切换到数据库 dbsu 用户
psql -c 'checkpoint; checkpoint;' # 两次 Checkpoint 刷脏页,避免PG后重启耗时过久
sudo systemctl stop patroni # 关闭 Patroni
pg-restart # 重新拉起 PostgreSQL
pg-promote # 将 PostgreSQL 从库提升为主库
psql -c 'SELECT pg_is_in_recovery();' # 如果结果为 f,表示已经提升为主库
如果你上面调整了 HAProxy 配置,那么现在可以执行 systemctl reload haproxy
重载 HAProxy 配置,将流量指向新的主库。
systemctl reload haproxy # 重载 HAProxy 配置,将写入流量指向当前实例
避免脑裂
紧急止血后,第二优先级问题为:避免脑裂。用户应当防止另外两台服务器重新上线后,与当前主库形成脑裂,导致数据不一致。
简单的做法是:
- 将另外两台服务器直接 断电/断网,确保它们不会在不受控的情况下再次上线。
- 调整应用使用的数据库连接串,将其 HOST 直接指向唯一幸存服务器上的主库。
然后应当根据具体情况,决定下一步的操作:
- A:这两台服务器是临时故障(比如断网断电),可以原地修复后继续服务
- B:这两台故障服务器是永久故障(比如硬件损坏),将移除并下线。
临时故障后的复原
如果另外两台服务器是临时故障,可以修复后继续服务,那么可以按照以下步骤进行修复与重建:
- 每次处理一台故障服务器,优先处理 管理节点 / INFRA 管理节点
- 启动故障服务器,并在启动后关停 Patroni
ETCD 集群在法定人数恢复后,将恢复工作,此时可以启动幸存服务器(当前主库)上的 Patroni,接管现有 PostgreSQL,并重新获取集群领导者身份。 Patroni 启动后进入维护模式。
systemctl restart patroni
pg pause <pg_cluster>
在另外两台实例上以 postgres
用户身份创建 touch /pg/data/standby.signal
标记文件将其标记为从库,然后拉起 Patroni:
systemctl restart patroni
确认 Patroni 集群身份/角色正常后,退出维护模式:
pg resume <pg_cluster>
永久故障后的复原
出现永久故障后,首先需要恢复管理节点上的 ~/pigsty
目录,主要是需要 pigsty.yml
与 files/pki/ca/ca.key
两个核心文件。
如果您无法取回或没有备份这两个文件,您可以选择部署一套新的 Pigsty,并通过 备份集群 的方式将现有集群迁移至新部署中。
请定期备份
pigsty
目录(例如使用 Git 进行版本管理)。建议吸取教训,下次不要犯这样的错误。
配置修复
您可以将幸存的节点作为新的管理节点,将 ~/pigsty
目录拷贝到新的管理节点上,然后开始调整配置。
例如,将原本默认的管理节点 10.10.10.10
替换为幸存节点 10.10.10.12
all:
vars:
admin_ip: 10.10.10.12 # 使用新的管理节点地址
node_etc_hosts: [10.10.10.12 h.pigsty a.pigsty p.pigsty g.pigsty sss.pigsty]
infra_portal: {} # 一并修改其他引用旧管理节点 IP (10.10.10.10) 的配置
children:
infra: # 调整 Infra 集群
hosts:
# 10.10.10.10: { infra_seq: 1 } # 老的 Infra 节点
10.10.10.12: { infra_seq: 3 } # 新增 Infra 节点
etcd: # 调整 ETCD 集群
hosts:
#10.10.10.10: { etcd_seq: 1 } # 注释掉此故障节点
#10.10.10.11: { etcd_seq: 2 } # 注释掉此故障节点
10.10.10.12: { etcd_seq: 3 } # 保留幸存节点
vars:
etcd_cluster: etcd
pg-meta: # 调整 PGSQL 集群配置
hosts:
#10.10.10.10: { pg_seq: 1, pg_role: primary }
#10.10.10.11: { pg_seq: 2, pg_role: replica }
#10.10.10.12: { pg_seq: 3, pg_role: replica , pg_offline_query: true }
10.10.10.12: { pg_seq: 3, pg_role: primary , pg_offline_query: true }
vars:
pg_cluster: pg-meta
ETCD修复
然后执行以下命令,将 ETCD 重置为单节点集群:
./etcd.yml -e etcd_safeguard=false -e etcd_clean=true
根据 ETCD重载配置 的说明,调整对 ETCD Endpoint 的引用。
INFRA修复
如果幸存节点上没有 INFRA 模块,请在当前节点上配置新的 INFRA 模块并安装。执行以下命令,将 INFRA 模块部署到幸存节点上:
./infra.yml -l 10.10.10.12
修复当前节点的监控
./node.yml -t node_monitor
PGSQL修复
./pgsql.yml -t pg_conf # 重新生成 PG 配置文件
systemctl reload patroni # 在幸存节点上重载 Patroni 配置
各模块修复后,您可以参考标准扩容流程,将新的节点加入集群,恢复集群的高可用性。