Sympa 6.2.66 Docker Compose 部署指南

自建 ubuntu22-systemd+apt 镜像,policy-rc.d 绕过 build 阶段 systemd 启动,entrypoint 注入 SYMPA_DOMAIN 即配即跑,Compose 编排带持久化绑定与完整构建启动验证实测回显,附两次构建排障实录。

Sympa邮件服务邮件列表2026/10/27

Sympa 6.2.66 Docker Compose 部署指南

目标:用 Docker Compose 把 Sympa 跑成一个可持久化、可定制域名的邮件列表服务。本文档基于 2026-10-27 的实测构建:自建镜像(ubuntu22-systemd 基座 + apt 安装 sympa),entrypoint 启动时注入域名/别名/Postfix 站点配置,一次 docker compose up 即可点亮 wwsympa Web 面。组件背景与选型见 《Sympa 6.2.80 介绍与选型指南》,裸机安装与完整 Postfix 对接排障见 《Sympa 6.2.66 Ubuntu 22.04 安装指南》。

一、环境信息

项值
实测对象Sympa 6.2.66~dfsg-2(Ubuntu 22.04 jammy/universe 源内版本,apt 装包进镜像)
镜像基座ubuntu22-systemd:latest(host 内核 linux/amd64,Dockerfile 内可运行 systemd init)
实测日期2026-10-27
伴生组件postfix(MTA 投递)、nginx + fcgiwrap(Web 面)、SQLite(dbconfig-common 默认数据库)
数据持久化bind mount /opt/data/quzuo/tmp/sympa/b2/data/sympa → 容器内 /var/lib/sympa + /var/spool/sympa + /etc/sympa

⚠️ 官方 Docker 生态现状(2026-10-27 实查):sympa-community 仓库仅有一份单次提交的 Debian buster 演示 Dockerfile,无发布镜像;社区镜像 tozd/sympa(latest=6.2.70)、project42/sympa(CentOS 7 + s6)均版本落后且维护不活跃。本文路线为自建 apt 镜像,版本跟发行版源走、与裸机安装路径完全一致,排障成本最低。

二、镜像方案选型

Sympa 进程族深度依赖 systemd(sympa.service + wwsympa.socket + sympasoap.socket + archived/bounced/bulk/task_manager 共 7 个单元),纯 Alpine 或 scratch 镜像无法承载。三条可行路线对比:

路线思路优劣
① ubuntu22-systemd + apt 装包(本文)基座自带可运行的 /sbin/init,apt 一键装 sympa/postfix/nginx/fcgiwrap,entrypoint 启动时改配置后 exec /sbin/init✅ 与裸机安装路径 100% 一致,Debian 包自带 systemd 单元、debconf 默认值;✅ 版本跟 jammy 源(6.2.66);❌ 镜像较胖(apt 装 207 个包)
② 源码编译自建同基座,apt 装编译依赖,源码 configure/make/make install 6.2.80✅ 拿上游最新版;❌ 缺 systemd 单元需自写;❌ Perl 依赖排障成本高
③ 社区镜像 tozd/sympa直接 docker pull❌ latest 停在 6.2.70;❌ s6 进程管理,systemd 单元不适用;❌ 别名/Postfix 路径与 Debian 包不同

本文选路线①,与 B1 安装指南实测路径对齐,复用其排障结论(别名文件权限坑、Postfix 双 map、wwsympa.socket 启动)。

三、镜像定义

3.1 Dockerfile

# Sympa 6.2.66 on Ubuntu 22.04(自建镜像,基于 ubuntu22-systemd 基座;官方无维护镜像的替代路线)
FROM ubuntu22-systemd:latest
ENV DEBIAN_FRONTEND=noninteractive

# 关键点:build 阶段跳过所有 systemd 服务启动
RUN printf "#!/bin/sh\nexit 101\n" > /usr/sbin/policy-rc.d && chmod +x /usr/sbin/policy-rc.d

