string.is Docker Compose 部署指南

string.is v1.38.0 的 Docker Compose 从零部署指南:官方镜像选型、编排文件逐项解说、部署与探活实测回显、环境变量表(含构建期统计域说明)、升级回滚与常见问题,Ubuntu 22.04 实测。

string.isstring.isDocker Compose容器化2026/10/31

string.is Docker Compose 部署指南

目标:在 Ubuntu 22.04 服务器上以 Docker Compose 完成 string.is v1.38.0 的从零部署与验证,覆盖镜像选型、部署步骤、环境变量、升级与常见问题。全部命令与回显已在 Ubuntu 22.04(x86_64)实测通过,服务发布于 http://localhost:8128 。

一、环境信息

本文实测环境与部署目标:

  • 操作系统:Ubuntu 22.04(x86_64)
  • 软件版本:string.is v1.38.0(官方最新版,2025-04-15 发布)
  • 部署方式:Docker Compose(单服务编排)
  • 应用监听端口:3000(服务内置,不可省略);宿主发布端口:8128(可自行更换,避开已占用端口即可)
  • 部署目录:/opt/string-is(编排文件 docker-compose.yml 所在目录)
  • 运行身份:应用进程以非特权用户 nextjs(uid 1001)运行(官方内置,无需配置)
  • 架构支持:官方发布 linux/amd64 与 linux/arm64 双架构应用包,Docker 会按主机架构自动匹配

string.is 是无状态 Web 服务:无数据库、无服务端配置文件、无持久化数据,编排里不需要任何卷——整个部署的核心工作只有「拉应用包 → 起服务 → 发布端口」三步。

二、镜像选型说明

string.is 的官方分发渠道与版本选择建议如下:

渠道地址说明
Docker Hub(官方推荐)daveperrett/string-is官方 README 指定的发布渠道,v1.38.0 实测约 218 MB(228,413,743 字节),基于 node:20-alpine,Next.js standalone 运行
源码构建GitHub 仓库 recurser/string-is 的 Dockerfile三阶段构建(deps → builder → runner),适合需要自定义统计域等深度定制的场景

版本选择两条原则:

  1. 锁定具体 tag,不用 latest:latest 会随官方发布漂移,部署与升级都应写死版本号(如 v1.38.0),升级时显式改 tag;
  2. tag 与 GitHub Release 一一对应:镜像 v1.38.0 对应 2025-04-15 发布的 v1.38.0,可在 https://hub.docker.com/r/daveperrett/string-is/tags 核对可用版本。

选型结论:常规部署直接用 Docker Hub 官方包 + 具体版本 tag;只有当需要修改构建期统计域名(见 7.2)等构建参数时才从源码构建。

三、前置条件

部署前确认宿主已具备:

  1. Docker Engine 与 Docker Compose 插件(docker compose version 可执行);
  2. 目标发布端口未被占用(本文用 8128,检查命令 ss -ltnp | grep 8128 无输出即空闲);
  3. 能访问 Docker Hub(拉取失败时的代理与镜像加速处理见第十一章)。

验证环境就绪:

docker compose version

实测回显:

Docker Compose version v2.39.2
ss -ltnp | grep 8128

实测回显(端口空闲时无输出,命令直接返回):

(无输出)

四、部署步骤

4.1 编排文件

创建部署目录并写入编排文件 /opt/string-is/docker-compose.yml:

name: string-is-stack

services:
  string-is:
    image: daveperrett/string-is:v1.38.0
    container_name: string-is-web
    restart: unless-stopped
    ports:
      - "8128:3000"

networks:
  default:
    name: string-is-net

逐项说明:

  • image: daveperrett/string-is:v1.38.0:官方应用包 + 锁定版本 tag(选型原则见第二章);
  • container_name:固定服务名,便于 docker logs/docker inspect 直接定位;不指定时 Compose 会按「目录名-服务名-序号」自动生成;
  • restart: unless-stopped:宿主重启后自动拉起(手动 stop 的除外);

