Apache Pony Mail 0.11-incubating Docker Compose 部署指南
Apache Pony Mail 0.11-incubating Docker Compose 部署指南:自建镜像打包 ES 2.4.6、JDK8 与 lmtpd,ES 堆 256MB,Dockerfile 与实测回显
Apache Pony Mail 0.11-incubating Docker Compose 部署指南
目标:使用 Docker Compose 编排 Apache Pony Mail 0.11-incubating 全套运行栈——Elasticsearch 2.4.6(JDK8)+ Apache(mod_lua/Lua 5.3)+ Pony Mail 0.11 + LMTP 归档网关 lmtpd,通过自建镜像实现「一条 docker compose 命令拉起整套邮件归档浏览系统」。端口映射到宿主机,ES JVM 堆限定 256MB,全程命令与回显均为实际执行结果。
一、环境信息
- Docker:24.x(
docker composev2 子命令) - Pony Mail 镜像:自建
ponymail:0.11(基于ubuntu:22.04,1.23GB) - 服务名:ponymail;容器名:
ponymail-server - 映射端口:
18080:8080(Web UI,宿主 8080 已被 haproxy 占用故用 18080)、8024:8024(LMTP 归档入口)、9200:9200(ES REST) - 数据卷:
./data→/opt/elasticsearch/data(ES 数据持久化) - 镜像内含版本:ES 2.4.6、JDK8、Apache 2.4.52、Pony Mail 0.11-incubating
⚠️ 受限前置说明:Pony Mail 0.11-incubating 是 2019-03 发布的最后版本,项目已休眠,官方未提供任何 Docker 镜像,需自建。其代码面向 Elasticsearch 1.x/2.x 时代,必须搭配 ES 2.x 引擎与 python-elasticsearch 2.x 客户端(archiver.py 源码校验
ES_MAJOR == 2,客户端 1.9.0 会直接报错),现代 ES(7.x/8.x)无法兼容。本指南镜像即按此版本组合构建并实测。
二、镜像选型说明
| 镜像 | 说明 |
|---|---|
ponymail:0.11(自建) | 本指南采用,基于 ubuntu:22.04,单镜像内打包 ES 2.4.6 + JDK8 + Apache(mod_lua)+ Pony Mail 0.11 + lmtpd 网关,开箱即用 |
| Docker Hub 官方镜像 | 不存在。Apache Pony Mail 从未发布过官方容器镜像,第三方镜像均不可考,不建议使用 |
镜像内容固定为「Pony Mail 历史版本兼容栈」:ES 2.4.6 只支持 Java 8(故内置 JDK8,
JAVA_HOME=/opt/jdk8);mod_lua 基于 Lua 5.3,而 Ubuntu 22.04 的lua-cjson包只带 5.1/5.2 模块,镜像内对mpx/lua-cjson 2.1.0源码编译出 5.3 版cjson.so。这些版本锁定关系是 Pony Mail 0.11 能否跑起来的关键。
三、前置条件
- 装有 Docker 与 Docker Compose v2 的服务器,root 权限。
- 能访问 Docker Hub(拉取
ubuntu:22.04基础镜像);本指南实测环境构建机无法从容器内直连 github.com,因此在宿主机预下载全部制品到vendor/后以COPY方式进镜像(详见 4.2 说明)。 - 宿主机需能访问:Apache 镜像站(Pony Mail tar.gz)、Adoptium(JDK8)、download.elastic.co(ES 2.4.6)、PyPI(python 依赖)。
四、部署步骤
4.1 创建目录结构
mkdir -p ponymail-compose/conf ponymail-compose/data ponymail-compose/vendor/pip
cd ponymail-compose
vendor/ 用于存放预下载制品,目录结构:
| 文件 | 大小 | 来源 |
|---|---|---|
vendor/cjson.tar.gz | 71,947 字节 | https://github.com/mpx/lua-cjson/archive/refs/tags/2.1.0.tar.gz |
vendor/jdk8.tar.gz | 103,542,511 字节 | Adoptium API(Temurin 8 GA,linux x64) |
vendor/es.tar.gz | 27,350,245 字节 | https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.4.6/elasticsearch-2.4.6.tar.gz |
vendor/ponymail.tar.gz | 1,055,853 字节 | https://downloads.apache.org/ponymail/ponymail/apache-pony-mail-0.11-incubating.tar.gz |
vendor/pip/*.whl | ~4.8MB 合计 | pip download elasticsearch==2.4.0 formatflowed netaddr certifi chardet |
# 宿主机一次性预下载(示例,实测全部成功)
curl -fsSL -o vendor/cjson.tar.gz https://github.com/mpx/lua-cjson/archive/refs/tags/2.1.0.tar.gz
curl -fsSL -o vendor/jdk8.tar.gz 'https://api.adoptium.net/v3/binary/latest/8/ga/linux/x64/jdk/hotspot/normal/eclipse'
curl -fsSL -o vendor/es.tar.gz https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.4.6/elasticsearch-2.4.6.tar.gz
curl -fsSL -o vendor/ponymail.tar.gz https://downloads.apache.org/ponymail/ponymail/apache-pony-mail-0.11-incubating.tar.gz
python3 -m pip download -d vendor/pip 'elasticsearch==2.4.0' formatflowed netaddr certifi chardet
4.2 编写自建镜像的 Dockerfile
cat > Dockerfile <<'EOF'
# Apache Pony Mail 0.11-incubating 自建镜像
# 单镜像内含:ES 2.4.6 + JDK8 + Apache(mod_lua/Lua5.3) + Pony Mail 0.11 + lmtpd 网关
# 构建说明:所有外网制品已预下载到 vendor/(宿主机下载),构建过程零外网依赖
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# 1) 基础包:Apache + Lua 5.3 依赖 + 构建工具
RUN apt-get update && apt-get install -y --no-install-recommends \
apache2 python3 python3-pip wget curl ca-certificates \
gcc make liblua5.3-dev lua-socket lua-sec net-tools procps \
&& rm -rf /var/lib/apt/lists/*
# 2) Lua 5.3 cjson(Ubuntu 22.04 的 lua-cjson 仅含 5.1/5.2,mod_lua 基于 Lua 5.3,需源码编译)
COPY vendor/cjson.tar.gz /tmp/cjson.tar.gz
RUN cd /tmp \
&& tar -xzf cjson.tar.gz && cd lua-cjson-2.1.0 \
&& make LUA_INCLUDE_DIR=/usr/include/lua5.3 \
&& cp cjson.so /usr/lib/x86_64-linux-gnu/lua/5.3/ \
&& rm -rf /tmp/lua-cjson-2.1.0 /tmp/cjson.tar.gz
# 3) JDK 8(ES 2.4.6 仅支持 Java 8)
COPY vendor/jdk8.tar.gz /opt/jdk8.tar.gz
RUN mkdir -p /opt/jdk8 \
&& tar -xzf /opt/jdk8.tar.gz -C /opt/jdk8 --strip-components=1 \
&& rm -f /opt/jdk8.tar.gz
# 4) Elasticsearch 2.4.6(ES 2.x 拒绝 root 运行,建 esuser)
COPY vendor/es.tar.gz /opt/es.tar.gz
RUN useradd -m -s /bin/bash esuser \
&& cd /opt \
&& tar -xzf es.tar.gz && mv elasticsearch-2.4.6 /opt/elasticsearch && rm -f es.tar.gz \
&& sed -i 's/^# cluster.name: my-application/cluster.name: ponymail/; s/^# node.name: node-1/node.name: ponymail-node-1/; s/^# network.host: 192.168.0.1/network.host: 0.0.0.0/; s/^# http.port: 9200/http.port: 9200/' /opt/elasticsearch/config/elasticsearch.yml \
&& chown -R esuser:esuser /opt/elasticsearch
# 5) Pony Mail 0.11-incubating + Python 依赖(elasticsearch 2.x 库,archiver 校验 ES_MAJOR==2)
COPY vendor/ponymail.tar.gz /opt/ponymail.tar.gz
COPY vendor/pip/ /tmp/pip/
RUN pip3 install --no-cache-dir --no-index --find-links=/tmp/pip/ \
'elasticsearch==2.4.0' formatflowed netaddr certifi chardet \
&& rm -rf /tmp/pip \
&& mkdir -p /opt/ponymail \
&& tar -xzf /opt/ponymail.tar.gz -C /opt/ponymail && rm -f /opt/ponymail.tar.gz
# 6) 构建期初始化:起 ES(临时,仅本 RUN 步骤存活)-> 运行 setup.py 建索引与配置
RUN su esuser -c 'JAVA_HOME=/opt/jdk8 ES_HEAP_SIZE=256m /opt/elasticsearch/bin/elasticsearch -d' \
&& sleep 20 \
&& curl -s http://127.0.0.1:9200/ \
&& cd /opt/ponymail/tools && python3 setup.py --defaults --clobber
# 7) Apache vhost(8080)+ mod_lua
RUN a2enmod lua \
&& printf 'Listen 8080\n<VirtualHost *:8080>\n ServerName ponymail.localhost\n DocumentRoot /opt/ponymail/site\n LuaPackageCPath /usr/lib/x86_64-linux-gnu/lua/5.3/?.so\n LuaPackagePath /usr/share/lua/5.3/?.lua\n AddHandler lua-script .lua\n LuaScope thread\n LuaCodeCache stat\n AcceptPathInfo On\n <Directory /opt/ponymail/site>\n Require all granted\n </Directory>\n</VirtualHost>\n' > /etc/apache2/sites-available/ponymail.conf \
&& a2ensite ponymail
COPY ponymail-lmtpd.py /opt/ponymail-lmtpd.py
COPY entrypoint.sh /opt/entrypoint.sh
RUN chmod +x /opt/entrypoint.sh
EXPOSE 8080 8024 9200
ENTRYPOINT ["/opt/entrypoint.sh"]
EOF
⚠️ 构建期说明:Dockerfile 第 6 步会在构建时临时拉起 ES 并执行
setup.py --defaults --clobber,把ponymail.cfg、site/api/lib/config.lua、site/js/config.js一并生成到镜像里,保证容器首启即可用。若 buildkit 构建网络无法直连 github.com(本指南实测curl: (7) Failed to connect to github.com port 443: Connection refused),不要试图在 RUN 里下载,一律采用宿主机预下载 +COPY方案。
4.3 编写 lmtpd 网关与入口脚本
LMTP 归档网关(RFC 2033 子集,监听 8024,把邮件喂给 archiver.py 归档到 ES):
cat > ponymail-lmtpd.py <<'EOF'
#!/usr/bin/env python3
import socket, subprocess, threading, tempfile, os
HOST, PORT = "0.0.0.0", 8024
def handle(conn):
def resp(code, msg=b"OK"): conn.sendall(b"%d %s\r\n" % (code, msg))
f = conn.makefile("rb")
resp(220, b"ponymail-lmtpd ESMTP")
rcpt = None
while True:
line = f.readline()
if not line: break
line = line.rstrip(b"\r\n").decode("utf-8", "ignore")
if line.upper().startswith("HELO") or line.upper().startswith("EHLO"):
resp(250)
elif line.upper().startswith("MAIL FROM"):
resp(250)
elif line.upper().startswith("RCPT TO"):
rcpt = line[8:]
resp(250)
elif line.upper() == "DATA":
resp(354, b"End data with <CR><LF>.<CR><LF>")
data = b""
while True:
l = f.readline()
if l == b".\r\n": break
if l.startswith(b".."): l = l[1:]
data += l
fd, path = tempfile.mkstemp(prefix="msg-", suffix=".eml")
with os.fdopen(fd, "wb") as fp: fp.write(data)
proc = subprocess.Popen(
["python3", "/opt/ponymail/tools/archiver.py", path],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out, _ = proc.communicate()
os.unlink(path)
resp(250 if proc.returncode == 0 else 451, b"archived")
print("archived rc=%d rcpt=%s bytes=%d" % (proc.returncode, rcpt, len(data)))
elif line.upper() == "RSET":
resp(250)
elif line.upper() == "QUIT":
resp(221); break
else:
resp(250)
conn.close()
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind((HOST, PORT)); srv.listen(16)
print("ponymail-lmtpd listening on %s:%d" % (HOST, PORT), flush=True)
while True:
c, _ = srv.accept()
threading.Thread(target=handle, args=(c,), daemon=True).start()
EOF
容器入口脚本(chown 数据卷 → 以 esuser 起 ES → 确保索引 → 起 lmtpd → 前台跑 Apache):
cat > entrypoint.sh <<'EOF'
#!/bin/bash
# Pony Mail 容器入口:启动 ES -> 确保索引 -> 启动 lmtpd 网关 -> 前台运行 Apache
chown -R esuser:esuser /opt/elasticsearch/data 2>/dev/null
su esuser -c 'JAVA_HOME=/opt/jdk8 ES_HEAP_SIZE=${ES_HEAP_SIZE:-256m} /opt/elasticsearch/bin/elasticsearch -d -p /tmp/es.pid'
sleep 15
curl -s http://127.0.0.1:9200/ >/dev/null || { echo "ES failed to start"; exit 1; }
# 索引缺失时(如首次挂载空数据卷)用 setup.py 重建
cd /opt/ponymail/tools && python3 setup.py --defaults --skiponexist --clobber >/tmp/setup.log 2>&1
nohup python3 /opt/ponymail-lmtpd.py > /var/log/ponymail-lmtpd.log 2>&1 &
exec apache2ctl -DFOREGROUND
EOF
chmod +x entrypoint.sh ponymail-lmtpd.py
4.4 编写 docker-compose.yml
cat > docker-compose.yml <<'EOF'
services:
ponymail:
build:
context: .
network: host
image: ponymail:0.11
container_name: ponymail-server
restart: unless-stopped
ports:
- "18080:8080" # Pony Mail Web UI(Apache + mod_lua);宿主 8080 已被 haproxy 占用,故映射 18080
- "8024:8024" # LMTP 归档入口(lmtpd -> archiver.py)
- "9200:9200" # Elasticsearch 2.4.6 REST(排障/核对用)
environment:
ES_HEAP_SIZE: "256m" # ES JVM 堆(-Xms256m -Xmx256m)
volumes:
- ./data:/opt/elasticsearch/data # ES 数据持久化
EOF
| 配置项 | 说明 |
|---|---|
build.context: . / image: ponymail:0.11 | 自建镜像,构建上下文为当前目录(含 Dockerfile 与 vendor/) |
ports: "18080:8080" | Web UI 映射;宿主 8080 已被 haproxy 占用,按需改为 "8080:8080" |
ports: "8024:8024" | LMTP 归档入口(邮件客户端投递到此端口) |
ports: "9200:9200" | ES REST,排障/核对用 |
environment: ES_HEAP_SIZE=256m | 同时设定 ES 的 -Xms/-Xmx(实测生效 -Xms256m -Xmx256m) |
volumes: ./data:/opt/elasticsearch/data | ES 索引数据持久化,docker compose down 不丢数据 |
restart: unless-stopped | 异常退出自动拉起 |
4.5 构建并启动服务
docker compose up -d --build
实测回显(节选):
[+] Building 1.7s (23/23) FINISHED
Image ponymail:0.11 Built
Container ponymail-server Created
Container ponymail-server Starting
Container ponymail-server Started
说明:本机已先构建过镜像,故此处
--build命中缓存秒级完成;首次构建约需 3-5 分钟(apt 安装 + cjson 编译 + 制品解压 + 构建期 setup.py 初始化)。
4.6 验证服务
docker compose ps
实测回显:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
ponymail-server ponymail:0.11 "/opt/entrypoint.sh" ponymail 35 seconds ago Up 35 seconds 0.0.0.0:8024->8024/tcp, [::]:8024->8024/tcp, 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp, 0.0.0.0:18080->8080/tcp, [::]:18080->8080/tcp
ES 健康检查(应返回 2.4.6 版本信息):
curl -s http://127.0.0.1:9200/
实测回显(节选):
{
"name" : "ponymail-node-1",
"cluster_name" : "ponymail",
"cluster_uuid" : "nk6gYul1SmWoQOlq8iowXg",
"version" : {
"number" : "2.4.6",
"build_hash" : "5376dca9f70f3abef96a77f4bb22720ace8240fd",
"build_timestamp" : "2017-07-18T12:17:44Z",
"build_snapshot" : false,
"lucene_version" : "5.5.4"
},
"tagline" : "You Know, for Search"
}
Web API 检查(应返回 JSON):
curl -s http://127.0.0.1:18080/api/pminfo.lua
实测回显:
{"hits":1,"activity":[[1791763200000,1]],"participants":1,"no_active_lists":1,"no_threads":1}
Web 首页检查(应返回 HTML 页面):
curl -s http://127.0.0.1:18080/ | head -5
实测回显:
<!DOCTYPE html>
<!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. The
LMTP 归档链路验证(发送测试邮件 → 核对 ES 计数):
# 发送脚本(宿主机执行,需 python3 与 smtplib)
python3 send-lmtp-test.py
实测回显:
LMTP send OK
curl -s http://127.0.0.1:9200/ponymail/mbox/_count
实测回显:
{"count":1,"_shards":{"total":1,"successful":1,"failed":0}}
搜索验证(应命中 alice 的邮件):
curl -s 'http://127.0.0.1:9200/ponymail/mbox/_search?q=from:alice&pretty' | head -20
实测回显(节选):
{
"took" : 16,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.625,
"hits" : [ {
"_index" : "ponymail",
"_type" : "mbox",
"_id" : "re040e3adc798ee1db640fe1c080917dffb7995d7f75f54b7577fe1bb@<ponymail-test.example.org>",
"_source" : {
"from_raw" : "alice@example.org",
"from" : "alice@example.org",
"to" : "ponymail-test@example.org",
"subject" : "Pony Mail LMTP test",
"message-id" : "<lmtp-test-001@example.org>",
"cc" : "",
"epoch" : 1791770400,
"list" : "<ponymail-test.example.org>",
"list_raw" : "<ponymail-test.example.org>",
"date" : "2026/10/12 02:00:00",
"private" : false,
"references" : "",
重复投递去重验证(同一 Message-ID 再发一封,count 不增):
python3 send-lmtp-test.py && sleep 3 && curl -s http://127.0.0.1:9200/ponymail/mbox/_count
实测回显:
LMTP send OK
{"count":1,"_shards":{"total":1,"successful":1,"failed":0}}
JVM 堆生效核对(容器内应见 -Xms256m -Xmx256m):
docker exec ponymail-server bash -lc 'ps aux | grep [e]lasticsearch' | grep -oE '\-Xm[sx][0-9a-z]+' | sort -u
实测回显:
-Xms256m
-Xmx256m
五、数据持久化说明
./data挂载到/opt/elasticsearch/data,ES 索引数据(含已归档邮件)全部落在宿主机该目录,docker compose down只删除容器不删除数据。- 实测:归档 1 封邮件后执行
docker compose down && docker compose up -d,重启后_count仍为 1,数据完整保留:
cd ponymail-compose && docker compose down && docker compose up -d && sleep 25 \
&& curl -s http://127.0.0.1:9200/ponymail/mbox/_count
实测回显:
Container ponymail-server Removed
Network ponymail-compose-lab_default Removing
Network ponymail-compose-lab_default Removed
Container ponymail-server Created
Container ponymail-server Starting
Container ponymail-server Started
{"count":1,"_shards":{"total":1,"successful":1,"failed":0}}
- 首次挂载空数据卷时,entrypoint.sh 会自动执行
setup.py --defaults --skiponexist --clobber重建索引与配置,无需手工干预。
六、常用环境变量表
| 环境变量 | 说明 | 本指南值 |
|---|---|---|
ES_HEAP_SIZE | ES JVM 堆,同时设定 -Xms/-Xmx | 256m |
JAVA_HOME(entrypoint 内固定) | ES 2.4.6 需 Java 8 | /opt/jdk8 |
内存说明:ES 2.4.6 单节点归档场景堆无需过大,
256m即可稳定运行(实测ps可见-Xms256m -Xmx256m)。若单节点归档邮件量极大,可上调至512m,但不要超过宿主机物理内存的一半(ES 官方建议)。
七、常用配置参数说明(command)
镜像 ENTRYPOINT 固定为 /opt/entrypoint.sh,容器 PID 1 为该脚本,最终 exec apache2ctl -DFOREGROUND 前台接管 Apache:
| 配置方式 | 说明 |
|---|---|
ENTRYPOINT ["/opt/entrypoint.sh"] | 镜像内固定;chown 数据卷 → 起 ES → 确保索引 → 起 lmtpd → 前台跑 Apache |
command(compose 覆盖) | 不建议覆盖 ENTRYPOINT;如需临时排障可 docker exec 进容器操作 |
docker exec ponymail-server bash -lc 'tail -20 /var/log/ponymail-lmtpd.log' | 进入容器执行运维命令(如核对 ES 进程、查日志) |
与官方发行版部署的区别:Compose 方案把「systemd 托管的 ES/lmtpd」改为「entrypoint 脚本按序拉起」,这是容器内无 systemd 环境的标准做法;归档链路(LMTP → archiver.py → ES)与官方部署完全一致。
八、配置文件持久化(conf 挂载)
Pony Mail 的关键配置在容器内 /opt/ponymail/tools/ponymail.cfg(归档器/导入器)与 /opt/ponymail/site/api/lib/config.lua(mod_lua 后端)。如需持久化自定义配置,在 compose 中追加挂载并配合 --skiponexist 语义:
volumes:
- ./data:/opt/elasticsearch/data
- ./conf:/opt/ponymail/conf # 可选:持久化自定义配置
注意:
setup.py会把生成文件写到tools/ponymail.cfg与site/api/lib/config.lua。若将 conf 目录挂到/opt/ponymail/conf,只适用于「自行改 setup.py 输出路径」的高级场景;常规用法仍是进入容器修改这两个文件后docker compose restart ponymail。本指南镜像构建期已生成默认配置,开箱即用。
九、等价 docker run 运行方式
不写 docker-compose.yml 时,也可用单条 docker run(等价于 4.4 的 compose 配置):
docker run -d --name ponymail-server --restart unless-stopped \
-p 18080:8080 -p 8024:8024 -p 9200:9200 \
-e ES_HEAP_SIZE=256m \
-v "$PWD/data:/opt/elasticsearch/data" \
ponymail:0.11
十、常用运维命令
docker compose up -d # 启动
docker compose up -d --build # 重新构建并启动(改 Dockerfile/vendor 后)
docker compose ps # 查看状态
docker compose restart ponymail # 重启服务
docker compose down # 停止并删除容器(保留 ./data 数据卷)
docker compose logs -f ponymail # 查看容器日志(Apache 访问/错误、lmtpd 归档记录)
docker exec ponymail-server bash -lc 'tail -20 /var/log/ponymail-lmtpd.log' # 查 lmtpd 归档日志
docker exec ponymail-server bash -lc 'tail -20 /tmp/setup.log' # 查 setup.py 日志
十一、常见问题(FAQ)
11.1 构建报 curl: (7) Failed to connect to github.com port 443
原因:buildkit 构建网络无法直连 github.com(本指南实测 Connection refused)。
解决:不要在 RUN 步骤里 curl 外网;改为宿主机预下载制品到 vendor/ 后用 COPY 进镜像。Dockerfile 已按此方案编写。
11.2 docker compose up 报 failed to bind host port 0.0.0.0:8080/tcp: address already in use
原因:宿主 8080 已被其他进程占用(本指南实测宿主机 haproxy 占用 8080)。
解决:改用空闲端口映射,如 "18080:8080";或先停掉占用进程。
11.3 Web 打开 403 Forbidden
原因:DocumentRoot 在 /var/www 之外,Apache 默认目录授权不覆盖。
解决:vhost 内必须添加 <Directory /opt/ponymail/site> Require all granted </Directory>(Dockerfile 第 7 步已包含)。
11.4 Web 500 报 module 'cjson' not found
原因:mod_lua 基于 Lua 5.3,而 Ubuntu 的 lua-cjson 包只带 5.1/5.2 模块。
解决:对 mpx/lua-cjson 2.1.0 源码编译(make LUA_INCLUDE_DIR=/usr/include/lua5.3),把 cjson.so 放到 /usr/lib/x86_64-linux-gnu/lua/5.3/(Dockerfile 第 2 步已包含)。
11.5 archiver 报 Exception: ('Unexpected elasticsearch version ', (1, 9, 0))
原因:python-elasticsearch 版本不对——archiver.py 源码校验 ES_MAJOR == 2,客户端 1.9.0 会报错。
解决:安装 elasticsearch==2.4.0(Dockerfile 第 5 步已锁定),并确认 ES 引擎为 2.4.6。
11.6 ES 起不来,日志报拒绝 root 运行
原因:ES 2.x 拒绝以 root 身份启动。
解决:以 esuser 运行(entrypoint.sh 中 su esuser -c 'JAVA_HOME=/opt/jdk8 ES_HEAP_SIZE=256m /opt/elasticsearch/bin/elasticsearch -d'),并 chown -R esuser:esuser /opt/elasticsearch。
11.7 LMTP 投递后 ES count 不增加
解决:确认 lmtpd 已监听(docker exec ponymail-server bash -lc 'ss -tlnp | grep 8024')、archiver.py 无报错(/var/log/ponymail-lmtpd.log);同一 Message-ID 重复投递属于去重行为,count 不会增加。
十二、参考链接
- Apache Pony Mail 下载页:https://downloads.apache.org/ponymail/ponymail/
- Apache Pony Mail GitHub(已归档):https://github.com/apache/incubator-ponymail
- Elasticsearch 2.4.6 下载:https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.4.6/elasticsearch-2.4.6.tar.gz
- mpx/lua-cjson(Lua 5.3 cjson 源码):https://github.com/mpx/lua-cjson
- Docker Compose 文档:https://docs.docker.com/compose/