跳转到主要内容

于 2025年04月22日 摘录自 Managing multiple Postfix instances on a single host

概述

本文件是使用postmulti(1)实例管理器在单个主机上管理多个Postfix实例的指南。Postfix 2.6 及更高版本支持多实例功能。有关实例管理框架的背景信息以及如何部署自定义实例管理器的详细说明,请参阅 postfix-wrapper(5) 手册页。

本文档涵盖的主题:

为什么需要多个 Postfix 实例

Postfix 是一个通用邮件系统,可配置以满足各种需求。Postfix 的应用示例包括:

  • 为 shell 用户和系统进程提交本地邮件。
  • 从互联网接收(MX 主机)的入站邮件。
  • 企业网络的出站邮件中继。
  • 漫游用户的认证提交。
  • 内容过滤邮件(过滤前/后)。

单个 Postfix 配置可提供上述部分或全部服务,但可能需要复杂的设置交互,例如通过 master.cf 选项覆盖 main.cf 设置。在本文件中,我们认为使用多个 Postfix 实例可能是配置多功能 Postfix 系统的更简单方法。多个 Postfix 实例各自拥有独立的配置目录、队列文件和数据文件,但共享所有 Postfix 程序文件和文档文件。

由于没有单一的正确配置方式,我们建议您选择最适合自己的方案。如果不同 Postfix 服务不涉及不兼容的 main.cfmaster. cf 設定,且可透過簡單方式整合,則單一整合式設定可能是最簡潔的方案。

Postfix 支援多執行個體的目的是提供選擇,而非強制您建立多個 Postfix 執行個體。多執行個體讓您能將每個 Postfix 執行個體調校至專精單一任務,並將執行個體整合為完整系統。

随着 postmulti(1) 工具的引入,以及次要 Postfix 实例的配置占用空间减少到仅需一个 main. cfmaster.cf 文件(其他文件现在位于共享位置),我们希望多个实例的使用比以往任何时候都更加便捷。

空客户端实例与服务实例

在多实例配置 Postfix 的方法中,第一个简化是默认的本地提交 Postfix 实例。

大多数 UNIX 系统需要支持通过 sendmail(1) 命令提交电子邮件,以便系统进程(如 cron 任务)发送状态报告,以及系统用户通过命令行工具发送电子邮件。此类邮件可通过 Postfix 的 null-client 配置进行处理,该配置将所有邮件转发至中央邮件枢纽。null 客户端通常不会运行 SMTP 监听器(master_service_disable = inet),或仅监听回环接口(inet_interfaces = loopback-only)。

在实现用于入站互联网邮件、出站 MTA、内部邮件中继等专用服务器时,我们建议使用空客户端进行本地提交,并创建单功能的 Postfix 次要实例以满足专用需求。

注意:通常,在同一主机上运行多个实例时,需要使用不同的 "myhostname" 设置。否则,当一个实例尝试向另一个实例发送邮件时,会触发错误的"邮件循环回自身"警报。通常,空客户端实例将使用系统的主机名,而其他实例将使用各自专用的"myhostname"设置。当实例之间使用除 SMTP 以外的协议发送邮件,或通过除 25 以外的 TCP 端口使用 SMTP(如基于 SMTP 的内容过滤器常见情况)时,无需使用不同名称。

多实例操作指南

在讨论多实例操作的细节之前,我们先展示创建边界邮件服务器的步骤。该服务器包含一个用于本地提交的空客户端 Postfix 实例、一个用于接收来自互联网邮件的输入 Postfix 实例,以及一个 高级 SMTP 内容过滤器和一个用于将过滤后的邮件发送至内部目的地的输出 Postfix 实例。

配置空客户端 Postfix 实例

在边界邮件枢纽上,来自互联网的邮件需要严格审查,而本地提交的邮件通常仅限于 cron 任务和其他系统服务生成的邮件。在此方面,边界 MTA 与环境中的其他 Unix 主机并无不同。因此,它将本地生成的邮件提交到内部邮件枢纽。我们从构建边界邮件服务器的默认实例开始,该实例将是一个本地提交的空客户端

