WeTTY 3.0.0 Docker Compose 部署实战

WeTTY v3.0.0 官方镜像 Docker Compose 全流程部署实测:wettyoss/wetty + openssh-server 双容器编排、三层连通性验证、浏览器终端真实登录与 HTTPS 反代要点。

WeTTYSSH终端工具2026/10/30

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)
Docker29.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 容器来充当目标机,生产环境中把它换成真实服务器地址即可。

二、镜像与版本选型

服务镜像说明
wettywettyoss/wetty:latest官方唯一发布渠道(Docker Hub wettyoss 组织),1.8M+ 拉取
sshdlinuxserver/openssh-server:latest轻量 Alpine 系 sshd,实验/生产均可复用

版本选型的三个关键结论(均为实测):

  1. 镜像名认准 wettyoss/wetty。旧教程里广泛流传的 buttermilk/wetty 在 Docker Hub 已不存在(API 返回 object not found),项目现由 butlerx/wetty 仓库维护,镜像发布在 wettyoss 组织下;
  2. latest 是滚动标签,锁版本用 digest。官方没有提供 v3.0.0 这类语义化版本标签(只有 latest/main/sha-<commit>),要锁定本次实测版本可使用 digest 引用 wettyoss/wetty@sha256:17a07724594f04e7c4c0b3b8d07c6feb882dfe65b869831f280fff0d882e6075(对应 v3.0.0,2026-10-25 构建);
  3. 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 挂代理。

三、前置条件

  1. 服务器已安装 Docker 与 Compose 插件;
  2. 端口规划:8152(Web 入口,放行给用户浏览器);2222/2223 是 sshd 侧端口,生产部署在纯内网时无需对用户网段开放;
  3. 准备一个 SSH 目标机:真实服务器、云主机或 sshd 容器均可,需有可登录账号(本实验 admin/q1w2e3r4);
  4. 磁盘余量 ≥ 2GB(两镜像约 1GB,wetty 解压后 940MB);
  5. 确认目标 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;
}

两个要点:

  1. WebSocket 升级头必须配(Upgrade/Connection 两行),否则页面能开但终端连不上,这是 Web 终端反代最高频错误;
  2. 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 引用锁版本,永远不漂移。

十、部署完成后的下一步