多页打印视图 点击此处打印.

返回常规视图.

部署

将Pigsty部署至您自己的服务器与虚拟机上

部署Pigsty分为两步:准备资源,与配置部署

准备资源

安装Pigsty前,您需要准备符合要求的环境软件

配置部署

您需要参考组件部署指南进行筹划,通过配置清单向Pigsty表明自己的需求,并通过剧本贯彻意图。

部署样例

  • 标准部署:您自己准备全新节点,完成标准Pigsty部署流程。
  • 沙箱部署 : 通过预制的vagrant模板一键拉起本地虚拟机沙箱环境。
  • 多云部署:使用terraform模板在云服务供应商处拉起所需虚拟机资源,并执行部署。
  • 仅监控部署 : 使用单节点Pigsty监控现有数据库集群。

1 - 环境准备

部署Pigsty所需的环境准备:主机节点,管理员用户,SSH,Sudo与DNS

1.1 - 节点置备

在部署Pigsty前,用户需要准备机器节点资源。

在部署Pigsty前,用户需要准备机器节点资源,包括至少一个元节点,与任意数量的普通节点

节点可以使用任意类型:物理机、本地虚拟机、云虚拟机,容器等,只需要满足以下条件:

  • 处理器架构:x86_64
  • 硬件规格至少为1核/1GB
  • 操作系统:CentOS 7.8.2003 (或其他RHEL 7等效发行版)
  • 管理用户可以从 元节点 ssh 登陆其他节点并执行sudo

元节点本身也是一个普通的节点。

节点数量

如果您计划将Pigsty用作开箱即用的PostgreSQL数据库实例,则一台节点足矣。

对于部署生产环境的高可用PostgreSQL数据库集群来说,您最少需要三个节点,我们建议使用4个节点。

如果您还计划将Pigsty用作更多主机/数据库的管控,则可以准备更多的节点备用。

置备节点

您可以参考 本地虚拟机置备 教程,使用Vagrant与Virtualbox一键在本地 x86_64 笔记上拉起沙箱环境所需的四台虚拟机。 您也可以参考 云端虚拟机置备 教程,使用Terraform在云供应商上创建沙箱环境所需的四台虚拟机。

1.2 - 元节点置备

Pigsty需要至少元节点作为整个环境的控制中心。

Pigsty需要元节点作为整个环境的控制中心,并提供基础设施 服务。

元节点的数量最少为1个,沙箱环境默认使用1个元节点。

默认情况下,Pigsty的基础设施以副本的形式部署在多个元节点上,DCS(Consul/Etcd)例外,DCS以Quorum的形式存在。

Pigsty的数据库集群需要使用DCS以实现高可用功能,您可以使用自动部署于元节点上的DCS集群,或使用外部的DCS集群。在大规模生产环境中,如果您没有专用的外部DCS集群,建议使用3个元节点以充分保证DCS服务的可用性。

用户应当确保自己可以登录元节点,并能使用管理用户从元节点上通过ssh登陆其他数据库节点,并带有sudoroot权限。用户应当确保自己可以直接或间接访问元节点的80端口,以访问Pigsty提供的用户界面。

  • 元节点数量:奇数个,至少一个
  • 能够使用管理员用户登陆元节点
  • 能够(直接或间接)通过浏览器访问元节点80端口
  • 管理用户可以从元节点远程ssh登陆数据库节点并执行sudo (包括自身)

1.3 - 管理用户置备

如何准备所需的管理员用户,配置SSH免密登陆,与免密sudo

Pigsty需要一个管理用户,该用户能够从元节点免密码SSH登陆其他节点,并免密码执行sudo命令。

如果您使用的是云服务器,通常在申请创建云服务器时,便会自动创建这样的管理用户给到客户。

管理用户

Pigsty需要一个管理用户,该用户能够从元节点上SSH登陆其他节点,并执行sudo命令。

  • 可以在元节点上使用该用户
  • 可以使用该用户SSH登陆所有被元节点(包括自身)
  • 可以在登陆所有被元节点后执行sudo命令(包括自身)
  • 管理用户不是postgres{{ dbsu }} (使用DBSU作为管理员有安全隐患)
  • ssh 登陆免密码,sudo 命令免密码(或您知晓如何通过-k,-K手工输入)

执行部署与变更时,您所使用的管理用户必须拥有所有节点的sshsudo权限。免密码并非必需,您总是可以在执行剧本时通过-k|-K参数传入ssh与sudo的密码,甚至通过 -eansible_host=<another_user> 使用其他用户来执行剧本。但Pigsty强烈建议为管理用户配置SSH免密码登陆与免密码sudo

Pigsty推荐将管理用户的创建,权限配置与密钥分发放在虚拟机的Provisioning阶段完成,作为机器资源交付内容的一部分。对于生产环境来说,机器交付时应当已经配置有这样一个具有免密远程SSH登陆并执行免密sudo的用户。通常绝大多数云平台和运维体系都可以做到这一点。

Pigsty剧本nodes 可以在节点上创建管理用户,但这涉及到一个先有鸡还是先有蛋但的问题:为了在远程节点执行Ansible剧本,需要有一个管理用户。为了创建一个专用管理用户,需要在远程节点上执行Ansible剧本。 作为Bootstrap阶段的妥协,只要您有SSH登陆与SUDO权限,即使没有密码,也可以用于执行Ansible剧本,详情请参考 Nodes:创建管理用户

配置SSH免密访问

在元节点上,假设执行命令的用户名为vagrant

生成密钥

vagrant用户的身份执行以ssh-keygen一路回车,会为vagrant生成公私钥对,用于登陆。

[vagrant@node-3 ~]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):
/home/vagrant/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/vagrant/.ssh/id_rsa.
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:nys41SqjxFcQYDJO2WkT/ZOB2rlechcOztPEZhrYSR0 vagrant@node-3
The key's randomart image is:
+---[RSA 2048]----+
|  +o+=.. .E.     |
| o.+= o.o .      |
|  .. +.* =       |
|    . +.O *      |
|       +S% .     |
|   .  o.O.=.     |
|    o..* +o      |
|   . .* o  .     |
|    .. + ..      |
+----[SHA256]-----+
[vagrant@node-3 ~]$
[vagrant@node-3 ~]$
  • 默认公钥:~/.ssh/id_rsa.pub
  • 默认私钥:~/.ssh/id_rsa

安装密钥

将公钥添加至需要登陆机器的对应用户上:/home/vagrant/.ssh/authorized_keys

如果您已经可以直接通过密码访问远程机器,可以直接通过ssh-copy-id的方式拷贝公钥。

# 输入密码以完成公钥拷贝
ssh-copy-id <ip>

# 直接将密码嵌入命令中,避免交互式密码输入
sshpass -p <password> ssh-copy-id <ip>

然后便可以通过该用户免密码SSH登陆远程机器。

配置免密SUDO

假设用户名为vagrant,则通过visudo 命令,或创建/etc/sudoers.d/vagrant 文件添加以下记录:

%vagrant ALL=(ALL) NOPASSWD: ALL

则 vagrant 用户即可免密sudo执行所有命令

1.4 - 本地虚拟机置备

如何使用Vagrant与Virtualbox迅速在本机创建Pigsty部署所需的虚拟机资源

通常为了测试“数据库集群”这样的系统,用户需要事先准备若干台虚拟机。尽管云服务已经非常方便,但本地虚拟机访问通常比云虚拟机访问方便,响应迅速,成本低廉。本地虚拟机配置相对繁琐,Vagrant 可解决这一问题。

Pigsty用户无需了解vagrant的原理,只需要知道vagrant可以简单、快捷地按照用户的需求,在笔记本、PC或Mac上拉起若干台虚拟机。用户需要完成的工作,就是将自己的虚拟机需求,以vagrant配置文件的形式表达出来。

太长;不看

对于MacOS用户,直接使用homebrew命令行一键安装Vagrant与Virtualbox即可,安装完毕后,一键拉起4台虚拟机。

make deps    # 安装homebrew,并通过homebrew安装vagrant与virtualbox(需重启)
make dns     # 向本机/etc/hosts写入静态域名 (需sudo输入密码)
make start   # 使用Vagrant拉起单个meta节点  (start4则为4个节点)

其他操作系统与平台请参考 VagrantVirtualbox 图形安装指南。

实际上底下真正管事的命令是:

# 安装Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装Vagrant与Virtualbox
brew install vagrant virtualbox ansible

# 拉起由Vagrantfile预先定义的4台虚拟机
cd vagrant; vagrant up

Vagrant安装

这里介绍了图形界面下下载、安装Vagrant的过程

访问Vagrant官网

https://www.vagrantup.com/downloads

下载Vagrant

最新版本为2.2.19

安装Vagrant

点击 vagrant.pkg 执行安装,安装过程需要输入密码。https://www.virtualbox.org/

在MacOS上安装Virtualbox非常简单,其他操作系统上与之类似。

Virtualbox安装

这里介绍了图形界面下下载、安装Virtualbox的过程

前往Virtualbox官网https://www.virtualbox.org/

下载Virtualbox

最新版本为6.1.24

安装Virtualbox

点击 VirtualBox.pkg 执行安装,安装过程需要输入密码并重启。

如果安装失败,请检查您的 系统偏好设置 - 安全性与隐私 - 通用 - 允许以下位置的App中点击“允许”按钮。

Vagrant配置文件

https://github.com/Vonng/pigsty/blob/master/vagrant/Vagrantfile 提供了一个Vagrantfile样例。

这是Pigsty沙箱所使用的Vagrantfile,定义了四台虚拟机,包括一台2核/4GB的中控机/元节点,和3台 1核/1GB 的数据库节点

vagrant 二进制程序根据 Vagrantfile 中的定义,默认调用 Virtualbox 完成本地虚拟机的创建工作。

进入Pigsty根目录下的vagrant目录,执行vagrant up,即可拉起所有的四台虚拟机。

IMAGE_NAME = "centos/7"
N=3  # 数据库机器节点数量,可修改为0

Vagrant.configure("2") do |config|
    config.vm.box = IMAGE_NAME
    config.vm.box_check_update = false
    config.ssh.insert_key = false

    # 元节点
    config.vm.define "meta", primary: true do |meta|  # 元节点默认的ssh别名为`meta`
        meta.vm.hostname = "meta"
        meta.vm.network "private_network", ip: "10.10.10.10"
        meta.vm.provider "virtualbox" do |v|
            v.linked_clone = true
            v.customize [
                    "modifyvm", :id,
                    "--memory", 4096, "--cpus", "2",   # 元节点的内存与CPU核数:默认为2核/4GB
                    "--nictype1", "virtio", "--nictype2", "virtio",
                    "--hwv·irtex", "on", "--ioapic", "on", "--rtcuseutc", "on", "--vtxvpid", "on", "--largepages", "on"
                ]
        end
        meta.vm.provision "shell", path: "provision.sh"
    end

    # 初始化N个数据库节点
    (1..N).each do |i|
        config.vm.define "node-#{i}" do |node|  # 数据库节点默认的ssh别名分别为`node-{1,2,3}`
            node.vm.box = IMAGE_NAME
            node.vm.network "private_network", ip: "10.10.10.#{i + 10}"
            node.vm.hostname = "node-#{i}"
            node.vm.provider "virtualbox" do |v|
                v.linked_clone = true
                v.customize [
                        "modifyvm", :id,
                        "--memory", 2048, "--cpus", "1", # 数据库节点的内存与CPU核数:默认为1核/2GB
                        "--nictype1", "virtio", "--nictype2", "virtio",
                        "--hwvirtex", "on", "--ioapic", "on", "--rtcuseutc", "on", "--vtxvpid", "on", "--largepages", "on"
                    ]
            end
            node.vm.provision "shell", path: "provision.sh"
        end
    end
end

如果用户的机器配置不足,则可以考虑使用更小的N值,减少数据库节点的数量。如果只希望运行单个元节点,将其修改为0即可。

用户还可以修改每台机器的CPU核数和内存资源等,如配置文件中的注释所述,详情参阅Vagrant与Pigsty文档。

沙箱环境默认使用IMAGE_NAME = "centos/7",首次执行时会从vagrant官方下载centos 7.8 virtualbox 镜像,确保宿主机拥有合适的网络访问权限(科学上网)!