💡 技术拓展:restart 策略有四种——no(默认,不自动重启)、on-failure[:次数](仅进程异常退出时重启,适合一次性任务)、always(任何情况都重启,包括手动 stop 后 daemon 重启时)、unless-stopped(类似 always,但尊重手动 stop)。Web 常驻服务的标准选择是 unless-stopped:既保证开机自启,又不会在人工停服维护后被 daemon 悄悄拉起。本文采用 unless-stopped。

  • ports: "8128:3000":宿主 8128 发布到应用 3000;3000 是应用内置监听端口,等号右边不能改;
  • networks:显式命名网络,便于多项目环境辨识;单项目部署也可以整个删掉,用 Compose 默认网络。

4.2 拉取应用包

docker pull daveperrett/string-is:v1.38.0

实测回显(输出节选,实测首尾):

v1.38.0: Pulling from daveperrett/string-is
Digest: sha256:dfe8775071d37ab6182fdefa8f20ed14beaa4aa5b9e9658904742e57b87d54d9
Status: Downloaded newer image for daveperrett/string-is:v1.38.0
docker.io/daveperrett/string-is:v1.38.0

再次执行时因本地已存在同版本包,回显为 Status: Image is up to date for daveperrett/string-is:v1.38.0,属正常现象。

4.3 启动服务

cd /opt/string-is && docker compose up -d

实测回显(逐行照抄):

 Container string-is-web Creating
 Container string-is-web Created
 Container string-is-web Starting
 Container string-is-web Started

4.4 验证服务

第一步:编排状态。

docker compose ps

实测回显:

NAME           IMAGE                         COMMAND                  SERVICE     CREATED          STATUS          PORTS
string-is-web  daveperrett/string-is:v1.38.0 "docker-entrypoint.s…"   string-is   14 seconds ago   Up 13 seconds   0.0.0.0:8128->3000/tcp, [::]:8128->3000/tcp

STATUS 为 Up、端口映射 0.0.0.0:8128->3000/tcp 生效即启动成功。

第二步:服务日志。

docker logs string-is-web

实测回显:

Listening on port 3000

Next.js standalone 服务的启动日志只有这一行,出现即代表监听就绪;没有报错堆栈、没有配置告警——无状态服务的日志面就是这么小。

第三步:HTTP 探活。

curl -s -o /dev/null -w '首页 HTTP %{http_code}\n' http://localhost:8128/

实测回显:

首页 HTTP 200
curl -s http://localhost:8128/ | head -c 220

实测回显(输出节选):

<!DOCTYPE html><style data-cmpld="true">._t5xn1vki body{background-color:var(--_120p1lg)}@media only screen and (max-width:768px){._1irj1j28 body{background-color:transparent}}</style><html style="--_120p1lg:#FAFBFF" cla

返回 200 且响应体为 string.is 页面 HTML,浏览器访问 http://<服务器IP>:8128 即可使用全部 49 个转换器页面。运行身份与重启策略复核:

docker inspect string-is-web --format '运行用户: {{.Config.User}} | 状态: {{.State.Status}} | 重启策略: {{.HostConfig.RestartPolicy.Name}}'

实测回显:

运行用户: nextjs | 状态: running | 重启策略: unless-stopped

五、数据持久化说明

string.is 不需要任何持久化配置,编排里没有也不应有卷:

  1. 服务端无状态:转换逻辑全部在访问者浏览器执行,服务端不落盘、不写库,应用包本身只读;
  2. 用户偏好存浏览器:转换选项保存在访问者浏览器的 LocalStorage(官方隐私政策明示),换浏览器或清缓存后回到默认值;
  3. 升级零迁移:换版本只是替换应用包,不存在「数据目录」「配置兼容」「数据库 schema」等升级负担。

这一特性也决定了备份策略为空:没有需要备份的服务端数据;若要固化的是用户自己的转换选项,浏览器侧导出即可,与部署无关。

六、环境变量表

v1.38.0 运行时环境变量实测(docker exec string-is-web env 过滤关键项)与官方 Dockerfile 定义对照:

变量实际值作用可否运行时修改
PORT3000应用监听端口(Dockerfile ENV PORT 3000)否,固定值,编排里通过端口映射改对外发布端口
NODE_ENVproductionNode 运行模式否,生产部署保持 production
NEXT_TELEMETRY_DISABLED0Next.js 遥测开关(构建阶段为 1 关闭)否
NEXT_PUBLIC_ANALYTICS_DOMAINstring.is匿名统计(Plausible)的归属域名,默认指向官网否,见下方说明

关于 NEXT_PUBLIC_ANALYTICS_DOMAIN 需要特别注意:Next.js 的 NEXT_PUBLIC_* 变量是构建期内联进前端产物的,运行时用 environment: 覆盖不会生效。它的取值来自构建参数 ANALYTICS_DOMAIN(Dockerfile 中 ARG ANALYTICS_DOMAIN=string.is)。因此:

  • 直接使用官方应用包:统计脚本以官网域名上报,自托管实例的匿名访问量会计入官方公开面板,建议在反代层拦截对 plausible.io 的请求(或在公司网络出站过滤),即彻底关闭统计;
  • 需要归属自己域名:从源码构建并在构建时覆盖,docker build --build-arg ANALYTICS_DOMAIN=stats.example.com .。

七、配置参数说明

7.1 服务端配置

string.is 没有服务端配置文件——没有 YAML/TOML/env 文件需要挂载,全部行为由应用包内置(端口、安全响应头、界面语言等)。「配置」维度上唯一可调整的就是第六章的环境变量与构建参数。

7.2 安全响应头(内置)

应用内置的安全响应头在部署后可直接验证(这是 string.is 的卖点之一,部署时值得确认一遍):

curl -sI http://localhost:8128/ | grep -iE 'content-security|x-frame'

实测回显(输出节选,实测首行):

Content-Security-Policy: base-uri 'none'; connect-src 'self' https://plausible.io; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; img-src 'self'; manifest-src 'self'; script-src 'self' 'unsafe-eval' https://plausible.io; script-src-elem 'self' 'unsafe-eval' https://plausible.io https://plausible.io; style-src 'self' 'unsafe-inline';
X-Frame-Options: DENY

default-src 'none' 白名单模型 + X-Frame-Options: DENY 禁嵌入,应用层安全基线开箱即得,反代层无需再补安全头。

7.3 反向代理接入(可选)

若需要域名 + TLS 统一入口,在宿主 Nginx 中新增 server 块(以 string.example.com 为例,证书由 certbot 签发):