/etc/postfix/main.cf:
# 我们是 mta1.example.com
#
myhostname = mta1.example.com
mydomain = example.com
# 示例.com 中的扁平用户账户命名空间:
#
# [email protected] 而不是 [email protected]
#
myorigin = $mydomain
# Postfix 2.6+,禁用 inet 服务,特别是禁用 smtpd(8)
#
master_service_disable = inet
# 禁用本地投递:
#
mydestination =
local_transport = error:5.1.1 邮箱不可用
alias_database =
alias_maps =
local_recipient_maps =
# 将所有邮件转发至内部邮件中继服务器
#
relayhost = [mailhub.example.com]
# 索引表宏:
# (当 cdb 不可用时使用 "hash", ...)
#
default_database_type = cdb
索引 = ${default_database_type}:${config_directory}/
# 暴露来自 "root" 等用户的邮件的源主机
#
smtp_generic_maps = ${indexed}generic
# 将发送到 "root" 等用户的邮件转发给 MTA 支持团队
#
virtual_alias_maps = ${indexed}virtual
/etc/postfix/generic:
# 智能主机支持 "+" 地址(recipient_delimiter = +)。
# 来自 "root" 的邮件会暴露源主机,且不返回回复
# 及退信至同一主机。
#
# 在集群 MTA 中,此文件通常从
# 模板文件自动生成。生成过程将模板展开为
# "mtaadmin+root=mta1"
#
root mtaadmin+root=mta1
/etc/postfix/virtual:
# 管理员别名:
#
root mtaadmin
postmaster root

通常还需添加一个 Makefile,以便在源文件更改时自动运行 postmap(1) 命令。此 Makefile 还会在不存在时创建一个"通用"数据库。

/etc/postfix/Makefile:
MTAADMIN=mtaadmin
all: virtual.cdb generic.cdb
generic: Makefile
@echo 创建 $@
@rm -f [email protected]
@printf '%s\t%s+root=%s\n' root ${MTAADMIN} `uname -n` > [email protected]
@mv [email protected] generic
%.cdb: %
postmap cdb:$<;

构建"虚拟"和"通用"数据库(后者通过运行"make"创建),然后启动并测试空客户端:

# cd /etc/postfix; make
# postfix start
# sendmail -i -f root -t <;<;EOF
From: root
To: root
Subject: test
testing
EOF

测试消息应发送给"mtaadmin"地址组(或您选择的任何地址组)的成员,并带有以下标头:

From: [email protected]
To: [email protected]
Subject: test

设置"输出"Postfix实例

在移除 null-client 实例后,我们可以创建 MTA "output" 实例,用于将过滤后的邮件发送至内部网络。我们首先添加 "output" 实例,因为输出实例必须在输入实例完全测试前启动,且系统启动时 "output" 实例必须先于输入实例启动。我们将输出和输入实例放入名为 "mta" 的单一实例组中。

仅在添加第一个次要实例时,在默认(空客户端)实例中启用多实例支持:

# postmulti -e init

然后创建输出实例:

 ;

# postmulti -I postfix-out -G mta -e create

实例配置目录默认为 /etc/postfix-out,更准确地说,是默认实例配置目录父目录下的 "postfix-out" 子目录。新实例将以 "disabled" 状态创建:

/etc/postfix-out/main.cf
#
# ... "默认" main.cf 设置 ...
#
multi_instance_name = postfix-out
queue_directory = /var/spool/postfix-out
data_directory = /var/lib/postfix-out
#
multi_instance_enable = no
master_service_disable = inet
authorized_submit_users =

此实例有一个"默认"master.cf 文件,其队列和数据目录也命名为"postfix-out",并将位于与默认实例对应目录相同的父目录中(例如 /var/spool/postfix-out 和 /var/lib/postfix-out)。

虽然此实例可立即安全启动,但尚未进行有用配置。需根据其作为后过滤器重新注入 SMTP 服务的作用进行自定义。典型添加内容包括:

/etc/postfix-out/master.cf:
# 将默认的 "smtp inet" 条目替换为监听端口 10026 的条目。
127.0.0.1:10026 inet n - n - - smtpd
/etc/postfix-out/main.cf
# ...
# 如果内部不使用 IPv6,请注释掉以下内容
# inet_protocols = ipv4
inet_interfaces = loopback-only
mynetworks_style = host
smtpd_authorized_xforward_hosts = $mynetworks
# 不要通过 anvil(8) 控制重新注入端口。
#
smtpd_client_connection_count_limit = 0
smtpd_client_event_limit_exceptions = $mynetworks
# 当 inet_interfaces 设置时,这是最佳实践,因为这不是
# "次要 IP 配置" 设置。
#
smtp_bind_address = 0.0.0.0
# 所有标头重写操作均在上游进行
#
local_header_rewrite_clients =
# 边界网关不进行本地投递
#
mydestination =
alias_maps =
alias_database =
local_recipient_maps =
local_transport = error:5.1.1 邮箱不可用
# 可能需要一个 recipient_delimiter 用于按用户查找传输映射:
#
recipient_delimiter = +
# 仅一个(无限制客户端)
# 在多个实例中,通常无需使用 "-o param=value" 覆盖
# 在 master.cf 中,每个实例都有自己的 main.cf 文件。
#
# Postfix 2.10 及更高版本:指定空的 smtpd_relay_restrictionssmtpd_relay_restrictions =
smtpd_recipient_restrictions = permit_mynetworks, reject
# 允许内容过滤器偶尔出现高延迟。
#
smtpd_timeout = 1200s
# 建议留空,所有父域匹配需显式指定。
#
parent_domain_matches_subdomains =
# 对于入站邮件使用 "relay" 传输,对于出站邮件(退信等)使用默认
# "smtp" 传输。后者
# 不会占用前者发送代理的槽位。
#
relay_domains = example.com, .example.com
# 启用 xforward 时,匹配输入实例设置,若
# 希望为 "yes",则两者均设为 "yes"。
#
smtpd_client_port_logging = no
# 传输设置 ...
# 消息大小限制
# "relay" 和 "smtp" 传输的并发调优
# ...

配置 "output" 后,启用并启动实例:

1 # postmulti -i postfix-out -x postconf -e \
2 "master_service_disable =" "authorized_submit_users = root"
3 # postmulti -i postfix-out -e enable
4 # postmulti -i postfix-out -p start

这使用 postmulti(1) 命令在输出实例的上下文中(MAIL_CONFIG=/etc/postfix-out)调用 postconf(1)

  • 第 1-2 行: 通过设置 "authorized_submit_users = root",超级用户可以使用 "postmulti -i postfix-out -x sendmail -bv recipient..." 测试 postfix-out 实例,否则本地提交仍被禁用。
  • 第1-2行:当"master_service_disable ="时,"inet"监听器将重新启用。
  • 第3行:输出实例已启用多实例启动/停止功能。
  • 第4行:启动输出实例。

通过"sendmail -bv"和"telnet"提交探针消息测试输出实例。对于生产系统,应在实验室系统上进行深入配置测试。上述简单测试仅确认已知良好的配置已成功部署。

设置内容过滤代理

输出实例准备就绪后,部署内容过滤代理。大多数代理需要自己的 /etc/rc* 启动/停止脚本。然而,部分代理由 Postfix spawn(8) 服务启动,此时需将相应的 spawn(8) 条目添加到输出实例的 master.cf 文件中。

配置代理以监听 127.0.0.1:10025,并将过滤后的邮件重新注入到 127.0.0.1:10026。如有必要,启动代理服务,然后通过 "telnet" 或自动 SMTP 注入器测试代理。代理应支持以下 ESMTP 功能: DSN、8BITMIME 和 XFORWARD。此外,代理应支持在单个 SMTP 会话中进行多次邮件投递。

设置输入 Postfix 实例

输入 Postfix 实例从网络接收邮件并通过内容过滤器转发。现在我们创建输入实例,该实例也属于 "mta" 实例组:

# postmulti -I postfix-in -G mta -e create

新实例的配置目录默认为 /etc/postfix-in,更准确地说,是默认实例配置目录父目录下的 "postfix-in" 子目录。新实例将以 "disabled" 状态创建:

/etc/postfix-in/main.cf
#
# ... "stock" main.cf 设置 ...
#
multi_instance_name = postfix-in
queue_directory = /var/spool/postfix-in
data_directory = /var/lib/postfix-in
#
multi_instance_enable = no
master_service_disable = inet
authorized_submit_users =

与之前一样,对 main.cfmaster.cf 进行适当修改,以使实例准备就绪。建议在部署后的前几个小时内将 "soft_bounce" 设置为 "yes",以便排查任何意外问题。

手动测试可从以下步骤开始:

/etc/postfix-in/main.cf
# 仅接受本地流量,但允许冒充:
inet_interfaces = 127.0.0.1
smtpd_authorized_xclient_hosts = 127.0.0.1

这允许您使用 Postfix 特定的 XCLIENT SMTP 命令,在任何远程系统能够连接之前,安全地模拟来自远程系统的连接。如果测试结果良好,将上述设置恢复为所需的生产值。预过滤器输入实例中的典型设置包括:

/etc/postfix-in/main.cf
#
# ... 
#
# 边界网关不进行本地投递
#
mydestination =
alias_maps =
alias_database =
local_recipient_maps =
local_transport = error:5.1.1 邮箱不可用
# 不要重写远程头部
#
local_header_rewrite_clients =
# 所有尚未过滤的邮件的收件人都一起发送到同一个过滤器。
#
# 在有多个实例的情况下,内容过滤器通过传输设置指定
# 通过传输设置而非 "content_filter" 传输
# 覆盖开关!在此处过滤器监听本地端口 10025。
#
# 若需将部分用户或收件人域直接路由到
# 输出实例并绕过过滤器,只需定义一个传输表
# 并添加相应条目。
#
default_transport = smtp:[127.0.0.1]:10025
relay_transport = $default_transport
虚拟传输 = $默认传输
transport_maps =
# 通过过滤器传递原始客户端日志信息。
#
smtp_send_xforward_command = yes
# 避免拆分信封并多次扫描消息。
# 匹配重新注入服务器的收件人限制。
#
smtp_destination_recipient_limit = 1000
# 允许内容过滤器偶尔出现高延迟。
#
smtp_data_done_timeout = 1200s
# 启用 xforward 时,与输出实例设置匹配,如果
# 希望为 "yes",则两者均设置为 "yes"。
#
smtpd_client_port_logging = no
# ... 许多入站 MX 主机的设置 ...

配置好 "input" 实例后,启用并启动它:

# postmulti -i postfix-in -x postconf -e \
"master_service_disable =" "authorized_submit_users = root"
# postmulti -i postfix-in -e enable
# postmulti -i postfix-in -p start

就这样。现在您已经配置了3个实例。一个空客户端将所有本地提交的邮件发送到内部邮件中继,以及一对"mta"实例,它们从互联网接收邮件,通过内容过滤器转发,然后将其交付到内部目的地。

运行 "postfix start" 或 "postfix stop" 将启动/停止所有三个 Postfix 实例。您可以使用 "postfix -c /config/path start" 启动单个实例,或通过 postmulti(1) 指定实例名称(或实例组名称):

# postmulti -i - -p stop
# postmulti -g mta -p status
# postmulti -i postfix-out -p flush
# postmulti -i postfix-in -p reload
# ...

此示例结束了多实例的"演示流程"。本文档的其余部分将提供 Postfix 多实例支持功能和选项的背景信息。

Postfix 系统的组成部分

Postfix 系统由以下组件构成:

所有实例共享的组件:

每个实例专有:

上述 Postfix 配置参数统称为"安装参数"。其默认值在从源代码编译 Postfix 软件时设置,除一个参数外,其余均可通过 main.cf 文件可选设置为非默认值。唯一一个无法在 main. cf 中无法设置的唯一参数是 $config_directory,因为该参数定义了 main.cf 文件的存储位置。

尽管 config_directory 无法在 main. cf 中设置,但 postfix(1) 以及大多数其他命令行 Postfix 工具允许您通过命令行选项(通常为 -c)或通过 MAIL_CONFIG 环境变量指定非默认配置目录。这样,可以在同一台机器上拥有多个配置目录,并运行多个 master(8) 守护进程,每个守护进程都有自己的配置文件、队列目录和数据目录。

