WeTTY 3.0.0 Docker Compose 部署实战
WeTTY v3.0.0 官方镜像 Docker Compose 全流程部署实测:wettyoss/wetty + openssh-server 双容器编排、三层连通性验证、浏览器终端真实登录与 HTTPS 反代要点。
WeTTY 3.0.0 Docker Compose 部署实战
目标:在真实服务器上以 Docker Compose 部署 WeTTY v3.0.0 官方镜像(wettyoss/wetty),并配套一个 linuxserver/openssh-server 容器作为 SSH 目标机,完成从「空栈」到「浏览器里登录终端、执行命令拿到真实回显」的全流程实操。组件定位与同类对比见 《WeTTY 3.0.0 介绍与选型指南》,全部参数说明见 《WeTTY 3.0.0 配置参数详解》,踩坑排障实录见 《WeTTY 3.0.0 常用操作与排障详解》。
一、环境信息
本次实测全程在真实服务器视角完成,环境基线如下:
| 项目 | 值 |
|---|---|
| 服务器操作系统 | Ubuntu 24.04.5 LTS(x86_64,内核 7.0.0-38-generic) |
| Docker | 29.9.2(含 Docker Compose v5.6.1) |
| WeTTY 版本 | 3.0.0(wettyoss/wetty:latest,2026-10-25 构建,Node v22.23.4) |
| SSH 目标机 | linuxserver/openssh-server:latest(Alpine 3.24.3,OpenSSH Server) |
| 服务端口 | 8152(WeTTY Web 入口)、2223(宿主直连 sshd 用,仅实验验证需要) |
| 实验网段 | 10.89.85.0/24(自定义 bridge:wetty=10.89.85.10,sshd=10.89.85.20) |
| 实验账号 | admin / q1w2e3r4(sshd 侧登录账号) |
架构一句话:浏览器 → WeTTY(8152)→ sshd(2222,容器内网)。WeTTY 本身无状态,SSH 目标机可以是任意能被 WeTTY 容器访问到的 sshd——本实验在同个 Compose 网络里起一个轻量 sshd 容器来充当目标机,生产环境中把它换成真实服务器地址即可。
二、镜像与版本选型
| 服务 | 镜像 | 说明 |
|---|---|---|
| wetty | wettyoss/wetty:latest | 官方唯一发布渠道(Docker Hub wettyoss 组织),1.8M+ 拉取 |
| sshd | linuxserver/openssh-server:latest | 轻量 Alpine 系 sshd,实验/生产均可复用 |
版本选型的三个关键结论(均为实测):
- 镜像名认准 wettyoss/wetty。旧教程里广泛流传的
buttermilk/wetty在 Docker Hub 已不存在(API 返回 object not found),项目现由 butlerx/wetty 仓库维护,镜像发布在 wettyoss 组织下; - latest 是滚动标签,锁版本用 digest。官方没有提供 v3.0.0 这类语义化版本标签(只有 latest/main/sha-<commit>),要锁定本次实测版本可使用 digest 引用
wettyoss/wetty@sha256:17a07724594f04e7c4c0b3b8d07c6feb882dfe65b869831f280fff0d882e6075(对应 v3.0.0,2026-10-25 构建); - v3.0.0 有两个部署层面的硬变化:其一,
ALLOWEDORIGINS从可选变为必配,缺失时容器会崩溃并无限重启(实测复现,见 D1 篇排障 1);其二,Web 界面默认挂在根路径/而不是 v2 的/wetty(实测/wetty返回 404)。从 v2 升级或按旧教程抄配置时这两处必改。
⚠️ 网络说明:本次实测环境访问 Docker Hub API 与 GitHub 需走代理
192.168.1.72:7897(用于核对镜像 tag 与官方文档),而镜像拉取走本机 Docker 配置的国内镜像加速即可直连成功(wettyoss/wetty 与 linuxserver/openssh-server 均在加速白名单内)。若你的环境直连 Docker Hub 困难,优先配置 registry mirror,而不是给 docker daemon 挂代理。
三、前置条件
- 服务器已安装 Docker 与 Compose 插件;
- 端口规划:8152(Web 入口,放行给用户浏览器);2222/2223 是 sshd 侧端口,生产部署在纯内网时无需对用户网段开放;
- 准备一个 SSH 目标机:真实服务器、云主机或 sshd 容器均可,需有可登录账号(本实验 admin/q1w2e3r4);
- 磁盘余量 ≥ 2GB(两镜像约 1GB,wetty 解压后 940MB);
- 确认目标 sshd 的认证方式:WeTTY 默认走
password认证,目标机需允许密码登录(注意部分镜像默认关闭 password 认证,见 D1 篇排障 3);用密钥登录则改走 SSHKEY 参数(见 C1 篇)。
四、编写 Compose 编排文件
创建项目目录并编写 docker-compose.yml:
mkdir -p ~/wetty && cd ~/wetty
vi docker-compose.yml
完整编排内容(可直接复制):
services:
wetty:
image: wettyoss/wetty:latest
# 生产建议改用 digest 锁版本:
# image: wettyoss/wetty@sha256:17a07724594f04e7c4c0b3b8d07c6feb882dfe65b869831f280fff0d882e6075
container_name: wetty-web
ports:
- "8152:3000"
environment:
SSHHOST: sshd
SSHPORT: "2222"
TITLE: WeTTY Lab
ALLOWEDORIGINS: "http://192.168.1.200:8152,http://localhost:8152"
depends_on:
- sshd
networks:
wetty-lab:
ipv4_address: 10.89.85.10
restart: unless-stopped
sshd:
image: linuxserver/openssh-server:latest
container_name: wetty-sshd
environment:
USER_NAME: admin
USER_PASSWORD: q1w2e3r4
PASSWORD_ACCESS: "true"
ports:
- "2223:2222"
networks:
wetty-lab:
ipv4_address: 10.89.85.20
restart: unless-stopped
networks:
wetty-lab:
driver: bridge
ipam:
config:
- subnet: 10.89.85.0/24
逐块解说:
- wetty.environment 是全部配置入口(v3 无配置文件依赖,
--conf为可选项):SSHHOST: sshd——SSH 目标主机名,直接用 Compose 内的服务名做 DNS;SSHPORT: "2222"——linuxserver/openssh-server 容器内监听 2222(注意不是 22);TITLE: WeTTY Lab——浏览器标签页标题(实测页面<title>WeTTY Lab</title>);ALLOWEDORIGINS——v3 必配,列出用户实际访问的完整来源。用 IP 访问就写http://<服务器IP>:8152,用域名就写http://terminal.example.com。逗号分隔可多值。这个值漏配服务起不来,写错则浏览器握手被拒,是本次部署第一大坑;
- wetty.ports:容器内固定监听 3000,对外映射 8152;
- sshd.environment:三个变量缺一不可——
USER_NAME指定登录账号,USER_PASSWORD是真正的密码值,PASSWORD_ACCESS: "true"是打开 sshd 密码认证的开关(该镜像默认PasswordAuthentication no,且变量名极易误解,详见 D1 篇排障 3); - sshd.ports:把 2222 映射到宿主 2223 仅用于「不经 WeTTY 直连验证 sshd 是否正常」,生产可删除;
- networks:实验要求固定网段与静态 IP,用 ipam 声明
10.89.85.0/24;不需要固定 IP 时整段可简化为networks: [wetty-lab]。
五、启动与初始化验证
cd ~/wetty
docker compose up -d
实测启动输出:
Container wetty-sshd Starting
Container wetty-sshd Started
Container wetty-web Starting
Container wetty-web Started
确认容器状态:
docker compose ps
NAME STATUS PORTS
wetty-sshd Up 2 minutes 0.0.0.0:2223->2222/tcp
wetty-web Up 2 minutes 0.0.0.0:8152->3000/tcp
看 WeTTY 日志确认监听成功:
docker logs wetty-web 2>&1 | tail -2
{"base":"/","label":"Wetty","level":"info","message":"Starting server","port":3000,"timestamp":"2026-10-30T00:35:12.069Z","title":"WeTTY Lab"}
{"connection":"http","label":"Wetty","level":"info","message":"Server started","port":3000,"timestamp":"2026-10-30T00:35:12.077Z"}
日志为 JSON 行格式,关键看 message: Server started。如果这里反复出现 Starting server + error in server 成对日志,就是 ALLOWEDORIGINS 缺失导致的崩溃循环(排障 1)。
三层递进验证:
# 1) HTTP 页面可达,标题正确
curl -s http://localhost:8152/ | grep -oE '<title>[^<]*</title>'
<title>WeTTY Lab</title>
# 2) Socket.IO 握手(必须带允许的 Origin)
curl -s -H "Origin: http://192.168.1.200:8152" \
"http://localhost:8152/socket.io/?EIO=4&transport=polling" | head -c 120
0{"sid":"V0dJF8S1nohB5otLAAAA","upgrades":["websocket"],"pingInterval":3000,"pingTimeout":7000,"maxPayload":1000000}
# 3)不经 WeTTY 直连 sshd,验证目标机认证正常(实验机装了 paramiko 的 Python 方式)
python3 -c "
import paramiko
t = paramiko.Transport(('127.0.0.1', 2223)); t.connect()
t.auth_password('admin','q1w2e3r4')
print('sshd auth OK')
t.close()"
sshd auth OK
三层分别回答三个问题:页面在不在(HTTP)、WebSocket 通道通不通(Origin 校验)、目标机能不能登(SSH 认证)。第 3 层通过后再进浏览器测试,能把「WeTTY 问题」和「sshd 问题」干净地分开。
六、浏览器终端登录实测
浏览器打开 http://192.168.1.200:8152/(注意 v3 就是根路径,没有 /wetty 后缀)。因为编排里没有预置 SSHUSER,页面终端会先提示输入用户名,随后提示输入密码,完整交互实测回显:
Enter your username: admin
Warning: Permanently added '[sshd]:2222' (ED25519) to the list of known hosts.
admin@sshd's password:
Welcome to OpenSSH Server
453d12874af2:~$
登录后执行一组命令验证远端真实执行:
453d12874af2:~$ whoami; hostname; uname -sr; cat /etc/alpine-release
admin
453d12874af2
Linux 7.0.0-38-generic
3.24.3
whoami/hostname/uname 全部来自 sshd 容器(Alpine 3.24.3),证明命令确实在目标机上执行而非本地回显。若想跳过用户名交互,把地址写成 http://192.168.1.200:8152/ssh/admin(URL 路径预置用户名),或在编排里给 wetty 服务加 SSHUSER: admin。
故意输错密码的场景也实测过,回显与原生 SSH 完全一致:
admin@sshd's password:
Permission denied, please try again.
七、日常生命周期操作
# 看实时日志(含每个连接的 accepted 记录)
docker logs -f wetty-web
连接进来时会多一条 {"label":"Wetty","level":"info","message":"Connection accepted.","timestamp":...}。
# 改完 compose 后重建(v3 无状态,直接重建不丢东西)
docker compose up -d --force-recreate wetty
# 整栈停止与删除(容器网络一并清理)
docker compose down
# 单独重启 wetty
docker compose restart wetty
验证容器资源占用(实测):
docker stats --no-stream wetty-web wetty-sshd
wetty-web 32.59MiB / 15.07GiB 0.35%
wetty-sshd 3.758MiB / 15.07GiB 0.00%
两个容器合计不到 40MiB,常年 CPU 近零——这也是「按需起、按需停」都不心疼的组件。
八、HTTPS 反向代理(生产必读)
生产环境务必走 HTTPS:终端里流过的是密码与完整 Shell 会话,明文 HTTP 等于裸奔。WeTTY 官方文档给出 Nginx/Apache 两种反代模板,Nginx 核心配置:
location / {
proxy_pass http://127.0.0.1:8152;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 21600;
}
两个要点:
- WebSocket 升级头必须配(Upgrade/Connection 两行),否则页面能开但终端连不上,这是 Web 终端反代最高频错误;
- ALLOWEDORIGINS 要同步改为对外域名,如
https://terminal.example.com——用户从 HTTPS 域名进来,Origin 就是它,写内网地址会被拒。
若希望挂在子路径(与既有站点共存),给 wetty 加 BASE: /wetty 环境变量,Nginx location 同步改为 location /wetty/,访问地址变为 https://terminal.example.com/wetty/。
九、升级与版本锁定
# 升级到最新
cd ~/wetty
docker compose pull wetty
docker compose up -d wetty
升级前两件事:一是记下当前 digest(docker images --digests | grep wetty),便于回退;二是看一眼上游 release note——v2 → v3 就属于有破坏性变化的升级(ALLOWEDORIGINS 必配、路径改根、输入协议事件从 data 改为 input),不是无痛替换。追求绝对稳定就用 digest 引用锁版本,永远不漂移。
十、部署完成后的下一步
- 所有环境变量的完整语义与组合用法:《WeTTY 3.0.0 配置参数详解》;
- 起不来、连不上、登录失败的逐条排障实录:《WeTTY 3.0.0 常用操作与排障详解》。