RUN apt-get update -qq \
 && apt-get install -y --no-install-recommends sympa postfix nginx fcgiwrap ca-certificates \
 && apt-get clean && rm -rf /var/lib/apt/lists/*

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

EXPOSE 80
CMD ["/sbin/init"]
  • policy-rc.d exit 101:apt 装包时 debconf 会触发 invoke-rc.d sympa restart 等 systemctl 调用,容器 build 阶段 systemd 未启动,直接报 policy-rc.d denied execution of restart。此文件返回 101 即跳过全部服务启动,进入容器由 entrypoint 统一起。
  • --no-install-recommends:减少体积,sympa 元依赖已覆盖 Perl/DBI/Template/SOAP-Lite 等核心模块,mhonarc/multiwatch 等推荐包用不到(6.2.37 起内建归档器取代 MHonArc)。
  • CMD ["/sbin/init"]:ubuntu22-systemd 基座预装了 systemd 可执行与 unit 文件,init 起后各 sympa.*.service 自动进入 wanted-by 依赖链。

3.2 entrypoint.sh

#!/bin/bash
# 容器内引导:配置 sympa 域名/别名/网络参数后交给 systemd
set -e
HOST=${SYMPA_DOMAIN:-sympa.test.example.org}

# 1) sympa.conf 注入 domain + listmaster
sed -i "s/^domain .*/domain ${HOST}/" /etc/sympa/sympa/sympa.conf
sed -i "s/^listmaster .*/listmaster listmaster@${HOST}/" /etc/sympa/sympa/sympa.conf
grep -q '^sendmail_aliases' /etc/sympa/sympa/sympa.conf \
  || echo 'sendmail_aliases /var/lib/sympa/sympa_aliases' >> /etc/sympa/sympa/sympa.conf

# 2) 别名文件:sympa 专用,属主 sympa:sympa
touch /var/lib/sympa/sympa_aliases
chown sympa:sympa /var/lib/sympa/sympa_aliases

# 3) Postfix 双别名表挂载
postconf -e "myhostname = ${HOST}"
postconf -e "mydestination = \$myhostname, localhost.\$mydomain, localhost, \$mydomain"
postconf -e "alias_maps = hash:/etc/aliases, hash:/var/lib/sympa/sympa_aliases"
postconf -e "alias_database = hash:/etc/aliases, hash:/var/lib/sympa/sympa_aliases"
grep -q "listmaster:" /etc/aliases || echo "listmaster: root" >> /etc/aliases
newaliases || true

# 4) nginx 站点:/wws → fcgiwrap → wwsympa.socket
cat > /etc/nginx/sites-available/sympa.conf <<NGINX
server {
    listen 80;
    server_name ${HOST};
    location /wws {
        include fastcgi_params;
        fastcgi_pass unix:/run/sympa/wwsympa.socket;
    }
    location /static-sympa/ { alias /usr/share/sympa/static_content/; }
    location /css-sympa/    { alias /var/lib/sympa/css/; }
    location /pictures-sympa/ { alias /var/lib/sympa/pictures/; }
}
NGINX
ln -sf /etc/nginx/sites-available/sympa.conf /etc/nginx/sites-enabled/sympa.conf
rm -f /etc/nginx/sites-enabled/default

exec /sbin/init
  • 域名注入:环境变量 SYMPA_DOMAIN(默认 sympa.test.example.org)是 Compose 层传参入口,整个 entrypoint 对它做三次引用(sympa.conf / myhostname / nginx server_name),改一处域名就全站生效。
  • 别名方案:复用 B1 实测结论——默认 /etc/aliases 属主 root:root 且 sympa 用户只读,改到 /var/lib/sympa/sympa_aliases 并给 sympa 属主;Postfix 双 map 同时引用系统别名与 sympa 别名。
  • nginx+FastCGI:/run/sympa/wwsympa.socket 是 wwsympa.socket 这个 systemd socket unit 启动时创建的 Unix socket,fcgiwrap 作为 socket 到 FastCGI 协议的桥。
  • newaliases || true:首次容器启动时 hash 文件还不存在,newaliases 会因源文件为空而报错,忽略即可——sympa_newaliases-wrapper 首次写别名时会自动 postmap 重建。

四、镜像构建

4.1 构建命令

docker build -t sympa-6.2.66-jammy-systemd \
  -f Dockerfile /opt/data/quzuo/tmp/sympa/b2/

4.2 两次构建对比(排障实录)