这些正在运行的master(8)实例共享基础 Postfix 软件。它们不共享(且无法共享)配置目录、队列目录或数据目录。

每个配置目录与队列目录和数据目录(在对应的 main.cf 文件中指定)的组合构成一个 Postfix 实例

默认 Postfix 实例

有一个 Postfix 实例是特殊的:这是配置目录为 Postfix 工具中编译的默认目录的实例。默认配置目录的位置通常为 /etc/postfix,可通过 "postconf -d config_directory" 命令查询。我们称具有此配置目录的实例为"默认实例"。

默认实例负责本地邮件提交。setgid postdrop(1) 实用程序由 sendmail(1) 本地提交程序用于将消息缓冲到默认实例队列目录下的 maildrop 子目录中。

即使在极少数情况下,使用 "sendmail -C" 将本地邮件提交到非默认 Postfix 实例,出于安全考虑,postdrop(1) 仍会咨询默认的 main. cf文件以验证请求的非默认配置目录的有效性。

因此,尽管在其他方面所有实例均等,但默认实例"更优于其他实例"。您可以选择创建额外实例,但必须至少保留默认实例,且其配置目录必须位于默认编译位置。

实例组

postmulti(1) 多实例管理器支持"实例组"的概念。通常,实例组中的成员实例构成一个逻辑服务,并且预期所有实例要么全部运行,要么全部停止。

在许多情况下,单个 Postfix 实例将是一个完整的逻辑"服务"。您应将此类实例定义为独立实例,不属于任何实例"组"。空客户端实例就是一个非组实例的示例。

当逻辑服务由多个 Postfix 实例组成(通常是一对预过滤器和后过滤器实例,中间夹着一个内容过滤代理)时,相关实例应属于同一个实例组(不过内容过滤器通常有独立于任何 Postfix 实例的启动/停止流程)。

默认实例 main. cf 文件中的 $multi_instance_directories 配置参数列出了所有次要(非默认)实例的配置目录。这些次要实例与默认实例一起由多实例管理器进行管理。实例按列表中的顺序启动,并按相反顺序停止。对于属于服务"组"的实例,应安排以相反顺序启动服务,即输出阶段启动并准备好接收邮件后,再启动输入阶段。

多实例配置参数

multi_instance_wrapper

此默认实例配置参数必须设置为一个合适的多实例管理器的"包装程序",该程序用于控制多实例 Postfix 系统的启动、停止等操作。要使用本文档中描述的 postmulti(1) 管理器,应使用 "postmulti -e init" 命令设置此参数。

multi_instance_directories

此默认实例配置参数指定通过多实例管理器控制的次要实例的可选列表。实例按其"启动"顺序列出,默认实例始终首先启动(如果启用)。如果 $multi_instance_directories 为空,则 postfix(1) 命令将以关闭多实例支持的方式运行,且所有 multi_instance_ 配置参数均无效。

在配置合适的 multi_instance_directories 之前,请勿将非空的二级实例配置目录列表赋值给该参数。 html#multi_instance_wrapper">multi_instance_wrapper 设置!建议通过 "postmulti -e init" 命令完成。

multi_instance_name

每个 Postfix 实例都可以分配一个唯一的名称(使用 "postfix -e create/import/assign -I name...")。该名称可与 postmulti(1) 命令行工具配合使用,通过名称(而非配置目录的完整路径名)对实例执行操作。选择一个简洁地描述实例作用的名称(必须以 "postfix-" 开头)。两个实例不能具有相同的 $multi_instance_name。您可以通过将此参数保留为默认的空设置来让实例保持无名。

为避免日志混淆,若未为每个次要实例分配一个非空(且唯一的)$multi_instance_name,您应确保每个实例的 $syslog_name 设置不同。$syslog_name 参数默认值为 $multi_instance_name,当后者不为空时。如果可能,syslog_name 应以 "postfix-" 开头,这有助于日志解析器识别来自次要 Postfix 实例的日志条目。

