SOAP作为一种基于XML的远程调用协议,在不少企业内部系统里仍然承担着核心接口的职责。把这些原本跑在物理机或虚拟机上的SOAP服务迁移到Docker容器,不仅能统一运行环境,也方便在测试与生产之间无缝迁移。下面通过一个具体的Axis2服务示例,看看如何完成容器化部署。

一、准备SOAP服务工程
我们假设已经有一个基于Java的Axis2服务,打包后得到一个axis2-soap-demo.war文件,里面包含一个简单的SayHello服务,其WSDL可通过 /services/SayHello?wsdl 访问。传统方式下,我们会把该war包放进Tomcat的webapps目录并启动容器。
在容器化场景中,我们要把这种依赖关系固化到镜像里。为了避免每次部署都受宿主机JDK版本影响,应当明确指定基础镜像的版本,例如使用官方的 openjdk:8-jre-slim 搭配独立下载的Tomcat,或者直接使用官方的 tomcat:8.5-jre8 镜像。这样无论在哪台机器上构建,运行环境都完全一致。
二、编写Dockerfile
Dockerfile是整个容器化过程的核心,它描述了如何从零开始组装我们的SOAP服务镜像。以下是一个可直接使用的示例:
FROM tomcat:8.5-jre8 # 删除默认自带应用,减小体积 RUN rm -rf /usr/local/tomcat/webapps/* # 将本地构建好的war包放入webapps COPY axis2-soap-demo.war /usr/local/tomcat/webapps/ROOT.war # 设置时区,避免SOAP日志时间错乱 ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 暴露SOAP服务端口 EXPOSE 8080 # 启动Tomcat CMD ["catalina.sh", "run"]
上面的Dockerfile中,我们把war包命名为ROOT.war,这样服务会挂载在Tomcat根路径下,访问WSDL时路径更简洁。EXPOSE 8080只是声明,真正映射要在docker run时指定。
如果服务内部硬编码了WSDL里的endpoint地址,在容器重启后IP变化会导致客户端调用失败。更优的做法是通过环境变量让应用在启动时动态读取宿主映射地址,例如在web.xml或Spring配置中用 ${SOAP_BASE_URL} 占位,Dockerfile里用ENV声明默认值,运行时覆盖。
三、构建与运行容器
在Dockerfile同级目录执行构建命令,生成名为 soap-demo 的镜像:
docker build -t soap-demo:1.0 .
构建完成后,通过以下命令启动容器,并把宿主机8080端口映射到容器8080:
docker run -d --name soap1 -p 8080:8080 -e SOAP_BASE_URL=http://192.168.0.1:8080 -v /data/soap/logs:/usr/local/tomcat/logs soap-demo:1.0
这里我们把Tomcat日志目录挂载到宿主机的 /data/soap/logs,方便后续排查SOAP报文异常。同时通过 -e 参数传入了SOAP_BASE_URL,应用启动后会用这个地址生成WSDL里的service location。
启动后可用curl验证WSDL是否正常返回:
curl -s http://127.0.0.1:8080/services/SayHello?wsdl | head -20
如果看到完整的WSDL XML内容,说明容器内的SOAP服务已经对外可用。此时任意支持SOAP的客户端都能通过该地址发起调用。
四、多实例与扩展思考
当单容器无法满足并发时,可以基于同一个镜像启动多个容器,配合Nginx或Kubernetes Service做负载均衡。由于镜像本身无状态,只要日志和临时文件不写在容器内层,水平扩展非常容易。
另外需要注意,SOAP协议对时间字段敏感,容器默认UTC时区常导致时间戳差八小时。如前文Dockerfile所示,设置TZ环境变量并刷新localtime,是从根源上规避该问题的简单方案。对于更复杂的Axis2模块,也可把整个conf目录通过挂载方式外部化,做到配置与镜像分离。
五、常见误区
不少人在容器里直接写死WSDL的IP为容器内部地址,比如172.17.0.2,结果外部网络根本无法连通。正确思路是区分容器内部监听地址(一般是0.0.0.0)与外部访问地址(宿主IP或域名),前者由应用绑定,后者通过环境变量注入WSDL生成逻辑。
还有人忽略EXPOSE与实际-p映射的区别,以为写了EXPOSE就能从外面访问,结果安全组放通了但docker没做端口映射。务必记住EXPOSE只是文档声明,真正打通网络靠的是run阶段的-p参数或compose里的ports配置。