快捷方式

Pigsty已经提供了对常用vagrant命令的包装,用户可以在项目的Makefile中看到虚拟机管理的相关命令:

make        # 启动集群
make new4   # 销毁并创建新集群
make dns    # 将Pigsty域名记录写入本机/etc/hosts (需要sudo权限)
make ssh    # 将虚拟机SSH配置信息写入 ~/.ssh/config

更多信息,请参考Makefile

#------------------------------#
# vagrant vm management
#------------------------------#
# default node (meta)
up:
	cd vagrant && vagrant up meta
dw:
	cd vagrant && vagrant halt meta
del:
	cd vagrant && vagrant destroy -f meta
new: del up
#------------------------------#
# extra nodes: node-{1,2,3}
up-test:
	cd vagrant && vagrant up node-1 node-2 node-3
dw-test:
	cd vagrant && vagrant halt node-1 node-2 node-3
del-test:
	cd vagrant && vagrant destroy -f node-1 node-2 node-3
new-test: del-test up-test
#------------------------------#
# all nodes (meta, node-1, node-2, node-3)
up4:
	cd vagrant && vagrant up
dw4:
	cd vagrant && vagrant halt
del4:
	cd vagrant && vagrant destroy -f
new4: del4 up4
clean: del4
#------------------------------#
# status
st: status
status:
	cd vagrant && vagrant status
suspend:
	cd vagrant && vagrant suspend
resume:
	cd vagrant && vagrant resume
#------------------------------#

1.5 - 云端虚拟机置备

如何使用Terraform迅速在阿里云创建Pigsty部署所需的虚拟机资源

如果您手头没有 x86_64 架构的PC、笔记本、Mac,使用即用即毁的云虚拟机可能是另一个不错的选择。

Terraform

Terraform 是开源免费的 基础设施即代码 工具。您只需要声明好所需的云虚拟机、网络与安全组配置等,一键即可拉起对应的资源。

在MacOS下安装Terraform,只需要执行brew install terraform即可。然后您需要有云厂商账号,并获取AccessKey与AccessSecret凭证,充点钱,就可以开始云端沙箱部署之旅啦。

TF配置文件

项目根目录 terraform/ 中提供了若干云厂商的 Terraform 资源定义文件,您可以使用这些模板快速在云上申请虚拟机资源用于部署Pigsty。这里以阿里云为例:

cd terraform        # 进入terraform目录中
vi alicloud.tf      # 编辑配置文件,填入您的阿里云AccessKey与SecretKey
阿里云样例Terraform文件
provider "alicloud" {
  access_key = "xxxxxx"
  secret_key = "xxxxxx"
  region = "cn-beijing"
}

# use 10.10.10.0/24 cidr block as demo network
resource "alicloud_vpc" "vpc" {
  vpc_name   = "pigsty-demo-network"
  cidr_block = "10.10.10.0/24"
}

# add virtual switch for pigsty demo network
resource "alicloud_vswitch" "vsw" {
  vpc_id     = "${alicloud_vpc.vpc.id}"
  cidr_block = "10.10.10.0/24"
  zone_id    = "cn-beijing-k"
}

# add default security group and allow all tcp traffic
resource "alicloud_security_group" "default" {
  name   = "default"
  vpc_id = "${alicloud_vpc.vpc.id}"
}
resource "alicloud_security_group_rule" "allow_all_tcp" {
  ip_protocol       = "tcp"
  type              = "ingress"
  nic_type          = "intranet"
  policy            = "accept"
  port_range        = "1/65535"
  priority          = 1
  security_group_id = "${alicloud_security_group.default.id}"
  cidr_ip           = "0.0.0.0/0"
}

# https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/instance
resource "alicloud_instance" "pg-meta-1" {
  instance_name              = "pg-meta-1"
  host_name                  = "pg-meta-1"
  instance_type              = "ecs.s6-c1m2.small"
  vswitch_id                 = "${alicloud_vswitch.vsw.id}"
  security_groups            = ["${alicloud_security_group.default.id}"]
  image_id                   = "centos_7_8_x64_20G_alibase_20200914.vhd"
  password                   = "PigstyDemo4"
  private_ip                 = "10.10.10.10"
  internet_max_bandwidth_out = 40 # 40Mbps , alloc a public IP
}

resource "alicloud_instance" "pg-test-1" {
  instance_name   = "pg-test-1"
  host_name       = "pg-test-1"
  instance_type   = "ecs.s6-c1m1.small"
  vswitch_id      = "${alicloud_vswitch.vsw.id}"
  security_groups = ["${alicloud_security_group.default.id}"]
  image_id        = "centos_7_8_x64_20G_alibase_20200914.vhd"
  password        = "PigstyDemo4"
  private_ip      = "10.10.10.11"
}

resource "alicloud_instance" "pg-test-2" {
  instance_name   = "pg-test-2"
  host_name       = "pg-test-2"
  instance_type   = "ecs.s6-c1m1.small"
  vswitch_id      = "${alicloud_vswitch.vsw.id}"
  security_groups = ["${alicloud_security_group.default.id}"]
  image_id        = "centos_7_8_x64_20G_alibase_20200914.vhd"
  password        = "PigstyDemo4"
  private_ip      = "10.10.10.12"
}

resource "alicloud_instance" "pg-test-3" {
  instance_name   = "pg-test-3"
  host_name       = "pg-test-3"
  instance_type   = "ecs.s6-c1m1.small"
  vswitch_id      = "${alicloud_vswitch.vsw.id}"
  security_groups = ["${alicloud_security_group.default.id}"]
  image_id        = "centos_7_8_x64_20G_alibase_20200914.vhd"
  password        = "PigstyDemo4"
  private_ip      = "10.10.10.13"
}


output "meta_ip" {
  value = "${alicloud_instance.pg-meta-1.public_ip}"
}

执行计划

首先,使用terraform命令,创建上面定义的云资源(共享1C1G临时用用很便宜,按需付费)

terraform init      # 安装 terraform provider: aliyun (仅第一次需要)
terraform apply     # 生成执行计划:创建虚拟机,虚拟网段/交换机/安全组

执行 apply 并输入 yes后,terraform会调用阿里云API创建对应的虚拟机资源。

Terraform Apply执行结果
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # alicloud_instance.pg-meta-1 will be created
  + resource "alicloud_instance" "pg-meta-1" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + dry_run                            = false
      + host_name                          = "pg-meta-1"
      + id                                 = (known after apply)
      + image_id                           = "centos_7_8_x64_20G_alibase_20200914.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "pg-meta-1"
      + instance_type                      = "ecs.s6-c1m2.small"
      + internet_charge_type               = "PayByTraffic"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 40
      + key_name                           = (known after apply)
      + password                           = (sensitive value)
      + private_ip                         = "10.10.10.10"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_efficiency"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 40
      + volume_tags                        = (known after apply)
      + vswitch_id                         = (known after apply)
    }

  # alicloud_instance.pg-test-1 will be created
  + resource "alicloud_instance" "pg-test-1" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + dry_run                            = false
      + host_name                          = "pg-test-1"
      + id                                 = (known after apply)
      + image_id                           = "centos_7_8_x64_20G_alibase_20200914.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "pg-test-1"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + password                           = (sensitive value)
      + private_ip                         = "10.10.10.11"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_efficiency"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 40
      + volume_tags                        = (known after apply)
      + vswitch_id                         = (known after apply)
    }

  # alicloud_instance.pg-test-2 will be created
  + resource "alicloud_instance" "pg-test-2" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + dry_run                            = false
      + host_name                          = "pg-test-2"
      + id                                 = (known after apply)
      + image_id                           = "centos_7_8_x64_20G_alibase_20200914.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "pg-test-2"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + password                           = (sensitive value)
      + private_ip                         = "10.10.10.12"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_efficiency"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 40
      + volume_tags                        = (known after apply)
      + vswitch_id                         = (known after apply)
    }

  # alicloud_instance.pg-test-3 will be created
  + resource "alicloud_instance" "pg-test-3" {
      + availability_zone                  = (known after apply)
      + credit_specification               = (known after apply)
      + deletion_protection                = false
      + dry_run                            = false
      + host_name                          = "pg-test-3"
      + id                                 = (known after apply)
      + image_id                           = "centos_7_8_x64_20G_alibase_20200914.vhd"
      + instance_charge_type               = "PostPaid"
      + instance_name                      = "pg-test-3"
      + instance_type                      = "ecs.s6-c1m1.small"
      + internet_max_bandwidth_in          = (known after apply)
      + internet_max_bandwidth_out         = 0
      + key_name                           = (known after apply)
      + password                           = (sensitive value)
      + private_ip                         = "10.10.10.13"
      + public_ip                          = (known after apply)
      + role_name                          = (known after apply)
      + secondary_private_ip_address_count = (known after apply)
      + secondary_private_ips              = (known after apply)
      + security_groups                    = (known after apply)
      + spot_strategy                      = "NoSpot"
      + status                             = "Running"
      + subnet_id                          = (known after apply)
      + system_disk_category               = "cloud_efficiency"
      + system_disk_performance_level      = (known after apply)
      + system_disk_size                   = 40
      + volume_tags                        = (known after apply)
      + vswitch_id                         = (known after apply)
    }

  # alicloud_security_group.default will be created
  + resource "alicloud_security_group" "default" {
      + id                  = (known after apply)
      + inner_access        = (known after apply)
      + inner_access_policy = (known after apply)
      + name                = "default"
      + security_group_type = "normal"
      + vpc_id              = (known after apply)
    }

  # alicloud_security_group_rule.allow_all_tcp will be created
  + resource "alicloud_security_group_rule" "allow_all_tcp" {
      + cidr_ip           = "0.0.0.0/0"
      + id                = (known after apply)
      + ip_protocol       = "tcp"
      + nic_type          = "intranet"
      + policy            = "accept"
      + port_range        = "1/65535"
      + priority          = 1
      + security_group_id = (known after apply)
      + type              = "ingress"
    }

  # alicloud_vpc.vpc will be created
  + resource "alicloud_vpc" "vpc" {
      + cidr_block        = "10.10.10.0/24"
      + id                = (known after apply)
      + ipv6_cidr_block   = (known after apply)
      + name              = (known after apply)
      + resource_group_id = (known after apply)
      + route_table_id    = (known after apply)
      + router_id         = (known after apply)
      + router_table_id   = (known after apply)
      + status            = (known after apply)
      + vpc_name          = "pigsty-demo-network"
    }

  # alicloud_vswitch.vsw will be created
  + resource "alicloud_vswitch" "vsw" {
      + availability_zone = (known after apply)
      + cidr_block        = "10.10.10.0/24"
      + id                = (known after apply)
      + name              = (known after apply)
      + status            = (known after apply)
      + vpc_id            = (known after apply)
      + vswitch_name      = (known after apply)
      + zone_id           = "cn-beijing-k"
    }

Plan: 8 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + meta_ip = (known after apply)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

alicloud_vpc.vpc: Creating...
alicloud_vpc.vpc: Creation complete after 6s [id=vpc-2zed78z7n5z06o1dmydhj]
alicloud_security_group.default: Creating...
alicloud_vswitch.vsw: Creating...
alicloud_security_group.default: Creation complete after 1s [id=sg-2ze7x7zu8tcdsefroofa]
alicloud_security_group_rule.allow_all_tcp: Creating...
alicloud_security_group_rule.allow_all_tcp: Creation complete after 0s [id=sg-2ze7x7zu8tcdsefroofa:ingress:tcp:1/65535:intranet:0.0.0.0/0:accept:1]
alicloud_vswitch.vsw: Creation complete after 6s [id=vsw-2zejctjdr16ryz194jxz4]
alicloud_instance.pg-test-3: Creating...
alicloud_instance.pg-test-2: Creating...
alicloud_instance.pg-test-1: Creating...
alicloud_instance.pg-meta-1: Creating...
alicloud_instance.pg-test-3: Still creating... [10s elapsed]
alicloud_instance.pg-test-2: Still creating... [10s elapsed]
alicloud_instance.pg-test-1: Still creating... [10s elapsed]
alicloud_instance.pg-meta-1: Still creating... [10s elapsed]
alicloud_instance.pg-meta-1: Creation complete after 16s [id=i-2zef4frw6kezb47339wr]
alicloud_instance.pg-test-1: Still creating... [20s elapsed]
alicloud_instance.pg-test-2: Still creating... [20s elapsed]
alicloud_instance.pg-test-3: Still creating... [20s elapsed]
alicloud_instance.pg-test-2: Creation complete after 23s [id=i-2zefzvz0fyl7mloc4v30]
alicloud_instance.pg-test-1: Still creating... [30s elapsed]
alicloud_instance.pg-test-3: Still creating... [30s elapsed]
alicloud_instance.pg-test-3: Creation complete after 33s [id=i-2zeeyodo2pc8b1k2d167]
alicloud_instance.pg-test-1: Creation complete after 33s [id=i-2zef4frw6kezb47339ws]