multi_instance_group

每个 Postfix 实例可被分配一个"实例组"名称(使用命令"postfix -e create/import/assign -G name...")。multi_instance_group 参数的默认(空)值表示一个独立的实例,不属于任何组。组名可与 postmulti(1) 命令行工具配合使用,对组中的成员按名称执行任务。请选择一个单词的组名,简洁地概括组的角色。

multi_instance_enable

此参数控制 Postfix 实例是否由 Postfix 多实例管理器启动。默认值为 "no"。实例可以显式地通过 "postfix -c /path/to/config/directory" 启动;这在测试时很有用。

当实例被禁用时,postfix(1) 的 "start" 命令将被 "check" 替换。

某些 postfix(1) 命令(如 "stop"、"flush" 等)需要 Postfix 实例正在运行,并跳过已禁用的实例。

其他 postfix(1) 命令(如 "status"、"set-permissions"、"upgrade-configuration" 等)不需要 Postfix 系统正在运行,并适用于所有实例,无论是否启用。

postmulti(1) 工具可用于创建(或销毁)实例。它还可用于将现有实例"导入"或"导出"到受管实例列表中。当使用postmulti(1)管理实例时,上述配置参数将自动为您管理。详见下方。

使用 postmulti(1) 命令

初始化多实例管理器

在首次使用 postmulti(1) 之前,您必须将其安装为 Postfix 系统的 multi_instance_wrapper,并启用默认 Postfix 实例的多实例操作。随后,您可以继续添加 现有 实例到多实例配置中。此初始安装操作如下所示:

 # postmulti -e init

这将更新默认实例的 main.cf 文件如下:

 # 使用 postmulti(1) 作为 postfix-wrapper(5)
#
multi_instance_wrapper = ${command_directory}/postmulti -p --
# 配置多实例模式下默认启动的实例
#
multi_instance_enable = yes

如果你愿意,你可以通过直接编辑默认的 main.cf 文件,或使用 "postconf -e" 命令来进行这些更改。

列出管理的实例

已管理的实例列表包含默认实例以及配置目录在 multi_instance_directories 参数中列出的附加实例(按启动顺序)。

您可以通过仅指定实例匹配选项并使用 "-l" 选项来列出选定的实例、实例组或所有实例。如果未指定其他实例选择选项,则默认使用 "-a" 选项(此行为在使用 "-e" 选项时会改变)。作为特殊情况,即使显式指定了名称,默认实例仍可通过 "-i -" 选项选中。

# postmulti -l -a
# postmulti -l -g a_group
# postmulti -l -i an_instance

输出为每实例一行(按 "postfix start" 命令执行顺序):

name启用config_directory
--/etc/postfix
mta-outmta/etc/postfix/mta-out
mta-inmta/etc/postfix-mta-in
msa-outmsayes/etc/postfix-msa-out
msa-inmsayes/etc/postfix-msa-in
test-no/etc/postfix-test

第一行显示的列标题不属于输出内容。当实例名称或实例组未设置时,将显示为 "-"。

通过 "-i" 选项选择现有实例时,您可以始终使用其配置目录的完整路径名代替实例(短)名称。这是选择非默认无名实例的唯一方法。默认实例可通过 "-i -" 选项选择,无论其是否有名称。

要按启动顺序反向列出实例,请与实例选择选项一起使用 "-R" 选项。

启动或停止多实例系统

要启动、停止、重新加载等操作已配置完成(如上所述)的多实例系统,只需像处理单实例系统一样使用postfix(1)。Postfix 多实例封装框架将 Postfix init.d 启动脚本和包升级脚本与多实例管理细节隔离!

-p 选项用于启用 postmulti(1)postfix(1) 兼容模式。启用此选项后,后续参数与postfix(1)完全兼容,但命令将应用于所有实例或所有已启用的实例(视情况而定)。如上所述,当使用 postmulti(1) 作为 multi_instance_wrapper 时,此开关是必需的。

如果您希望按名称或组名称指定实例子集,或在特定实例或实例组的上下文中(MAIL_CONFIG环境变量设置)执行任意命令(而不仅仅是"postfix stop/start/etc."),则可以直接使用实例感知型postmulti(1)工具。