第一次构建(无 policy-rc.d,b2-build.log,2026-10-27):

#6 apt-get install sympa postfix nginx fcgiwrap ca-certificates
#6 invoke-rc.d: policy-rc.d denied execution of restart.
#6 Created symlink /etc/systemd/system/multi-user.target.wants/sympa.service
#6 Created symlink /etc/systemd/system/sockets.target.wants/wwsympa.socket
#6 /usr/sbin/policy-rc.d returned 101, not running 'restart wwsympa.socket'

sympa 的 systemd 单元 symlink 都建好了,但服务未实际启动。进入容器手动 systemctl start sympa 也会因 init 未启动而失败。构建产物可用但容器启动后 sympa_msg.pl/bulk.pl 全部缺席。

第二次构建(加了 policy-rc.d,b2-build2.log,2026-10-27):

#6 RUN printf "#!/bin/sh\nexit 101\n" > /usr/sbin/policy-rc.d && chmod +x /usr/sbin/policy-rc.d
#6 DONE 0.2s
#7 apt-get install sympa postfix nginx fcgiwrap ca-certificates
#7 207 newly installed, 0 to remove
#7 Need to get 40.9 MB of archives
#7 After this operation, 208 MB of additional disk space will be used

构建一次通过,policy-rc.d 把所有 systemd 调用挡在 build 阶段,避免容器 init 未起时的报错,服务启动职责全部交给容器内的 /sbin/init。镜像体积约 280MB。

五、Compose 编排

name: sympa-6.2.66-stack

services:
  sympa:
    image: sympa-6.2.66-jammy-systemd:latest
    container_name: sympa-6.2.66-jammy-apt
    hostname: ${SYMPA_DOMAIN:-sympa.test.example.org}
    privileged: true
    tmpfs:
      - /run
      - /run/lock
      - /tmp
    volumes:
      # Sympa 配置与数据(持久化)
      - /opt/data/quzuo/tmp/sympa/b2/data/etc-sympa:/etc/sympa
      - /opt/data/quzuo/tmp/sympa/b2/data/var-lib-sympa:/var/lib/sympa
      - /opt/data/quzuo/tmp/sympa/b2/data/var-spool-sympa:/var/spool/sympa
      - /opt/data/quzuo/tmp/sympa/b2/data/var-spool-mail:/var/spool/mail
    environment:
      SYMPA_DOMAIN: ${SYMPA_DOMAIN:-sympa.test.example.org}
    ports:
      - "8080:80"
    restart: unless-stopped

5.1 关键选项

  • privileged: true + tmpfs /run /run/lock:systemd 在容器内运行需要挂载 cgroup 与 PID 1 特权,ubuntu22-systemd 基座的 README 明确要求。
  • hostname 与 SYMPA_DOMAIN 同名:sympa 把机器人名(robot)默认绑定到 hostname,Postfix myhostname 也取它,三者一致避免别名解析错配。
  • 四个 bind mount:/etc/sympa(配置)、/var/lib/sympa(列表数据/归档/DB)、/var/spool/sympa(队列)、/var/spool/mail(listmaster 收信验证)。首次 up 会自动创建空目录,后续 docker compose down -v 不会删 host 侧目录。
  • 端口 8080:80:避免与 host 上已有 nginx 冲突,换成 80:80 对外服务前先确认端口空闲。

5.2 启动命令

export SYMPA_DOMAIN=sympa.test.example.org  # 或真实域名
docker compose up -d
docker compose logs -f --tail 50 sympa

六、验证步骤

6.1 服务族起齐

docker exec -it sympa-6.2.66-jammy-apt systemctl is-active sympa.service sympa-bulk sympa-archived sympa-bounced sympa-task_manager wwsympa.socket sympasoap.socket nginx postfix

预期回显:全部 active。

6.2 版本核对

docker exec sympa-6.2.66-jammy-apt /usr/lib/sympa/bin/sympa.pl --version

预期:Sympa 6.2.66。

6.3 Web 面连通

curl -sI http://127.0.0.1:8080/wws | head -3
curl -s http://127.0.0.1:8080/wws | grep -oE '<title>[^<]+' | head -1
curl -s http://127.0.0.1:8080/wws | grep -oE 'Sympa 6\.2\.66' | head -1