SSH配置与微调

其中,管理机将分配一个按量付费的公网IP,您也可以使用命令terraform output将其打印出来。

# 打印公网IP与root密码
ssh_pass='PigstyDemo4'
public_ip=$(terraform output | grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
echo "meta node: root:${ssh_pass}@${public_ip}"

接下来,我们先来配置本地登录云端管理机器的SSH配置(默认用户root,密码PigstyDemo4

# 创建 ~/.ssh/pigsty_terraform 文件,包含云端管理机器的SSH定义(可选,好用一点)
cat > ~/.ssh/pigsty_terraform <<-EOF
Host demo
  User root
  HostName ${public_ip}
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication yes
EOF
chmod 0600 ~/.ssh/pigsty_terraform 

# 启用该配置
if ! grep --quiet "Include ~/.ssh/pigsty_terraform" ~/.ssh/config ; then
    (echo 'Include ~/.ssh/pigsty_terraform' && cat ~/.ssh/config) >  ~/.ssh/config.tmp;
    mv ~/.ssh/config.tmp ~/.ssh/config && chmod 0600 ~/.ssh/config;
fi

然后,您可以通过SSH别名demo访问该云端管理机了。

# 添加本地到元节点的免密访问
sshpass -p ${ssh_pass} ssh-copy-id demo 

然后,您就可以免密从本地访问该节点了,如果只需要进行单节点安装,这样就行了。接下来,在该元节点上完成标准安装

特殊注意事项

阿里云虚拟机CentOS 7.8镜像中运行有 nscd ,锁死了 glibc 版本,会导致安装时出现RPM依赖错误。

在所有机器上执行 yum remove -y nscd 即可解决此问题。

完成上述准备工作后,所有机器准备工作已经就绪,可以开始常规的 Pigsty下载配置安装三部曲啦。

1.6 - DNS域名配置

为Pigsty Web服务配置自定义域名

Pigsty默认通过域名访问所有Web系统,尽管您可以使用 IP:Port的方式访问主要系统的Web界面,但这并不是推荐的行为。

因为有一些Web服务(例如Consul)监听的地址是 127.0.0.1 而非 0.0.0.0,因此您只能通过本机上的Nginx代理访问,并使用域名来区分。

太长;不看

在MacOS与Linux中,执行以下命令 bin/dns,配置Pigsty所需的DNS记录

sudo make dns

实际上是将以下记录写入 /etc/hosts 中(需要输入sudo密码),在Windows中则自行添加至:C:\Windows\System32\drivers\etc\hosts中。

# pigsty dns records
10.10.10.10 meta pigsty p.pigsty g.pigsty a.pigsty c.pigsty l.pigsty
10.10.10.10 api.pigsty adm.pigsty cli.pigsty ddl.pigsty lab.pigsty git.pigsty sss.pigsty
10.10.10.11 node-1   # sandbox node node-1
10.10.10.12 node-2   # sandbox node node-2
10.10.10.13 node-3   # sandbox node node-3
10.10.10.2  pg-meta  # sandbox vip for pg-meta
10.10.10.3  pg-test  # sandbox vip for pg-test

默认域名

默认情况下,Pigsty使用的域名包括:

10.10.10.10 meta pigsty p.pigsty g.pigsty a.pigsty c.pigsty l.pigsty                      # 必选
10.10.10.10 api.pigsty adm.pigsty cli.pigsty ddl.pigsty lab.pigsty git.pigsty sss.pigsty

所有Web服务的域名均通过 nginx_upstream 进行配置,例如,沙箱demo的域名配置如下:

nginx_upstream:                   # domain names and upstream servers
  - { name: home         , domain: pigsty     , endpoint: "10.10.10.10:80"   }
  - { name: grafana      , domain: g.pigsty   , endpoint: "10.10.10.10:3000" }
  - { name: loki         , domain: l.pigsty   , endpoint: "10.10.10.10:3100" }
  - { name: prometheus   , domain: p.pigsty   , endpoint: "10.10.10.10:9090" }
  - { name: alertmanager , domain: a.pigsty   , endpoint: "10.10.10.10:9093" }
  - { name: consul       , domain: c.pigsty   , endpoint: "127.0.0.1:8500"   } #== ^ required ==#
  - { name: postgrest    , domain: api.pigsty , endpoint: "127.0.0.1:8884"   } #== v optional ==#
  - { name: pgadmin      , domain: adm.pigsty , endpoint: "127.0.0.1:8885"   }
  - { name: pgweb        , domain: cli.pigsty , endpoint: "127.0.0.1:8886"   }
  - { name: bytebase     , domain: ddl.pigsty , endpoint: "127.0.0.1:8887"   }
  - { name: jupyter      , domain: lab.pigsty , endpoint: "127.0.0.1:8888"   }
  - { name: gitea        , domain: git.pigsty , endpoint: "127.0.0.1:8889"   }
  - { name: minio        , domain: sss.pigsty , endpoint: "127.0.0.1:9000"   }

配置自定义域名

您可以定制Pigsty各自系统使用的域名,例如在Pigsty的公开Demo中,就使用了不同的域名,并由互联网DNS服务商负责解析

nginx_upstream:                   # domain names and upstream servers
  - { name: home         , domain: home.pigsty.cc , endpoint: "10.10.10.10:80"   }  # default -> index.html (80)
  - { name: grafana      , domain: demo.pigsty.cc , endpoint: "10.10.10.10:3000" }  # pigsty grafana (3000)
  - { name: loki         , domain: l.pigsty.cc    , endpoint: "10.10.10.10:3100" }  # pigsty loki (3100)
  - { name: prometheus   , domain: p.pigsty.cc    , endpoint: "10.10.10.10:9090" }  # pigsty prometheus (9090)
  - { name: alertmanager , domain: a.pigsty.cc    , endpoint: "10.10.10.10:9093" }  # pigsty alertmanager (9093)
  - { name: consul       , domain: c.pigsty.cc    , endpoint: "127.0.0.1:8500"   }  # pigsty consul UI (8500) (domain required)
  - { name: postgrest    , domain: api.pigsty.cc  , endpoint: "127.0.0.1:8884"   }  #== v optional ==#
  - { name: pgadmin      , domain: adm.pigsty.cc  , endpoint: "127.0.0.1:8885"   }
  - { name: pgweb        , domain: cli.pigsty.cc  , endpoint: "127.0.0.1:8886"   }
  - { name: bytebase     , domain: ddl.pigsty.cc  , endpoint: "127.0.0.1:8887"   }
  - { name: jupyter      , domain: lab.pigsty.cc  , endpoint: "127.0.0.1:8888"   }
  - { name: gitea        , domain: git.pigsty.cc  , endpoint: "127.0.0.1:8889"   }
  - { name: minio        , domain: sss.pigsty.cc  , endpoint: "127.0.0.1:9000"   }

如果您没有购买互联网域名,则可以自定义的域名配置到您自己网络的DNS服务器中,或者干脆写入需要访问Pigsty Web服务的服务器的静态解析记录中。

例如在Nginx与MacOS上,您需要将上述域名记录写入 /etc/hosts (需要sudo权限),在Windows中,则需要添加至:C:\Windows\System32\drivers\etc\hosts中。

2 - 软件准备

部署Pigsty所需的软件,安装包,以及如何在没有互联网访问的环境中进行离线安装

2.1 - Pigsty 软件下载

Pigsty 软件资源列表,以及从哪里获取它们。

Pigsty的唯一权威发布地址为 Github 仓库: Vonng/pigsty,所有软件发行版都位于该项目 Release 页面

太长不看

# 使用此命令下载 pigsty.tgz 源码包,该脚本将区分墙内墙外,在大陆使用CDN加速下载
bash -c "$(curl -fsSL https://get.pigsty.cc/latest)"  # get latest pigsty source

# 进入Pigsty源码目录,使用自带脚本下载离线软件包,同样区分墙内墙外环境。
cd pigsty; ./download pkg

软件包列表

当前,一个发行版本包含以下软件内容:

  • pigsty.tgz必选项,Pigsty源代码
  • pkg.tgz可选项,基于CentOS 7.8.2003 提前制作的离线软件安装包;如需离线安装Pigsty,需要下载此软件包。
  • matrix.tgz可选项,如果需要安装Greenplum与MatrixDB,请额外下载此软件包
  • docker.tgz可选项,如果需要使用一些基于Docker的扩展软件应用,可下载此软件包
  • app.tgz可选项,如果想要下载Pigsty自带的可视化应用Applet样例,可下载此软件包

除了源码包 pigsty.tgz 为必选项目,其他软件包均为可选。例如,如果不下载 pkg.tgz,Pigsty就会在基础设施初始化时,直接通过互联网从 repo_upstream 下载所需的所有软件包。

下载脚本

Pigsty本身提供了一个下载脚本:download,来下载这些软件包

$ ./download
    download and extract pigsty packages: pigsty.tgz pkg.tgz app.tgz matrix.tgz
    usage:
        download pigsty pkg   # download pigsty essentials

        download pigsty.tgz   # download pigsty source tarball
        download pkg.tgz      # download pigsty offline pkgs
        download app.tgz      # download extra pigsty apps
        download matrix.tgz   # download matrixdb packages
        download docker.tgz   # download docker images cache

        download pigsty       # download and extract pigsty to ~/pigsty
        download pkg          # download and extract pkg    to /www/pigsty
        download app          # download and extract app    to ~/app
        download matrix       # download and extract matrix to /www/matrix

此脚本会自动检测当前主机网络环境(by ping google),正常从Github下载Release,墙内则通过腾讯CDN下载软件包。

2.2 - Pigsty 离线安装

如何在没有互联网访问的环境中离线安装Pigsty

Pigsty是一个复杂的软件系统,为了确保系统的稳定,Pigsty会在初始化过程中从互联网下载所有依赖的软件包并建立本地仓库 (本地Yum源)。

所有依赖的软件总大小约1GB左右,下载速度取决于用户的网络情况。尽管Pigsty已经尽量使用镜像源以加速下载,但少量包的下载仍可能受到防火墙的阻挠,可能出现非常慢的情况。用户可以通过 proxy_env 配置项设置下载代理,以完成首次下载。

如果您使用了不同于CentOS 7.8的操作系统,通常建议用户采用完整的在线下载安装流程。并在首次初始化完成后缓存下载的软件,参见制作离线安装包

如果您希望跳过漫长的下载过程,或者执行控制的元节点没有互联网访问,则可以考虑下载预先打包好的离线安装包

离线安装包的内容

为了快速拉起Pigsty,建议使用离线下载软件包并上传的方式完成安装。

离线安装包收纳了本地Yum源的所有软件包。默认情况下,Pigsty会在基础设施初始化时创建本地Yum源,

{{ nginx_home }}
  |---- {{ repo_name }}.repo
  ^---- {{ repo_name}}/repo_complete
  ^---- {{ repo_name}}/**************.rpm

默认情况下,{{ nginx_home }} 是Nginx静态文件服务器的根目录,默认为/wwwrepo_name 是自定义的本地源名称,默认为pigsty

以默认情况为例,/www/pigsty 目录包含了所有 RPM 软件包,离线安装包实际上就是 /www/pigsty 目录的压缩包 。

离线安装包的原理是,Pigsty在执行基础设施初始化的过程中,会检查本地Yum源相关文件是否已经存在。如果已经存在,则会跳过下载软件包及其依赖的过程。

检测所用的标记文件为{{ nginx_home }}/{{ repo_name }}/repo_complete,默认情况下为/www/pigsty/repo_complete,如果该标记文件存在,(通常是由Pigsty在创建本地源之后设置),则表示本地源已经建立完成,可以直接使用。否则,Pigsty会执行常规的下载逻辑。下载完毕后,您可以将该目录压缩复制归档,用于加速其他环境的初始化。

沙箱环境

下载离线安装包

Pigsty自带了一个沙箱环境,沙箱环境的离线安装包默认放置于files目录中,可以从Github Release页面下载。

VERSION=v1.5.1
curl -SL https://github.com/Vonng/pigsty/releases/download/${VERSION}/pkg.tgz -o /tmp/pkg.tgz

Pigsty的官方CDN也提供最新版本的pkg.tgz下载,只需要执行以下命令即可。

curl http://download.pigsty.cc/v1.5.1/pkg.tgz -o /tmp/pkg.tgz

上传离线安装包

使用Pigsty沙箱时,下载离线安装包至本地files目录后,则可以直接使用 Makefile 提供的快捷指令make copy-pkg上传离线安装包至元节点上。

使用 make upload,也会将本地的离线安装包(Yum缓存)拷贝至元节点上。

# upload rpm cache to meta controller
upload:
	ssh -t meta "sudo rm -rf /tmp/pkg.tgz"
	scp -r files/pkg.tgz meta:/tmp/pkg.tgz
	ssh -t meta "sudo mkdir -p /www/pigsty/; sudo rm -rf /www/pigsty/*; sudo tar -xf /tmp/pkg.tgz --strip-component=1 -C /www/pigsty/"

制作离线安装包

使用 Pigsty 沙箱时,可以通过 make cache 将沙箱中元节点的缓存制为离线安装包,并拷贝到本地。

# cache rpm packages from meta controller
cache:
	rm -rf pkg/* && mkdir -p pkg;
	ssh -t meta "sudo tar -zcf /tmp/pkg.tgz -C /www pigsty; sudo chmod a+r /tmp/pkg.tgz"
	scp -r meta:/tmp/pkg.tgz files/pkg.tgz
	ssh -t meta "sudo rm -rf /tmp/pkg.tgz"

在生产环境离线安装包

在生产环境使用离线安装包前,您必须确保生产环境的操作系统与制作该离线安装包的机器操作系统一致。Pigsty提供的离线安装包默认使用CentOS 7.8。

使用不同操作系统版本的离线安装包可能会出错,也可能不会,我们强烈建议不要这么做。

如果需要在其他版本的操作系统(例如CentOS7.3,7.7等)上运行Pigsty,建议用户在安装有同版本操作系统的沙箱中完整执行一遍初始化流程,不使用离线安装包,而是直接从上游源下载的方式进行初始化。对于没有网络访问的生产环境元节点而言,制作离线软件包是至关重要的。

常规初始化完成后,用户可以通过make cache或手工执行相关命令,将特定操作系统的软件缓存打为离线安装包。供生产环境使用。

从初始化完成的本地元节点构建离线安装包:

tar -zcf /tmp/pkg.tgz -C /www pigsty     # 制作离线软件包

在生产环境使用离线安装包与沙箱环境类似,用户需要将pkg.tgz复制到元节点上,然后将离线安装包解压至目标地址。

这里以默认的 /www/pigsty 为例,将压缩包中的所有内容(RPM包,repo_complete标记文件,repodata 源的元数据库等)解压至目标目录/www/pigsty中,可以使用以下命令。

mkdir -p /www/pigsty/
sudo rm -rf /www/pigsty/*
sudo tar -xf /tmp/pkg.tgz --strip-component=1 -C /www/pigsty/

2.3 - Pigsty源码包

如何下载并使用可选的Pigsty Docker扩展离线镜像包

用户需要将Pigsty项目下载至元节点。

如果您是软件开发者,贡献者,则请使用Github克隆Master主干。如果您是软件用户,请务必使用具体的版本,而非Github Master分支,该分支可能处于开发中途的不一致状态中。

下载Pigsty源码

用户可以从 Github Release 页面下载最新版本的Pigsty源码包:

VERSION=v1.5.1
https://github.com/Vonng/pigsty/releases/download/${VERSION}/pigsty.tgz 

也可以从 Pigsty CDN 下载最新版本的Pigsty源代码

VERSION=v1.5.1
http://download.pigsty.cc/${VERSION}/pigsty.tgz

执行以下脚本,将自动获取最新稳定版本的Pigsty:

bash -c "$(curl -fsSL https://get.pigsty.cc/latest)"

Pigsty 源码包自带的下载脚本 download 也可以用于下载特定版本的Pigsty源码包本身

./download pigsty.tgz  # 下载最新稳定版本的离线软件包至 /tmp/pigsty.tgz
./download pigsty      # 不仅下载 /tmp/pigsty.tgz ,还将其解压至 ~/pigsty ,如果目标目录已经存在则跳过解压

克隆Pigsty源代码

git clone https://github.com/Vonng/pigsty
git clone git@github.com:Vonng/pigsty.git

2.4 - Pigsty pkg.tgz 离线软件包

如何下载并使用可选的Pigsty离线软件包

Pigsty提供了基于 CentOS 7.8.2003 环境下制作的离线软件包,如果您正好使用此系统镜像,则可以确保无需互联网即可成功安装。

离线软件包的默认下载放置路径为/tmp/pkg.tgz,Pigsty在执行 ./configure 的过程中,如果没有发现该路径下存在可用的离线软件包,会提示您下载,您也可以跳过,直接从原始上游下载。

不使用离线软件包时,Pigsty默认会从互联网上游Repo中直接下载所需软件(约1GB),这一过程耗时取决于您的网络条件,一些来自Github或墙外的软件包可能下载速度非常缓慢,甚至完全无法访问。

如果您使用的操作系统是其他 EL7 兼容发行版,则可能存在 极个别 RPM 软件包版本不兼容问题,您可以参考 Pigsty离线安装 或 FAQ 介绍的方法,从原始上游下载替换带有问题的RPM软件包。

下载离线软件包

从Github下载最新、权威的软件包

VERSION=v1.5.1
wget https://github.com/Vonng/pigsty/releases/download/${VERSION}/pkg.tgz -o /tmp/pkg.tgz 

中国大陆可以使用CDN下载:

VERSION=v1.5.1
curl http://download.pigsty.cc/${VERSION}/pkg.tgz -o  /tmp/pkg.tgz

更简洁的方式是使用 Pigsty 源码包自带的下载脚本 download

./download pkg.tgz  # 下载最新稳定版本的离线软件包至 /tmp/pkg.tgz
./download pkg      # 不仅下载 /tmp/pkg.tgz ,还将其解压至 /www/pigsty 并配置本地静态文件源,开箱即用。

离线软件安装包快捷命令

copy-pkg:
	scp dist/${VERSION}/pkg.tgz meta:/tmp/pkg.tgz

use-pkg:
	ssh meta '/home/vagrant/pigsty/configure --ip 10.10.10.10 --non-interactive --download -m demo'

load-docker:
	ssh meta 'cat /tmp/docker.tgz | gzip -d -c - | docker load'

release-pkg: cache
	scp meta:/tmp/pkg.tgz dist/${VERSION}/pkg.tgz

rp: release-pkg

2.5 - Pigsty Docker扩展镜像包

如何下载并使用可选的Pigsty Docker扩展离线镜像包

Docker是开箱即用的容器基础设施,您可以使用Docker拉起开箱即用的软件容器,而无需过多关心安装、部署等运维管理细节。

Pigsty带有一些Docker应用样例,这些Docker应用都可以直接从DockerHub或其他镜像站点直接拉取。

对于没有互联网访问的场景,Pigsty制作了几个常用容器镜像的软件包:docker.tgz,这是一个可选项,包含的软件如下:

docker pull kong                     # latest # 139MB
docker pull minio/minio              # latest # 227MB
docker pull alpine                   # latest # 5.57MB
docker pull registry                 # latest # 24.2MB
docker pull dpage/pgadmin4           # latest # 341MB
docker pull sosedoff/pgweb           # latest # 192MB
docker pull postgrest/postgrest      # latest # 16.3MB
docker pull swaggerapi/swagger-ui    # latest # 77MB
docker pull bytebase/bytebase:1.0.5  # 1.0.5  # 78.1MB
docker pull vonng/pg_exporter        # latest # 7.64B
docker pull gitea/gitea              # latest # 256MB

软件包的制作方式

docker save kong alpine registry dpage/pgadmin4 sosedoff/pgweb postgrest/postgrest swaggerapi/swagger-ui minio/minio bytebase/bytebase:1.0.5 vonng/pg_exporter gitea/gitea | gzip -9 -c > /tmp/docker.tgz  

您可以直接使用 cat /tmp/docker.tgz | gzip -d -c - | docker load 的方式加载这些镜像,或者在初始化节点前,将其放置于 /tmp/docker.tgz ,则Pigsty在部署Docker时,会自动加载此位置的镜像压缩包。

下载Docker扩展软件包

从Github下载最新、权威的软件包

VERSION=v1.5.1
wget https://github.com/Vonng/pigsty/releases/download/${VERSION}/docker.tgz -o /tmp/docker.tgz 

中国大陆可以使用CDN下载:

VERSION=v1.5.1
curl http://download.pigsty.cc/${VERSION}/docker.tgz -o /tmp/docker.tgz

更简洁的方式是使用 Pigsty 源码包自带的下载脚本 download

./download docker.tgz  # 下载最新稳定版本的docker扩展软件包至 /tmp/docker.tgz

Docker扩展软件包快捷方式

copy-docker:
	scp dist/${VERSION}/docker.tgz meta:/tmp/docker.tgz

load-docker:
	ssh meta 'cat /tmp/docker.tgz | gzip -d -c - | docker load'

release-docker:
	ssh meta 'docker save kong alpine registry dpage/pgadmin4 sosedoff/pgweb postgrest/postgrest swaggerapi/swagger-ui minio/minio bytebase/bytebase:1.0.5 vonng/pg_exporter gitea/gitea | gzip -9 -c > /tmp/docker.tgz'
	scp meta:/tmp/docker.tgz dist/${VERSION}/docker.tgz

rp3: release-docker

2.6 - Pigsty Applet扩展软件包

如何下载并使用可选的 Pigsty 可视化小应用演示样例包 app.tgz

GitHub Repo : Vonng/pigsty-app

Pigsty提供了基于了一些可视化应用样例,这些样例及其基础数据被制成了一个单独的,可选的软件包 app.tgz,大小约 70MB。

下载应用Applet演示包

从Github下载最新、权威的软件包

VERSION=v1.5.1
wget https://github.com/Vonng/pigsty/releases/download/${VERSION}/app.tgz -o /tmp/app.tgz 

中国大陆可以使用CDN下载:

VERSION=v1.5.1
curl http://download.pigsty.cc/${VERSION}/app.tgz -o files/app.tgz

更简洁的方式是使用 Pigsty 源码包自带的下载脚本 download

./download app.tgz  # 下载最新稳定版本的离线软件包至 /tmp/app.tgz
./download app      # 不仅下载 /tmp/app.tgz ,还将其解压至 ~/app

2.7 - Pigsty MatrixDB扩展软件包

如何下载并使用可选的 Pigsty MatrixDB扩展离线软件包

Pigsty v1.4 引入了 MatrixDB 部署支持,这是Greenplum7 (尚未发布)的一个功能超集版本。

但并不是所有用户都会用到数据仓库,因此在Pigsty中,与Greenplum/MatrixDB相关的软件包被单独打包为一个扩展软件包 matrix.tgz

当您需要使用 MatrixDB 或 Greenplum 时,可以参考 pigsty-mxdb 配置文件直接从原始上游下载,或下载 matrix.tgz 并解压至管理节点 /www/matrix 使用。

下载离线软件包

从Github下载最新、权威的软件包

VERSION=v1.5.1
wget https://github.com/Vonng/pigsty/releases/download/${VERSION}/matrix.tgz -o /tmp/matrix.tgz 

中国大陆可以使用CDN下载:

VERSION=v1.5.1
curl http://download.pigsty.cc/${VERSION}/matrix.tgz -o  /tmp/matrix.tgz

更简洁的方式是使用 Pigsty 源码包自带的下载脚本 download

./download matrix.tgz  # 下载最新稳定版本的离线软件包至 /tmp/matrix.tgz
./download matrix      # 不仅下载 /tmp/matrix.tgz ,还将其解压至 /www/matrix 并配置本地静态文件源,开箱即用。

制作MatrixDB扩展软件包

copy-matrix:
	scp dist/${VERSION}/matrix.tgz meta:/tmp/matrix.tgz

use-matrix:
	ssh meta 'sudo tar -xf /tmp/matrix.tgz -C /www'
	scp files/matrix.repo meta:/tmp/matrix.repo
	ssh meta sudo mv -f /tmp/matrix.repo /www/matrix.repo

rp2: release-matrix
release-matrix:
	#ssh meta 'sudo cp -r /www/matrix /tmp/matrix; sudo chmod -R a+r /www/matrix'
	ssh meta sudo tar zcvf /tmp/matrix.tgz -C /www matrix
	scp meta:/tmp/matrix.tgz dist/${VERSION}/matrix.tgz`

2.8 - Ansible

Pigsty剧本使用Ansible编写,但用户无需了解此软件的使用细节。

Ansible剧本需要使用ansible-playbook可执行命令,Ansible可以通过包管理器安装:

# 在EL7兼容系统中可通过以下命令安装 Ansible。
yum install ansible

# 在MacOS中可以使用Homebrew安装 Ansible
brew install ansible

安装后,可以检查安装的软件版本:

$ echo $(ansible --version)
ansible 2.10.3

当使用离线软件包时,Pigsty会在配置过程中尝试从离线软件包中安装ansible。

Pigsty依赖Ansible进行环境初始化。但如果元节点本身没有安装Ansible,也没有互联网访问怎么办?

离线软件包中本身带有 Ansible,可以直接通过本地文件Yum源的方式使用。

手工从离线软件包中安装Ansible

假设用户已经将离线安装包解压至默认位置:/www/pigsty

那么将以下Repo文件写入/etc/yum.repos.d/pigsty-local.repo 中,就可以直接使用该源。

[pigsty-local]
name=Local Yum Repo pigsty
baseurl=file:///www/pigsty
skip_if_unavailable = 1
enabled = 1
priority = 1
gpgcheck = 0

执行以下命令,在元节点上离线安装Ansible

yum clean all
yum makecache
yum install ansible

3 - 沙箱环境

介绍Pigsty的沙箱环境,一个配置规格、对象标识符、与默认数据库预先确定的环境,用于教学演示之用。

Pigsty支持使用 本地沙箱云端沙箱 两种方式,可用于快速在本机或云端准备标准的1/4节点演示环境。

尽管安装Pigsty已经非常简单了,但是搭建满足要求虚拟机仍然是比较费事的,您可能需要用到各类虚拟机软件。

因此Pigsty提供了沙箱环境,进一步免除用户准备环境的烦恼。完整地创建并跑通沙箱安装部署流程,对于在生产环境中部署有Pigsty 很大的帮助。

沙箱环境简介

沙箱环境是一个配置规格、对象标识符、与默认数据库预先确定的环境,无论是本地版还是云端版都保持一致。

沙箱环境使用固定的IP地址,以便于演示说明,沙箱的元节点IP地址固定为:10.10.10.1010.10.10.10 也是所有配置文件模板中元节点IP地址的占位符,执行 配置 时,该IP地址会被作为元节点的实际IP地址

您可以使用单节点沙箱,这种部署下,只有一个元节点meta,节点上部署有完整的基础设施,和一个单例Postgres数据库pg-meta

  • meta 10.10.10.10 pg-meta.pg-meta-1

单节点沙箱则适合用于个人开发、实验、学习;作为数据分析与可视化的环境;以及设计、演示、分发交互式数据应用,四节点沙箱可以完整演示Pigsty的功能,充分探索高可用架构与监控系统的能力,请您自行按需选择。

在四节点沙箱环境中,有三个额外的节点,与一个额外一套三节点PostgreSQL集群 pg-test

  • node-1 10.10.10.11 pg-test.pg-test-1
  • node-2 10.10.10.12 pg-test.pg-test-2
  • node-3 10.10.10.13 pg-test.pg-test-3

同时,沙箱环境还会使用以下两个IP地址与两条静态DNS记录,用于接入数据库集群。

  • 10.10.10.2 pg-meta
  • 10.10.10.2 pg-test

Pigsty提供了基于Vagrant的本地沙箱(使用Virtualbox拉起本地虚拟机),以及基于Terraform的云端沙箱(使用云厂商API创建虚拟机)。

  • 本地沙箱可以在普通Mac/PC上运行,不需要任何费用,但若想在本机运行完整的4节点沙箱环境,您的Mac/PC应当至少有 4C/8G的硬件规格。

  • 云端沙箱可以方便地向他人展示与共享,使用前需要您创建一个云账号,虚拟机资源按需创建使用,用后可以一键销毁,会有一些费用(通常非常便宜,一天几块钱)

沙箱环境部署

Pigsty设计了一个标准的,4节点的演示教学环境,称为沙箱环境,使用Vagrant或Terraform快速在本机或公有云上拉起所需的四台虚拟机资源,并进行部署测试。跑通流程后稍作修改,便可用于生产环境部署

]

以默认的沙箱环境为例,假设您已经在10.10.10.10元节点上完成单机Pigsty的安装:

./infra.yml # 在沙箱环境的 10.10.10.10 meta 机器上,完成完整的单机Pigsty安装

主机初始化

现希望将三个节点:10.10.10.11, 10.10.10.12, 10.10.10.13 纳入管理,则可使用 nodes.yml 剧本:

./nodes.yml -l pg-test      # 初始化集群pg-test包含的三台机器节点(配置节点+纳入监控)

执行完毕后,这三台节点已经带有DCS服务,主机监控与日志收集。可以用于后续的数据库集群部署。详情请参考节点 配置剧本

PostgreSQL部署

使用 pgsql.yml 剧本,可以在这三台节点上初始化一主两从的高可用PostgreSQL数据库集群 pg-test

./pgsql.yml  -l pg-test      # 初始化高可用PGSQL数据库集群pg-test

部署完成后,即可从监控系统 中看到新创建的PostgreSQL集群。

详情请参考:PgSQL数据库集群 配置定制剧本

Redis部署

除了标准的PostgreSQL集群,您还可以部署各种其他类型的集群,甚至其他类型的数据库。

例如在沙箱中部署Redis,可以使用Redis数据库集群 配置剧本

./nodes.yml    # 配置所有用于安装Redis的节点
./redis.yml    # 在所有节点上按照配置声明Redis

MatrixDB部署

例如在沙箱中部署开源数据仓库MatrixDB(Greenplum7),可以使用以下命令:

./configure -m mxdb  # 使用沙箱环境MatrixDB配置文件模板
./download matrix    # 下载MatrixDB软件包并构建本地源
./infra.yml -e no_cmdb=true  # 如果您准备在meta节点上部署 MatrixDB Master,添加no_cmdb选项,否则正常安装即可。   
./nodes.yml                  # 配置所有用于安装MatrixDB的节点
./pigsty-matrixdb.yml        # 在上述节点上安装MatrixDB

4 - 监控系统部署

如何使用Pigsty监控已有的PostgreSQL实例?如RDS for PG

对于由Pigsty所创建的实例,所有监控组件均已自动配置妥当。但对于非Pigsty所创建的现存Pigsty实例,若希望使用Pigsty监控系统的部分对其监控,则需一些额外的配置。

太长;不看

  1. 在目标实例创建监控对象:监控对象配置

  2. 在配置清单中声明该集群:

    pg-test:
      hosts:                                # 为每个实例分配唯一本地端口
        10.10.10.11: { pg_seq: 1, pg_role: primary , pg_exporter_port: 20001}
        10.10.10.12: { pg_seq: 2, pg_role: replica , pg_exporter_port: 20002}
        10.10.10.13: { pg_seq: 3, pg_role: offline , pg_exporter_port: 20003}
      vars:
        pg_cluster: pg-test                 # 填入集群名称
        pg_version: 14                      # 填入数据库大版本
        pg_databases: [{ name: test }]      # 填入数据库列表(每个数据库对象作为一个数组元素)
    
    # 在全局/集群/实例配置中提供监控用户密码 pg_monitor_username/pg_monitor_password
    
  3. 针对该集群执行剧本:./pgsql-monly.yml -l pg-test

  4. 该剧本会在Grafana中注册目标PostgreSQL数据源,因此PGCAT功能完整可用。该剧本会在元节点本地部署PG Exporter监控远程PG实例,故PGSQL中纯数据库相关指标可用。但主机节点、连接池、负载均衡、高可用Patroni相关指标则不可用。

监控部署概述

如果用户只希望使用Pigsty的监控系统部分,比如希望使用Pigsty监控系统监控已有的PostgreSQL实例,那么可以使用 仅监控部署(monitor only) 模式。仅监控模式下,您可以使用Pigsty管理监控其他PostgreSQL实例(目前默认支持10+以上的版本,更老的版本可以通过手工修改 pg_exporter 配置文件支持)

首先,您需要在1台元节点上完成标准的Pigsty的标准安装流程,然后便可以将更多的数据库实例接入监控。按照目标数据库节点的访问权限,又可以分为两种情况:

如果目标节点可被管理

如果目标DB节点可以被Pigsty所管理(ssh可达,sudo可用),那么您可以使用 pgsql.yml 剧本中的pg-exporter任务,使用相同的的方式,在目标节点上部署监控组件:PG Exporter, 您也可以使用该剧本的其他任务,在已有实例节点上部署额外的组件及其监控:连接池Pgbouncer与负载均衡器HAProxy。此外,您也可以使用 nodes.yml 中的 node-exporterpromtail 任务,部署主机节点监控与日志收集组件。从而获得与原生Pigsty数据库实例完全一致的使用体验。

因为目标数据库集群已存在,您需要参考本节的内容手工在目标数据库集群上创建监控用户、模式与扩展。其余流程与完整部署并无区别。

# 修改pigsty配置参数,在节点上添加yum repo,然后通过yum安装软件包
exporter_install: yum # none|yum|binary, none by default
exporter_repo_url: http://<your primary ip address>/pigsty.repo

./nodes.yml -l <yourcluster> -t node-exporter  # 部署节点指标监控
./nodes.yml -l <yourcluster> -t promtail       # 部署节点日志收集
./pgsql.yml -l <yourcluster> -t pg-exporter    # 部署PG指标监控收集

如果只有数据库连接串

如果您只能通过PGURL(数据库连接串)的方式访问目标数据库,则可以考虑使用仅监控模式/精简模式(Monitor Only:Monly)监控目标数据库。在此模式下,所有监控组件均部署在安装Pigsty的元节点上。监控系统不会有 节点,连接池,负载均衡器,高可用组件的相关指标,但数据库本身,以及数据目录(Catalog)中的实时状态信息仍然可用。

为了执行精简监控部署,您同样需要参考本节的内容手工在目标数据库集群上创建监控用户、模式与扩展,并确保可以从元节点上使用监控用户访问目标数据库。此后,针对目标集群执行 pgsql-monly.yml剧本即可完成部署。

本文着重介绍此种监控部署模式

图:仅监控模式架构示意图,部署于管理机本地的多个PG Exporter用于监控多个远程数据库实例。

精简部署与标准部署的区别

Pigsty监控系统由三个核心模块组成:

事项\等级 L1 L2 L3
名称 基础部署 托管部署 完整部署
英文 basic managed full
场景 只有连接串 DB已存在,节点可管理 实例由Pigsty创建
PGCAT功能 ✅ 完整可用 ✅ 完整可用 ✅ 完整可用
PGSQL功能 ✅ 限PG指标 ✅ 限PG与节点指标 ✅ 完整功能
连接池指标 ❌ 不可用 ⚠️ 选装 ✅ 预装项
负载均衡器指标 ❌ 不可用 ⚠️ 选装 ✅ 预装项
PGLOG功能 ❌ 不可用 ⚠️ 选装 ✅ 预装项
PG Exporter ⚠️ 部署于元节点 ✅ 部署于DB节点 ✅ 部署于DB节点
Node Exporter ❌ 不部署 ✅ 部署于DB节点 ✅ 部署于DB节点
侵入DB节点 ✅ 无侵入 ⚠️ 安装Exporter ⚠️ 完全由Pigsty管理
监控现有实例 ✅ 可支持 ✅ 可支持 ❌ 仅用于Pigsty托管实例
监控用户与视图 人工创建 人工创建 Pigsty自动创建
部署使用剧本 pgsql-monly.yml pgsql.yml -t pg-exporter,promtail
nodes.yml -t node-exporter
pgsql.yml -t pg-exporter
nodes.yml -t node-exporter
所需权限 元节点可达的PGURL DB节点ssh与sudo权限 DB节点ssh与sudo权限
功能概述 基础功能:PGCAT+PGSQL 大部分功能 完整功能

监控已有实例:精简模式

为数据库实例部署监控系统分为三步:准备监控对象修改配置清单执行部署剧本

准备监控对象

为了将外部现存PostgreSQL实例纳入监控,您需要有一个可用于访问该实例/集群的连接串。任何可达连接串(业务用户,超级用户)均可使用,但我们建议使用一个专用监控用户以避免权限泄漏。

  • 监控用户:默认使用的用户名为 dbuser_monitor, 该用户需要属于 pg_monitor 角色组,或确保具有相关视图访问权限。
  • 监控认证:默认使用密码访问,您需要确保HBA策略允许监控用户从管理机或DB节点本地访问数据库。
  • 监控模式:固定使用名称 monitor,用于安装额外的监控视图与扩展插件,非必选,但强烈建议创建。
  • 监控扩展:强烈建议启用PG自带的监控扩展 pg_stat_statements

关于监控对象的准备细节,请参考文后:监控对象配置 一节。

修改配置清单

如同部署一个全新的Pigsty实例一样,您需要在配置清单(配置文件或CMDB)中声明该目标集群。例如,为集群与实例指定身份标识。不同之处在于,您还需要在实例层次为每一个实例手工分配一个唯一的本地端口号( pg_exporter_port)。

下面是一个数据库集群声明样例:

pg-test:
  hosts:                                # 为每个实例分配唯一本地端口
    10.10.10.11: { pg_seq: 1, pg_role: primary , pg_exporter_port: 20001}
    10.10.10.12: { pg_seq: 2, pg_role: replica , pg_exporter_port: 20002}
    10.10.10.13: { pg_seq: 3, pg_role: offline , pg_exporter_port: 20003}
  vars:
    pg_cluster: pg-test                 # 填入集群名称
    pg_version: 14                      # 填入数据库大版本
    pg_databases: [{ name: test }]      # 填入数据库列表(每个数据库对象作为一个数组元素)
    
# 在全局/集群/实例配置中提供监控用户密码 pg_monitor_username/pg_monitor_password

注,即使您通过域名访问数据库,依然需要通过填入实际IP地址的方式来声明数据库集群。

若要启用PGCAT功能,您需要显式在 pg_databases 中列出目标集群的数据库名称列表,在此列表中的数据库将被注册为Grafana的数据源,您可以直接通过Grafana访问该实例的Catalog数据。若您不希望使用PGCAT相关功能,不设置该变量,或置为空数组即可。

连接信息

说明:Pigsty将默认使用以下规则生成监控连接串。但参数 pg_exporter_url 存在时,将直接覆盖拼接连接串。

postgres://{{ pg_monitor_username }}:{{ pg_monitor_password }}@{{ inventory_hostname }}:{{ pg_port }}/postgres?sslmode=disable

您可以在全局使用统一的监控用户/密码设置,或者在集群层面实例层次根据实际情况按需配置以下连接参数

pg_monitor_username: dbuser_monitor  # 监控用户名,若使用全局统一配置则无需在此配置
pg_monitor_password: DBUser.Monitor  # 监控用户密码,若使用全局统一配置则无需在此配置
pg_port: 5432                        # 若使用非标准的数据库端口,在此修改
示例:在实例层面指定连接信息 ```yaml pg-test: hosts: # Specify the access URL for the instance 10.10.10.11: pg_seq: 1 pg_role: primary pg_exporter_port: 20001 pg_monitor_username: monitor_user1 pg_monitor_password: monitor_pass1 10.10.10.12: pg_seq: 2 pg_role: replica pg_exporter_port: 20002 # Specify pg_exporter_url directly pg_exporter_url: 'postgres://someuser:pass@rds.pg.hongkong.xxx:5432/postgres?sslmode=disable'' 10.10.10.13: pg_seq: 3 pg_role: offline pg_exporter_port: 20003 pg_monitor_username: monitor_user3 pg_monitor_password: monitor_pass3 vars: pg_cluster: pg-test # Fill in cluster name pg_version: 14 # Fill in the major version of the database pg_databases: [{ name: test }] # Fill in the database list (each database object as an array element) ```

执行部署剧本

集群声明完成后,将其纳入监控非常简单,在元节点上针对目标集群使用剧本 pgsql-monly.yml 即可:

./pgsql-monly.yml -l <cluster>     # 在指定集群上完成监控部署

监控已有实例:托管部署

在托管部署模式下,目标DB节点可以被Pigsty所管理(ssh可达,sudo可用),用户将在已有的节点上加装以下监控组件:promtail, node_exporter, pg_exporter。

您可以使用 nodes.yml中的node-exporter任务,以及 pgsql.yml 剧本中的pg-exporter任务,在目标节点上部署监控组件:node_exporterpg_exporter

因为目标数据库集群已存在,您需要在目标数据库集群上创建监控用户、模式与扩展

# 修改pigsty配置参数,在节点上添加yum repo,然后通过yum安装软件包
exporter_install: yum # none|yum|binary, none by default
exporter_repo_url: http://<your primary ip address>/pigsty.repo

./nodes.yml -l <yourcluster> -t promtail       # 部署节点日志收集(可选,注意日志位置)
./nodes.yml -l <yourcluster> -t node-exporter  # 部署节点指标监控
./pgsql.yml -l <yourcluster> -t pg-exporter    # 部署PG指标监控收集

exporter_install的值为yum时,Pigsty会从 exporter_repo_url 指定的URL下载Repo文件至节点本地的/etc/yum.repos.d中。通常您应当填入管理节点上的Pigsty本地源地址,例如:http://10.10.10.10/pigsty.repo


监控对象配置

如何在已有实例上配置监控所需的用户,模式,扩展、视图与函数。

监控用户

以Pigsty默认使用的监控用户dbuser_monitor为例,在目标数据库集群创建以下用户。

CREATE USER dbuser_monitor;
GRANT pg_monitor TO dbuser_monitor;
COMMENT ON ROLE dbuser_monitor IS 'system monitor user';
ALTER USER dbuser_monitor SET log_min_duration_statement = 1000;
ALTER USER dbuser_monitor PASSWORD 'DBUser.Monitor'; -- 按需修改监控用户密码(建议修改!!)

请注意,这里创建的监控用户与密码需要与 pg_monitor_usernamepg_monitor_password 保持一致。

配置数据库 pg_hba.conf 文件,添加以下规则以允许监控用户从本地,以及管理机使用密码访问数据库。

# allow local role monitor with password
local   all  dbuser_monitor                    md5
host    all  dbuser_monitor  127.0.0.1/32      md5
host    all  dbuser_monitor  <管理机器IP地址>/32 md5

监控模式

监控模式与扩展是可选项,即使没有,Pigsty监控系统的主体也可以正常工作,但我们强烈建议创建监控模式,并至少启用PG官方自带的 pg_stat_statements,该扩展提供了关于查询性能的重要数据。注意:该扩展必须列入数据库参数shared_preload_libraries 中方可生效,修改该参数需要重启数据库。

创建扩展模式:

CREATE SCHEMA IF NOT EXISTS monitor;               -- 创建监控专用模式
GRANT USAGE ON SCHEMA monitor TO dbuser_monitor;   -- 允许监控用户使用

监控扩展

创建扩展插件:

-- 强烈建议启用 pg_stat_statements 扩展
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" WITH SCHEMA "monitor";

-- 可选的其他扩展
CREATE EXTENSION IF NOT EXISTS "pgstattuple" WITH SCHEMA "monitor";
CREATE EXTENSION IF NOT EXISTS "pg_qualstats" WITH SCHEMA "monitor";
CREATE EXTENSION IF NOT EXISTS "pg_buffercache" WITH SCHEMA "monitor";
CREATE EXTENSION IF NOT EXISTS "pageinspect" WITH SCHEMA "monitor";
CREATE EXTENSION IF NOT EXISTS "pg_prewarm" WITH SCHEMA "monitor";
CREATE EXTENSION IF NOT EXISTS "pg_visibility" WITH SCHEMA "monitor";
CREATE EXTENSION IF NOT EXISTS "pg_freespacemap" WITH SCHEMA "monitor";

监控视图

监控视图提供了若干常用的预处理结果,并对某些需要高权限的监控指标进行权限封装(例如共享内存分配),便于查询与使用。强烈建议在所有需要监控的数据库中创建

监控模式与监控视图定义
--==================================================================--
--                            Monitor Schema                        --
--==================================================================--

----------------------------------------------------------------------
-- cleanse
----------------------------------------------------------------------
CREATE SCHEMA IF NOT EXISTS monitor;
GRANT USAGE ON SCHEMA monitor TO dbuser_monitor;
GRANT USAGE ON SCHEMA monitor TO "{{ pg_admin_username }}";
GRANT USAGE ON SCHEMA monitor TO "{{ pg_replication_username }}";

--==================================================================--
--                            Monitor Views                         --
--==================================================================--

----------------------------------------------------------------------
-- Table bloat estimate : monitor.pg_table_bloat
----------------------------------------------------------------------
DROP VIEW IF EXISTS monitor.pg_table_bloat CASCADE;
CREATE OR REPLACE VIEW monitor.pg_table_bloat AS
SELECT CURRENT_CATALOG AS datname, nspname, relname , tblid , bs * tblpages AS size,
       CASE WHEN tblpages - est_tblpages_ff > 0 THEN (tblpages - est_tblpages_ff)/tblpages::FLOAT ELSE 0 END AS ratio
FROM (
         SELECT ceil( reltuples / ( (bs-page_hdr)*fillfactor/(tpl_size*100) ) ) + ceil( toasttuples / 4 ) AS est_tblpages_ff,
                tblpages, fillfactor, bs, tblid, nspname, relname, is_na
         FROM (
                  SELECT
                      ( 4 + tpl_hdr_size + tpl_data_size + (2 * ma)
                          - CASE WHEN tpl_hdr_size % ma = 0 THEN ma ELSE tpl_hdr_size % ma END
                          - CASE WHEN ceil(tpl_data_size)::INT % ma = 0 THEN ma ELSE ceil(tpl_data_size)::INT % ma END
                          ) AS tpl_size, (heappages + toastpages) AS tblpages, heappages,
                      toastpages, reltuples, toasttuples, bs, page_hdr, tblid, nspname, relname, fillfactor, is_na
                  FROM (
                           SELECT
                               tbl.oid AS tblid, ns.nspname , tbl.relname, tbl.reltuples,
                               tbl.relpages AS heappages, coalesce(toast.relpages, 0) AS toastpages,
                               coalesce(toast.reltuples, 0) AS toasttuples,
                               coalesce(substring(array_to_string(tbl.reloptions, ' ') FROM 'fillfactor=([0-9]+)')::smallint, 100) AS fillfactor,
                               current_setting('block_size')::numeric AS bs,
                               CASE WHEN version()~'mingw32' OR version()~'64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END AS ma,
                               24 AS page_hdr,
                               23 + CASE WHEN MAX(coalesce(s.null_frac,0)) > 0 THEN ( 7 + count(s.attname) ) / 8 ELSE 0::int END
                                   + CASE WHEN bool_or(att.attname = 'oid' and att.attnum < 0) THEN 4 ELSE 0 END AS tpl_hdr_size,
                               sum( (1-coalesce(s.null_frac, 0)) * coalesce(s.avg_width, 0) ) AS tpl_data_size,
                               bool_or(att.atttypid = 'pg_catalog.name'::regtype)
                                   OR sum(CASE WHEN att.attnum > 0 THEN 1 ELSE 0 END) <> count(s.attname) AS is_na
                           FROM pg_attribute AS att
                                    JOIN pg_class AS tbl ON att.attrelid = tbl.oid
                                    JOIN pg_namespace AS ns ON ns.oid = tbl.relnamespace
                                    LEFT JOIN pg_stats AS s ON s.schemaname=ns.nspname AND s.tablename = tbl.relname AND s.inherited=false AND s.attname=att.attname
                                    LEFT JOIN pg_class AS toast ON tbl.reltoastrelid = toast.oid
                           WHERE NOT att.attisdropped AND tbl.relkind = 'r' AND nspname NOT IN ('pg_catalog','information_schema')
                           GROUP BY 1,2,3,4,5,6,7,8,9,10
                       ) AS s
              ) AS s2
     ) AS s3
WHERE NOT is_na;
COMMENT ON VIEW monitor.pg_table_bloat IS 'postgres table bloat estimate';

----------------------------------------------------------------------
-- Index bloat estimate : monitor.pg_index_bloat
----------------------------------------------------------------------
DROP VIEW IF EXISTS monitor.pg_index_bloat CASCADE;
CREATE OR REPLACE VIEW monitor.pg_index_bloat AS
SELECT CURRENT_CATALOG AS datname, nspname, idxname AS relname, tblid, idxid, relpages::BIGINT * bs AS size,
       COALESCE((relpages - ( reltuples * (6 + ma - (CASE WHEN index_tuple_hdr % ma = 0 THEN ma ELSE index_tuple_hdr % ma END)
                                               + nulldatawidth + ma - (CASE WHEN nulldatawidth % ma = 0 THEN ma ELSE nulldatawidth % ma END))
                                  / (bs - pagehdr)::FLOAT  + 1 )), 0) / relpages::FLOAT AS ratio
FROM (
         SELECT nspname,idxname,indrelid AS tblid,indexrelid AS idxid,
                reltuples,relpages,
                current_setting('block_size')::INTEGER                                                               AS bs,
                (CASE WHEN version() ~ 'mingw32' OR version() ~ '64-bit|x86_64|ppc64|ia64|amd64' THEN 8 ELSE 4 END)  AS ma,
                24                                                                                                   AS pagehdr,
                (CASE WHEN max(COALESCE(pg_stats.null_frac, 0)) = 0 THEN 2 ELSE 6 END)                               AS index_tuple_hdr,
                sum((1.0 - COALESCE(pg_stats.null_frac, 0.0)) *
                    COALESCE(pg_stats.avg_width, 1024))::INTEGER                                                     AS nulldatawidth
         FROM pg_attribute
                  JOIN (
             SELECT pg_namespace.nspname,
                    ic.relname                                                   AS idxname,
                    ic.reltuples,
                    ic.relpages,
                    pg_index.indrelid,
                    pg_index.indexrelid,
                    tc.relname                                                   AS tablename,
                    regexp_split_to_table(pg_index.indkey::TEXT, ' ') :: INTEGER AS attnum,
                    pg_index.indexrelid                                          AS index_oid
             FROM pg_index
                      JOIN pg_class ic ON pg_index.indexrelid = ic.oid
                      JOIN pg_class tc ON pg_index.indrelid = tc.oid
                      JOIN pg_namespace ON pg_namespace.oid = ic.relnamespace
                      JOIN pg_am ON ic.relam = pg_am.oid
             WHERE pg_am.amname = 'btree' AND ic.relpages > 0 AND nspname NOT IN ('pg_catalog', 'information_schema')
         ) ind_atts ON pg_attribute.attrelid = ind_atts.indexrelid AND pg_attribute.attnum = ind_atts.attnum
                  JOIN pg_stats ON pg_stats.schemaname = ind_atts.nspname
             AND ((pg_stats.tablename = ind_atts.tablename AND pg_stats.attname = pg_get_indexdef(pg_attribute.attrelid, pg_attribute.attnum, TRUE))
                 OR (pg_stats.tablename = ind_atts.idxname AND pg_stats.attname = pg_attribute.attname))
         WHERE pg_attribute.attnum > 0
         GROUP BY 1, 2, 3, 4, 5, 6
     ) est;
COMMENT ON VIEW monitor.pg_index_bloat IS 'postgres index bloat estimate (btree-only)';


----------------------------------------------------------------------
-- Relation Bloat : monitor.pg_bloat
----------------------------------------------------------------------
DROP VIEW IF EXISTS monitor.pg_bloat CASCADE;
CREATE OR REPLACE VIEW monitor.pg_bloat AS
SELECT coalesce(ib.datname, tb.datname)                                                   AS datname,
       coalesce(ib.nspname, tb.nspname)                                                   AS nspname,
       coalesce(ib.tblid, tb.tblid)                                                       AS tblid,
       coalesce(tb.nspname || '.' || tb.relname, ib.nspname || '.' || ib.tblid::RegClass) AS tblname,
       tb.size                                                                            AS tbl_size,
       CASE WHEN tb.ratio < 0 THEN 0 ELSE round(tb.ratio::NUMERIC, 6) END                 AS tbl_ratio,
       (tb.size * (CASE WHEN tb.ratio < 0 THEN 0 ELSE tb.ratio::NUMERIC END)) ::BIGINT    AS tbl_wasted,
       ib.idxid,
       ib.nspname || '.' || ib.relname                                                    AS idxname,
       ib.size                                                                            AS idx_size,
       CASE WHEN ib.ratio < 0 THEN 0 ELSE round(ib.ratio::NUMERIC, 5) END                 AS idx_ratio,
       (ib.size * (CASE WHEN ib.ratio < 0 THEN 0 ELSE ib.ratio::NUMERIC END)) ::BIGINT    AS idx_wasted
FROM monitor.pg_index_bloat ib
         FULL OUTER JOIN monitor.pg_table_bloat tb ON ib.tblid = tb.tblid;

COMMENT ON VIEW monitor.pg_bloat IS 'postgres relation bloat detail';


----------------------------------------------------------------------
-- monitor.pg_index_bloat_human
----------------------------------------------------------------------
DROP VIEW IF EXISTS monitor.pg_index_bloat_human CASCADE;
CREATE OR REPLACE VIEW monitor.pg_index_bloat_human AS
SELECT idxname                            AS name,
       tblname,
       idx_wasted                         AS wasted,
       pg_size_pretty(idx_size)           AS idx_size,
       round(100 * idx_ratio::NUMERIC, 2) AS idx_ratio,
       pg_size_pretty(idx_wasted)         AS idx_wasted,
       pg_size_pretty(tbl_size)           AS tbl_size,
       round(100 * tbl_ratio::NUMERIC, 2) AS tbl_ratio,
       pg_size_pretty(tbl_wasted)         AS tbl_wasted
FROM monitor.pg_bloat
WHERE idxname IS NOT NULL;
COMMENT ON VIEW monitor.pg_index_bloat_human IS 'postgres index bloat info in human-readable format';

----------------------------------------------------------------------
-- monitor.pg_table_bloat_human
----------------------------------------------------------------------
DROP VIEW IF EXISTS monitor.pg_table_bloat_human CASCADE;
CREATE OR REPLACE VIEW monitor.pg_table_bloat_human AS
SELECT tblname                                          AS name,
       idx_wasted + tbl_wasted                          AS wasted,
       pg_size_pretty(idx_wasted + tbl_wasted)          AS all_wasted,
       pg_size_pretty(tbl_wasted)                       AS tbl_wasted,
       pg_size_pretty(tbl_size)                         AS tbl_size,
       tbl_ratio,
       pg_size_pretty(idx_wasted)                       AS idx_wasted,
       pg_size_pretty(idx_size)                         AS idx_size,
       round(idx_wasted::NUMERIC * 100.0 / idx_size, 2) AS idx_ratio
FROM (SELECT datname,
             nspname,
             tblname,
             coalesce(max(tbl_wasted), 0)                         AS tbl_wasted,
             coalesce(max(tbl_size), 1)                           AS tbl_size,
             round(100 * coalesce(max(tbl_ratio), 0)::NUMERIC, 2) AS tbl_ratio,
             coalesce(sum(idx_wasted), 0)                         AS idx_wasted,
             coalesce(sum(idx_size), 1)                           AS idx_size
      FROM monitor.pg_bloat
      WHERE tblname IS NOT NULL
      GROUP BY 1, 2, 3
     ) d;
COMMENT ON VIEW monitor.pg_table_bloat_human IS 'postgres table bloat info in human-readable format';



----------------------------------------------------------------------
-- Activity Overview: monitor.pg_session
----------------------------------------------------------------------
DROP VIEW IF EXISTS monitor.pg_session CASCADE;
CREATE OR REPLACE VIEW monitor.pg_session AS
SELECT coalesce(datname, 'all') AS datname, numbackends, active, idle, ixact, max_duration, max_tx_duration, max_conn_duration
FROM (
         SELECT datname,
                count(*)                                         AS numbackends,
                count(*) FILTER ( WHERE state = 'active' )       AS active,
                count(*) FILTER ( WHERE state = 'idle' )         AS idle,
                count(*) FILTER ( WHERE state = 'idle in transaction'
                    OR state = 'idle in transaction (aborted)' ) AS ixact,
                max(extract(epoch from now() - state_change))
                FILTER ( WHERE state = 'active' )                AS max_duration,
                max(extract(epoch from now() - xact_start))      AS max_tx_duration,
                max(extract(epoch from now() - backend_start))   AS max_conn_duration
         FROM pg_stat_activity
         WHERE backend_type = 'client backend'
           AND pid <> pg_backend_pid()
         GROUP BY ROLLUP (1)
         ORDER BY 1 NULLS FIRST
     ) t;
COMMENT ON VIEW monitor.pg_session IS 'postgres activity group by session';


----------------------------------------------------------------------
-- Sequential Scan: monitor.pg_seq_scan
----------------------------------------------------------------------
DROP VIEW IF EXISTS monitor.pg_seq_scan CASCADE;
CREATE OR REPLACE VIEW monitor.pg_seq_scan AS
    SELECT schemaname                                                        AS nspname,
           relname,
           seq_scan,
           seq_tup_read,
           seq_tup_read / seq_scan                                           AS seq_tup_avg,
           idx_scan,
           n_live_tup + n_dead_tup                                           AS tuples,
           round(n_live_tup * 100.0::NUMERIC / (n_live_tup + n_dead_tup), 2) AS live_ratio
    FROM pg_stat_user_tables
    WHERE seq_scan > 0
      and (n_live_tup + n_dead_tup) > 0
    ORDER BY seq_scan DESC;
COMMENT ON VIEW monitor.pg_seq_scan IS 'table that have seq scan';
查看共享内存分配的函数(PG13以上可用)
DROP FUNCTION IF EXISTS monitor.pg_shmem() CASCADE;
CREATE OR REPLACE FUNCTION monitor.pg_shmem() RETURNS SETOF
   pg_shmem_allocations AS $$ SELECT * FROM pg_shmem_allocations;$$ LANGUAGE SQL SECURITY DEFINER;
COMMENT ON FUNCTION monitor.pg_shmem() IS 'security wrapper for pg_shmem';

5 - 安全考量

生产网段隔离 + Pigsty默认的权限模型通常足以满足一般安全要求

Pigsty依赖的工作假设是,部署位于内网工作环境。

如果您在带有公网IP网卡的机器上安装部署Pigsty,需要特别注意网络防火墙相关配置。

通常建议您开启外网网卡上的防火墙进行端口过滤,只允许 80 端口的Web服务入站流量,请尽可能避免直接通过公网端口使用数据库。

依赖内网网络安全,辅以Pigsty默认的权限模型,通常足以满足一般安全要求。

通常不建议您自行折腾CA,SSL,除非您真的知道自己在做什么。

网络安全

  • 确保生产网段与开发、公网隔离
  • 确保元节带有合理的访问控制机制(严格限制仅DBA可登录)

数据库安全

元数据安全

  • 限制Consul UI界面访问权限
  • 限制ETCD访问
  • 为Consul启用SSL
  • 为ETCD启用SSL

6 - 部署样例

在实际环境中部署Pigsty的几个例子

6.1 - Vagrant沙箱环境

针对本地Vagrant沙箱的Pigsty配置示例

Pigsty自带一个基于Vagrant的沙箱环境

该沙箱所使用的配置文件即Pigsty根目录中的 pigsty.yml ,Github原地址为:https://github.com/Vonng/pigsty/blob/master/pigsty.yml

该配置文件可作为一个标准的学习样例,例如使用相同规格的虚拟机环境部署时,通常只需要在这份配置文件的基础上进行极少量修改就可以直接使用。

6.2 - 腾讯云VPC部署

使用腾讯云VPC虚拟机部署Pigsty

本样例将基于腾讯云VPC部署Pigsty

资源准备

申请虚拟机

买几台虚拟机,如下图所示,其中11这一台作为元节点,带有公网IP,数据库节点3台,普通1核1G即可。

配置SSH远程登录

现在假设我们的管理用户名为vonng,就是我啦!现在首先配置我在元节点上到其他三台节点的ssh免密码访问。

# vonng@172.21.0.11           # meta
ssh-copy-id root@172.21.0.3   # pg-test-1
ssh-copy-id root@172.21.0.4   # pg-test-2
ssh-copy-id root@172.21.0.16  # pg-test-3
scp ~/.ssh/id_rsa.pub root@172.21.0.3:/tmp/
scp ~/.ssh/id_rsa.pub root@172.21.0.4:/tmp/
scp ~/.ssh/id_rsa.pub root@172.21.0.16:/tmp/
ssh root@172.21.0.3 'useradd vonng; mkdir -m 700 -p /home/vonng/.ssh; mv /tmp/id_rsa.pub /home/vonng/.ssh/authorized_keys; chown -R vonng /home/vonng; chmod 0600 /home/vonng/.ssh/authorized_keys;'
ssh root@172.21.0.4 'useradd vonng; mkdir -m 700 -p /home/vonng/.ssh; mv /tmp/id_rsa.pub /home/vonng/.ssh/authorized_keys; chown -R vonng /home/vonng; chmod 0600 /home/vonng/.ssh/authorized_keys;'
ssh root@172.21.0.16 'useradd vonng; mkdir -m 700 -p /home/vonng/.ssh; mv /tmp/id_rsa.pub /home/vonng/.ssh/authorized_keys; chown -R vonng /home/vonng; chmod 0600 /home/vonng/.ssh/authorized_keys;'

然后配置该用户免密码执行sudo的权限:

ssh root@172.21.0.3  "echo '%vonng ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vonng"
ssh root@172.21.0.4  "echo '%vonng ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vonng"
ssh root@172.21.0.16 "echo '%vonng ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/vonng"

# 校验配置是否成功
ssh 172.21.0.3 'sudo ls'
ssh 172.21.0.4 'sudo ls'
ssh 172.21.0.16 'sudo ls'

下载项目

# 从Github克隆代码
git clone https://github.com/Vonng/pigsty

# 如果您不能访问Github,也可以使用Pigsty CDN下载代码包
curl http://pigsty-1304147732.cos.accelerate.myqcloud.com/latest/pigsty.tar.gz -o pigsty.tgz && tar -xf pigsty.tgz && cd pigsty 

下载离线安装包

# 从Github Release页面下载
# https://github.com/Vonng/pigsty

# 如果您不能访问Github,也可以使用Pigsty CDN下载离线软件包
curl http://pigsty-1304147732.cos.accelerate.myqcloud.com/latest/pkg.tgz -o files/pkg.tgz

# 将离线安装包解压至元节点指定位置 (也许要sudo)
mv -rf /www/pigsty /www/pigsty-backup && mkdir -p /www/pigsty
tar -xf files/pkg.tgz --strip-component=1 -C /www/pigsty/

调整配置

我们可以基于Pigsty沙箱的配置文件进行调整。因为都是普通低配虚拟机,因此不需要任何实质配置修改,只需要修改连接参数与节点信息即可。简单的说,只要改IP地址就可以了!

现在将沙箱中的IP地址全部替换为云环境中的实际IP地址。(如果使用了L2 VIP,VIP也需要替换为合理的地址)

说明 沙箱IP 虚拟机IP
元节点 10.10.10.10 172.21.0.11
数据库节点1 10.10.10.11 172.21.0.3
数据库节点2 10.10.10.12 172.21.0.4
数据库节点3 10.10.10.13 172.21.0.16
pg-meta VIP 10.10.10.2 172.21.0.8
pg-test VIP 10.10.10.3 172.21.0.9

编辑配置文件:pigsty.yml,如果都是规格差不多的虚拟机,通常您只需要修改IP地址即可。特别需要注意的是在沙箱中我们是通过SSH Alias来连接的(诸如meta, node-1之类),记得移除所有ansible_host配置,我们将直接使用IP地址连接目标节点。

cat pigsty.yml | \
	sed 's/10.10.10.10/172.21.0.11/g' |\
	sed 's/10.10.10.11/172.21.0.3/g' |\
	sed 's/10.10.10.12/172.21.0.4/g' |\
	sed 's/10.10.10.13/172.21.0.16/g' |\
	sed 's/10.10.10.2/172.21.0.8/g' |\
	sed 's/10.10.10.3/172.21.0.9/g' |\
	sed 's/10.10.10.3/172.21.0.9/g' |\
	sed 's/, ansible_host: meta//g' |\
	sed 's/ansible_host: meta//g' |\
	sed 's/, ansible_host: node-[123]//g' |\
	sed 's/vip_interface: eth1/vip_interface: eth0/g' |\
	sed 's/vip_cidrmask: 8/vip_cidrmask: 24/g' > pigsty2.yml
mv pigsty.yml pigsty-backup.yml; mv pigsty2.yml pigsty.yml

就这?

是的,配置文件已经修改完了!我们可以看看到底修改了什么东西

$ diff pigsty.yml pigsty-backup.yml
38c38
<       hosts: {172.21.0.11: {}}
---
>       hosts: {10.10.10.10: {ansible_host: meta}}
46c46
<         172.21.0.11: {pg_seq: 1, pg_role: primary}
---
>         10.10.10.10: {pg_seq: 1, pg_role: primary, ansible_host: meta}
109,111c109,111
<         vip_address: 172.21.0.8             # virtual ip address
<         vip_cidrmask: 24                     # cidr network mask length
<         vip_interface: eth0                 # interface to add virtual ip
---
>         vip_address: 10.10.10.2             # virtual ip address
>         vip_cidrmask: 8                     # cidr network mask length
>         vip_interface: eth1                 # interface to add virtual ip
120,122c120,122
<         172.21.0.3: {pg_seq: 1, pg_role: primary}
<         172.21.0.4: {pg_seq: 2, pg_role: replica}
<         172.21.0.16: {pg_seq: 3, pg_role: offline}
---
>         10.10.10.11: {pg_seq: 1, pg_role: primary, ansible_host: node-1}
>         10.10.10.12: {pg_seq: 2, pg_role: replica, ansible_host: node-2}
>         10.10.10.13: {pg_seq: 3, pg_role: offline, ansible_host: node-3}
147,149c147,149
<         vip_address: 172.21.0.9             # virtual ip address
<         vip_cidrmask: 24                     # cidr network mask length
<         vip_interface: eth0                 # interface to add virtual ip
---
>         vip_address: 10.10.10.3             # virtual ip address
>         vip_cidrmask: 8                     # cidr network mask length
>         vip_interface: eth1                 # interface to add virtual ip
326c326
<       - 172.21.0.11 yum.pigsty
---
>       - 10.10.10.10 yum.pigsty
329c329
<       - 172.21.0.11
---
>       - 10.10.10.10
393c393
<       - server 172.21.0.11 iburst
---
>       - server 10.10.10.10 iburst
417,430c417,430
<       - 172.21.0.8  pg-meta                       # sandbox vip for pg-meta
<       - 172.21.0.9  pg-test                       # sandbox vip for pg-test
<       - 172.21.0.11 meta-1                        # sandbox node meta-1 (node-0)
<       - 172.21.0.3 node-1                        # sandbox node node-1
<       - 172.21.0.4 node-2                        # sandbox node node-2
<       - 172.21.0.16 node-3                        # sandbox node node-3
<       - 172.21.0.11 pigsty
<       - 172.21.0.11 y.pigsty yum.pigsty
<       - 172.21.0.11 c.pigsty consul.pigsty
<       - 172.21.0.11 g.pigsty grafana.pigsty
<       - 172.21.0.11 p.pigsty prometheus.pigsty
<       - 172.21.0.11 a.pigsty alertmanager.pigsty
<       - 172.21.0.11 n.pigsty ntp.pigsty
<       - 172.21.0.11 h.pigsty haproxy.pigsty
---
>       - 10.10.10.2  pg-meta                       # sandbox vip for pg-meta
>       - 10.10.10.3  pg-test                       # sandbox vip for pg-test
>       - 10.10.10.10 meta-1                        # sandbox node meta-1 (node-0)
>       - 10.10.10.11 node-1                        # sandbox node node-1
>       - 10.10.10.12 node-2                        # sandbox node node-2
>       - 10.10.10.13 node-3                        # sandbox node node-3
>       - 10.10.10.10 pigsty
>       - 10.10.10.10 y.pigsty yum.pigsty
>       - 10.10.10.10 c.pigsty consul.pigsty
>       - 10.10.10.10 g.pigsty grafana.pigsty
>       - 10.10.10.10 p.pigsty prometheus.pigsty
>       - 10.10.10.10 a.pigsty alertmanager.pigsty
>       - 10.10.10.10 n.pigsty ntp.pigsty
>       - 10.10.10.10 h.pigsty haproxy.pigsty
442c442
<     grafana_url: http://admin:admin@172.21.0.11:3000 # grafana url
---
>     grafana_url: http://admin:admin@10.10.10.10:3000 # grafana url
478,480c478,480
<       meta-1: 172.21.0.11                         # you could use existing dcs cluster
<       # meta-2: 172.21.0.3                       # host which have their IP listed here will be init as server
<       # meta-3: 172.21.0.4                       # 3 or 5 dcs nodes are recommend for production environment
---
>       meta-1: 10.10.10.10                         # you could use existing dcs cluster
>       # meta-2: 10.10.10.11                       # host which have their IP listed here will be init as server
>       # meta-3: 10.10.10.12                       # 3 or 5 dcs nodes are recommend for production environment
692c692
<           - host    all     all                         172.21.0.11/32      md5
---
>           - host    all     all                         10.10.10.10/32      md5

执行剧本

您可以使用同样的 沙箱初始化 来完成 基础设施和数据库集群的初始化。

其输出结果除了IP地址,与沙箱并无区别。参考输出

访问Demo

现在,您可以通过公网IP访问元节点上的服务了!请注意做好信息安全工作。

与沙箱环境不同的是,如果您需要从公网访问Pigsty管理界面,需要自己把定义的域名写入/etc/hosts中,或者使用真正申请的域名。

否则就只能通过IP端口直连的方式访问,例如: http://<meta_node_public_ip>:3000

Nginx监听的域名可以通过可以通过 nginx_upstream 选项。

nginx_upstream:
  - { name: home,          host: pigsty.cc,   url: "127.0.0.1:3000"}
  - { name: consul,        host: c.pigsty.cc, url: "127.0.0.1:8500" }
  - { name: grafana,       host: g.pigsty.cc, url: "127.0.0.1:3000" }
  - { name: prometheus,    host: p.pigsty.cc, url: "127.0.0.1:9090" }
  - { name: alertmanager,  host: a.pigsty.cc, url: "127.0.0.1:9093" }
  - { name: haproxy,       host: h.pigsty.cc, url: "127.0.0.1:9091" }

6.3 - 生产环境部署

基于高规格硬件执行生产环境部署