临时多实例操作

postmulti(1) 命令可由管理员用于在单个或多个 Postfix 实例的上下文中执行任意命令。最常见的用例是停止或启动一组 Postfix 实例:

# postmulti -g mygroup -p start
# postmulti -g mygroup -p flush
# postmulti -g mygroup -p reload
# postmulti -g mygroup -p status
# postmulti -g mygroup -p stop
# postmulti -g mygroup -p upgrade-configuration

-p 选项本质上是 postfix 命令前缀参数的简写形式,但会根据第一个参数自动启用相应的附加选项。在 "start" 情况下,已禁用的实例会进行 "检查"(postfix check)而非直接跳过。

生成的命令将针对每个候选实例执行,同时将 MAIL_CONFIG 环境变量设置为对应 Postfix 实例的配置目录。

postmulti(1) 工具能够执行除 postfix(1) 之外的命令。使用 -x 选项,可让 postmulti 为所有实例、一组实例或仅一个实例执行自定义命令。对于临时命令,multi_instance_enable 参数将被忽略:命令将无条件地执行给通过 -a、-g 或 -i 选项选择的实例。除了 MAIL_CONFIG 之外,以下实例参数将导出到命令环境中:

command_directory=$command_directory
daemon_directory=$daemon_directory
config_directory=$config_directory
队列目录=$队列目录
data_directory=$data_directory
multi_instance_name=$multi_instance_name
multi_instance_group=$multi_instance_group
multi_instance_enable=$multi_instance_enable

config_directory 设置当然与 MAIL_CONFIG 相同,且可能被认为是多余的,但保留它会更直观。如果你想跳过已禁用的实例,只需检查 multi_instance_enable 环境变量,如果设置为 "no",则退出。

能够运行临时命令为我们打开了许多额外的可能性:

  • 在使用 sendmail(1) 发送验证探针时,通过名称而非配置目录指定实例:

    $ postmulti -i postfix-myinst -x sendmail -bv [email protected]
    
  • 显示所有 Postfix 实例的非默认 main.cf 设置。这使用一个内联 shell 脚本将多个 shell 命令打包在一起,并在每个实例上执行:

    $ postmulti -x sh -c 'echo "-- $MAIL_CONFIG"; postconf -n'
    
  • 将组中启用成员实例中的所有邮件置于暂停状态:

    # postmulti -g group_name -x \
    sh -c 'test $multi_instance_enable = yes &;&; postsuper -h ALL'
    
  • 显示所有实例中延迟队列中的前10个域名:

    # postmulti -x sh -c 'echo "-- $MAIL_CONFIG"; qshape deferred | head -12'
    

创建新的 Postfix 实例

postmulti(1) 命令可用于创建额外的 Postfix 实例。新实例将使用本地提交并禁用所有 "inet" 服务,通过在 main.cf 文件中设置以下非默认参数实现:

authorized_submit_users =
master_service_disable = inet

上述设置确保新实例可安全立即启动:它们不会与现有 Postfix 实例中的 inet 监听器发生冲突。它们也不会接受任何邮件,直到完全配置完毕,届时可移除上述安全措施中的一个或全部。

postmulti(1) 命令建议一种组织非默认实例的配置目录、队列目录和数据目录的优选方式。如果默认实例设置为:

config_directory = /conf-path/postfix
queue_directory = /queue-path/postfix
data_directory = /data-path/postfix

一个新创建的实例名为postfix-myinst,默认情况下将具有:

multi_instance_enable = no
multi_instance_name = postfix-myinst
config_directory = /conf-path/postfix-myinst
queue_directory = /queue-path/postfix-myinst
data_directory = /data-path/postfix-myinst

在创建实例时,您可以覆盖这些默认设置,但除非您希望将实例队列目录分布在多个文件系统中,否则建议使用默认命名策略。这可确保多个实例以统一、可预测的方式组织。

在后续指定实例名称时,您可以使用"postfix-myinst"或配置目录的完整路径进行引用。

要创建新实例,只需使用-e create选项:

# postmulti -I postfix-myinst -e create