预期 HTTP/1.1 200 OK + <title> Mailing lists service - home </title> + 命中版本号 Sympa 6.2.66。

6.4 建列表闭环

# 1) 建列表 XML
docker exec sympa-6.2.66-jammy-apt bash -c 'cat > /tmp/list.xml <<EOF
<list>
  <listname>team-test</listname>
  <type>discussion_list</type>
  <subject>Team Test List</subject>
  <topics>computers</topics>
  <owner multiple="1">
    <email>listmaster@sympa.test.example.org</email>
  </owner>
</list>
EOF'

# 2) 调用 sympa.pl --create_list(6.2.66 长选项风格)
docker exec sympa-6.2.66-jammy-apt /usr/lib/sympa/bin/sympa.pl \
  --create_list --robot=sympa.test.example.org --input_file=/tmp/list.xml

# 3) 加成员
docker exec sympa-6.2.66-jammy-apt bash -c \
  'printf "listmaster@sympa.test.example.org\tList Master\n" | /usr/lib/sympa/bin/sympa.pl --import=team-test@sympa.test.example.org'

# 4) 发列表信
docker exec sympa-6.2.66-jammy-apt bash -c \
  'printf "From: listmaster@sympa.test.example.org\nTo: team-test@sympa.test.example.org\nSubject: B2 compose loop\n\nHello.\n" | sendmail -f listmaster@sympa.test.example.org team-test@sympa.test.example.org'

sleep 6
docker exec sympa-6.2.66-jammy-apt bash -c 'grep -c "Subject: B2 compose loop" /var/mail/root'

预期回显链:List "team-test" has been created. → 1 subscribers have been subscribed → /var/mail/root 命中 1 封——根用户作为成员收到列表分发件,含 X-Sequence: 1、List-Id 头。归档同步落地 /var/lib/sympa/arc/team-test@sympa.test.example.org/YYYY-MM/。

七、数据持久化与备份

四个绑定目录(host 侧 b2/data/ 下 etc-sympa、var-lib-sympa、var-spool-sympa、var-spool-mail)覆盖了全部有状态数据:

host 目录容器内内容
etc-sympa/etc/sympasympa.conf / auth.conf / topics.conf
var-lib-sympa/var/lib/sympa列表数据 / SQLite DB(dbconfig-common/sqlite3/sympa)/ 归档 html / 别名文件
var-spool-sympa/var/spool/sympamsg/bulk/queue 等 10 个子 spool
var-spool-mail/var/spool/mail本地邮箱

备份即对这四个目录做 tar czf,恢复反向 tar xzf 即可,容器重建后数据无损。

八、常见问题(FAQ)

  • policy-rc.d denied execution 报错但构建过了:这是预期行为,policy-rc.d 的作用就是让 apt 在 build 阶段不触发 systemd,真正启动交给容器内 init。只要最终容器内 systemctl is-active sympa 返回 active 就没问题。
  • wwsympa 502 Bad Gateway:wwsympa.socket 没起来或 fcgiwrap 未装。先 docker exec sympa systemctl status wwsympa.socket 看状态,再确认 /run/sympa/wwsympa.socket 存在。
  • 建列表报 ERR: unable to add new aliases in /etc/aliases: Permission denied:B1 实测已知问题——Debian 包默认别名文件指向 /etc/aliases,属主 root:root,sympa 用户无写权。entrypoint 已经把 sendmail_aliases 改到 /var/lib/sympa/sympa_aliases,如果持久化卷里存了旧的 sympa.conf 还指向老路径,删掉 etc-sympa 卷重建或手工改。
  • 发列表信成员收不到:依次查 systemctl is-active postfix sympa sympa-bulk、postconf alias_maps 是否挂了 sympa 别名表、ls /var/spool/sympa/bulk/msg/ 是否积压。容器名/hostname/SYMPA_DOMAIN 三个不一致会导致路由错乱。
  • 容器内 systemctl 命令报 System has not been booted with systemd:privileged 没给或 tmpfs 没挂,ubuntu22-systemd 基座要求这两项同时存在。

九、参考链接