server {
    listen 443 ssl;
    server_name string.example.com;
    ssl_certificate     /etc/letsencrypt/live/string.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/string.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8128;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

要点:proxy_pass 指向宿主发布端口 8128;string.is 自带头已完备,Nginx 不需要额外添加安全头;若按第六章方案关闭统计,可在此加 location / 内的 proxy_set_header 之外配合出站过滤,或在应用侧改用自建统计域(源码构建)。

重载前先做配置语法检查:

nginx -t

实测回显:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

语法通过后执行 systemctl reload nginx 生效,浏览器访问 https://string.example.com 验证页面正常打开。

八、配置文件挂载

结论先行:没有需要挂载的配置文件,编排里不要写 volumes。string.is 应用包自包含全部静态产物与运行时,官方 Dockerfile 也没有定义任何可挂载路径;强行挂载空目录到应用路径反而会导致页面 404。需要定制的场景(统计域、品牌文案、界面语言)都只能通过修改源码重新构建实现,不属于挂载范畴。

九、等价 docker run

不用 Compose 时,等价的单命令部署:

docker run -d --name string-is-run -p 8129:3000 daveperrett/string-is:v1.38.0

实测回显(容器 ID 样例):

28a648779df54c9f59d2da2276f687b4582b164d48f3eb8f27b9e1653d5e5f15

探活验证:

curl -s -o /dev/null -w 'docker run 实例 HTTP %{http_code}\n' http://localhost:8129/

实测回显:

docker run 实例 HTTP 200

验证后清理:

docker rm -f string-is-run

两种方式的取舍:单机长期服务用 Compose(编排文件即文档,升级/回滚/重建都是一条命令);临时试用或一次性验证用 docker run 更直接。生产建议 Compose,restart: unless-stopped 与编排可追溯性是 docker run 不具备的。

十、常用运维命令

在 /opt/string-is 目录下:

docker compose ps

实测回显:

NAME           IMAGE                         COMMAND                  SERVICE     CREATED          STATUS          PORTS
string-is-web  daveperrett/string-is:v1.38.0 "docker-entrypoint.s…"   string-is   14 seconds ago   Up 13 seconds   0.0.0.0:8128->3000/tcp, [::]:8128->3000/tcp

日志跟踪(应用日志量极小,-f 长跟通常无新输出):

docker logs --tail 20 string-is-web

实测回显:

Listening on port 3000

重启服务(编排文件或宿主变更后):

docker compose restart

实测回显(逐行照抄):

 Container string-is-web Restarting
 Container string-is-web Started

停止并移除服务与网络(应用包保留在本地):

docker compose down

实测回显(逐行照抄):

 Container string-is-web Removing
 Container string-is-web Removed
 Network string-is-net Removing
 Network string-is-net Removed

升级流程(以未来升级到 v1.39.0 为例):

  1. 编辑 docker-compose.yml 把 image 的 tag 从 v1.38.0 改为 v1.39.0;
  2. 拉取并滚动重建:
docker compose up -d

Compose 会自动完成「拉新包 → 停旧服务 → 以新包重建 → 启动」,全程数秒;因服务无状态,无任何数据迁移步骤。

  1. 探活确认:
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8128/

预期输出 200。

回滚:把 tag 改回 v1.38.0 再执行一次 docker compose up -d 即可(旧版本应用包仍在本地时无需重新拉取)。

十一、常见问题

Q1:拉取应用包超时或失败?

国内网络直连 Docker Hub 不稳定时的处理链:优先配置镜像加速器(/etc/docker/daemon.json 的 registry-mirrors 后重启 docker 服务);仍失败可走 HTTP 代理拉取:

HTTPS_PROXY=http://192.168.1.62:7897 docker pull daveperrett/string-is:v1.38.0

实测环境曾全程走代理完成拉取,拉取成功判据同 4.2:回显 Digest: sha256:dfe877… 与 Status: Downloaded newer image(或已存在时 Image is up to date)。

Q2:8128 端口被占用,docker compose up 报错?

报错形如 Error starting userland proxy: listen tcp4 0.0.0.0:8128: bind: address already in use。改编排文件 ports 左侧宿主端口为空闲端口(如 "8129:3000")后重新 docker compose up -d;右侧 3000 保持不变。

Q3:升级后发现统计请求发往 plausible.io?

这是官方应用包的默认行为(构建参数 ANALYTICS_DOMAIN 默认 string.is,见第六章)。运行时改环境变量无效;关闭方案是在反代/出站层过滤 plausible.io,归属自有域则必须源码构建并传 --build-arg ANALYTICS_DOMAIN=<你的域>。

Q4:访问出现 502/连接拒绝?

按顺序排查:docker compose ps 看服务是否 Up;docker logs string-is-web 是否有 Listening on port 3000;curl -s -o /dev/null -w '%{http_code}' http://localhost:8128/ 在宿主本地探活。宿主本地 200 而外部不通时,检查防火墙(ufw status)或云安全组是否放行发布端口。

Q5:arm64 主机(如 Apple Silicon 虚拟机、树莓派)能否部署?

可以。官方发布 linux/amd64 与 linux/arm64 双架构应用包,docker pull 自动匹配主机架构,编排文件无需任何改动。

Q6:需要接入 LDAP/SSO 或限制访问?

string.is 自身无任何鉴权体系。公网暴露时在反代层加认证(如 Nginx auth_basic + htpasswd,或 OAuth2 Proxy);内网部署则依赖网络边界控制。服务不产生访问日志,审计需求同样要在反代层实现。

十二、参考链接

string.is Docker Compose 部署指南 - Quzuo