如果新实例属于实现单一逻辑服务的关联实例组,请将其分配到组中:

# postmulti -I postfix-myinst -G mygroup -e create

若要覆盖实例安装参数的默认值,请在命令行中指定其值:

# postmulti [-I postfix-myinst] [-G mygroup] -e create \
"config_directory = /path/to/config_directory" \
"queue_directory = /path/to/queue_directory" \
"data_directory = /path/to/data_directory"

关于上述 -I-G 选项的说明。这些选项始终用于为实例分配名称或组名称,而 -i-g 选项始终用于选择现有实例。默认情况下,新管理的实例的配置目录会附加到实例列表中。您可以使用 "-i"、"-g" 或 "-a" 选项将新实例插入到指定实例或组之前,或插入到实例列表开头(默认实例的 "multi_instance_directories" 参数)。

如果您为新实例指定了名称(使用 "-I" 并指定一个不为 "-" 的名称),则可以省略任何三个实例安装参数中基于实例名称的值。否则,所有三个实例安装参数都是必需的。您应显式设置 "syslog_name",以避免在使用多个实例时邮件日志中出现混淆。

删除 Postfix 实例

如果您不再需要某个实例,可以通过以下方式销毁它:

# postmulti -i postfix-myinst -p stop
# postmulti -i postfix-myinst -e disable
# postmulti -i postfix-myinst -e destroy

实例必须已停止、禁用且没有待处理的消息。这将完全删除一个刚创建且从未使用过的实例。如果实例并非刚创建,实例创建后添加的文件将保留在配置、队列或数据目录中,此时对应目录可能无法完全删除,并会显示相应警告。您可以手动删除实例专属的"私有"目录中的任何残留文件以完成实例销毁。

导入现有 Postfix 实例

如果您已经有一个未通过 postmulti(1) 管理的主 Postfix 实例,您可以将其"导入"到已管理实例列表中。如果您的实例已使用默认配置目录命名方案,只需指定对应的实例名称(其配置文件中的multi_instance_name参数将根据需要调整为该名称):

# postmulti -I postfix-myinst [-G mygroup] -e import

否则,您必须指定其配置目录的位置:

# postmulti [-I postfix-myinst] [-G mygroup] -e import \
"config_directory = /path/of/config_directory"

导入实例时,您可以为其分配名称或组。与"create"类似,您可以通过在所选实例或实例前添加 "-i"、"-g" 或 "-a" 来控制新实例在启动顺序中的位置。

导入的实例通常不支持多实例,除非它之前属于多实例配置。如果实例已完全配置并准备就绪,请务必启用它,并在必要时启动。当其他已启用的实例正在运行时,新实例在首次创建或导入时需要单独启动。

要查看正在运行的实例,请使用:

# postfix status

从多实例管理中移除一个 Postfix 实例

您可以从受管实例列表中"迁移"现有实例。此操作不会删除实例,而是使实例成为未注册到多实例管理器的独立 Postfix 实例。postmulti(1) 将拒绝迁移未停止且未禁用的实例。

# postmulti -i postfix-myinst -p stop
# postmulti -i postfix-myinst -e disable
# postmulti -i postfix-myinst -e deport

为实例分配新名称或组名称

您可以为受管实例分配新名称或新组。使用 "-" 作为新值可将实例分配到无组或使其无名。要指定无名的次要实例,请使用配置目录路径代替旧名称:

# postmulti -i postfix-old [-I postfix-new] [-G newgroup] -e assign

启用/禁用受管实例

您可以启用或禁用一个受管实例。如在 postfix-wrapper(5) 中所述,禁用的实例将被跳过执行相关操作。 html#postmulti_start_commands">start、stop控制 正在运行的 Postfix 实例。

# postmulti -i postfix-myinst -e enable
# postmulti -i postfix-myinst -e disable

致谢

Wietse Venema 创建了 Postfix,设计并实现了多实例封装框架,并提供了设计反馈,使 postmulti(1) 工具比最初设想的更加通用和实用。

postmulti(1) 工具由摩根士丹利的 Victor Duchovni 开发,他同时撰写了本文件的初始版本。