首页
/ code-server 部署到 Coder 工作区:install.sh 启动脚本、healthz 健康检查与官方模块方案

code-server 部署到 Coder 工作区:install.sh 启动脚本、healthz 健康检查与官方模块方案

2026-09-03 19:45:34作者:明树来

本文以 docs/coder.md 为主体,讲解如何在 Coder 工作区的 Terraform 模板中部署 code-server。读完后你可以掌握两套落地方案:一是在 coder_agentstartup_script 中用官方安装脚本拉起服务,并配置 coder_app/healthz 健康检查;二是直接引用官方发布的 Coder 模块。同时会深入源码说明 --auth none--port 参数的含义、/healthz 端点的真实实现以及心跳(heartbeat)机制如何避免健康检查“误伤”活跃状态判定。

为什么把 code-server 跑在 Coder 工作区里

Coder 的每个 workspace 由一个或多个 coder_agent 进程负责执行初始化脚本并代理端口。code-server 作为“浏览器中的 VS Code”,只需要在 agent 容器内启动一个监听本地端口的 HTTP 服务,再通过 coder_app 资源把 localhost:13337 暴露为工作区应用即可。docs/coder.md 给出的正是这条最典型的路径:agent 装服务、app 做路由与探活、Terraform 声明式管理三者。

方案一:startup_script 中用 install.sh 安装并启动

官方推荐在模板中使用仓库根目录提供的 install.sh 脚本。docs/coder.md 中的完整模板如下:

resource "coder_agent" "dev" {
  arch           = "amd64"
  os             = "linux"
  startup_script = <<EOF
    #!/bin/sh
    set -x
    # install and start code-server
    curl -fsSL https://code-server.dev/install.sh | sh -s -- --version 4.8.3
    code-server --auth none --port 13337 &
    EOF
}

resource "coder_app" "code-server" {
  agent_id     = coder_agent.dev.id
  slug         = "code-server"
  display_name = "code-server"
  url          = "http://localhost:13337/"
  icon         = "/icon/code.svg"
  subdomain    = false
  share        = "owner"

  healthcheck {
    url       = "http://localhost:13337/healthz"
    interval  = 3
    threshold = 10
  }
}

install.sh 的工作方式与 --version 参数

安装脚本支持 --version X.X.X 指定固定版本(模板中固定为 4.8.3,保证工作区可复现),也支持 --edge 安装最新 edge 版。从 install.sh 源码看,其核心行为包括:

  • 安装方式选择--method detect(默认)会按发行版选择包管理器——Debian/Ubuntu 走 deb 包,Fedora/CentOS/RHEL/openSUSE 走 rpm 包,Arch 走 AUR,FreeBSD/Alpine 走 npm,macOS 优先 Homebrew,其余系统直接从 GitHub Release 拉取;--method standalone 则强制把发行包解压到 --prefix(默认 ~/.local)。
  • 架构约束:Release 只为 Linux 的 amd64/arm64 和 macOS 的 amd64 构建,detect 模式在无匹配 Release 时会回退到 npm 安装,standalone 模式则会报错。这解释了模板里 coder_agentarch = "amd64" 写法——与工作区架构保持一致才能命中预构建产物。
  • 下载缓存:脚本会把所有下载的资产缓存在 ~/.cache/code-server,工作区重启时的二次安装会更快。

--auth none--port 13337:启动参数的源码依据

在 Coder 场景下关闭 code-server 自身的认证是有意为之——访问控制交给 Coder 的 workspace 代理层(share = "owner" 表示只有 workspace 属主可访问应用),code-server 不再重复要求密码。参数含义可从源码确认:

  • --authsrc/node/cli.ts 中定义了 AuthType 枚举,仅有 passwordnone 两个取值;src/node/cli.tssetDefaults 显示,若不显式指定,默认值是 password。因此在 Coder 模板中必须显式写 --auth none,否则工作区会多出一层需要密码的登录页。
  • --port:属于已被 --bind-addr 取代但仍可用的参数(见 src/node/cli.ts 注释 “These two have been deprecated by bindAddr”),--port 13337 即监听 13337 端口。也可以用 --bind-addr 127.0.0.1:13337 或环境变量 $PORT 覆盖,模板中选用 --port 是因为它更直观且与 coder_appurl 一一对应。
  • 启动命令末尾的 & 让 code-server 在后台运行,避免阻塞 startup_script 中后续可能的步骤。

coder_app 各字段的作用

  • url = "http://localhost:13337/":Coder 在该 agent 容器内代理此地址,浏览器访问的工作区应用实际由 Coder 路由进来,这也是为什么 --auth none 是安全可行的。
  • subdomain = false:以路径方式挂载(如 /code-server/),而不是独立子域名;对需要 base-path 适配的场景,code-server 本身带有对代理路径的处理补丁(见 patches/base-path.diff)。
  • share = "owner":只有 workspace 属主能打开该应用,弥补了 --auth none 取消认证后的访问控制缺口。
  • healthcheck:Coder 每隔 interval(3 秒)探测一次 url,连续 threshold(10 次)失败才判定应用不可用。选择 /healthz 而非根路径不是随意的,原因在于下面的实现细节。

/healthz 端点:源码级的健康检查机制

code-server 的 /healthz 路由在 src/node/routes/index.ts 中挂载,处理逻辑位于 src/node/routes/health.ts:它返回一个 JSON,包含 statusaliveexpired)和 lastHeartbeat 时间戳:

router.get("/", (req, res) => {
  res.json({
    status: req.heart.alive() ? "alive" : "expired",
    lastHeartbeat: req.heart.lastHeartbeat,
  })
})

alive() 的判定来自心跳模块 src/node/heart.ts:code-server 通过一个本地心跳文件记录活动,heartbeatInterval 为 60000 毫秒,alive() 的语义是“距上次心跳不足 60 秒”。任何真实用户请求都会触发 heart.beat() 刷新心跳。

关键的排除逻辑在 src/node/routes/index.ts

// /healthz|/healthz/ needs to be excluded otherwise health checks will make
// it look like code-server is always in use.
if (!/^\/healthz\/?$/.test(req.url)) {
  heart.beat()
}

健康检查请求本身不刷新心跳。这样设计的原因可以推断为:Coder 每 3 秒一次的探测如果计入“使用量”,会让 status 永远是 alive,健康检查就失去了反映“用户是否真的在用编辑器”的意义。对部署者而言的实用结论是:当 Coder 报告应用不健康、或你直接 curl 该端点看到 expired 时,说明 60 秒内没有任何真实用户活动,这可用于判断空闲工作区;而 HTTP 层探活(进程是否存活、端口是否监听)由 Coder 的 interval/threshold 机制独立保障。对应行为在单元测试 test/unit/node/routes/health.test.ts 中有覆盖。

方案二:使用官方 Coder 模块

对于不想在模板里手写安装命令的场景,Coder 官方在模块 registry 中发布了 code-server 模块,模板中只需引用:

module "code-server" {
  source     = "registry.coder.com/modules/code-server/coder"
  version    = "1.0.5"
  agent_id   = coder_agent.example.id
  extensions = ["dracula-theme.theme-dracula", "ms-azuretools.vscode-docker"]
}

其中 extensions 参数支持按 VS Code 扩展 ID 批量预装扩展。这一能力对应 code-server CLI 的 --install-extension 选项:src/node/cli.ts 说明其接受 ${publisher}.${name} 形式的扩展标识符或 vsix 文件,并可追加 @${version} 指定版本(例如 vscode.csharp@1.2.3)。模块本质上就是把“install.sh 安装 + 启动 + coder_app 声明 + 扩展预装”打包成了可版本化管理的 Terraform 组件。

常见问题与求助渠道

  • 版本固定:模板示例中 --version 4.8.3 是显式钉死的,避免 Coder 重建工作区时因上游发版导致行为漂移;需要升级时应显式改模板,而不是依赖“latest”。
  • 端口冲突13337 只是约定端口,若 agent 容器内已有服务占用,可更换 --port 并同步修改 coder_app.urlhealthcheck.url
  • 认证层混淆:若在 Coder 代理的应用地址上仍看到 code-server 自带登录页,说明 --auth none 未生效或启动命令被改写;反过来,若绕过 Coder 直接暴露 code-server 端口,则务必恢复密码认证(auth: password,密码仅可通过 $PASSWORD/$HASHED_PASSWORD 或配置文件传入,见 src/node/cli.ts)。
  • 求助渠道:官方文档建议遇到问题时到 Coder 的 coder/coder 项目 Discussions 区提问,code-server 与 Coder 同属 Coder 团队维护,该渠道能同时覆盖两侧的问题定位。

小结

在 Coder 工作区部署 code-server 的完整链路是:coder_agent.startup_script 通过 install.sh 安装固定版本(detect 模式按发行版选包管理器、无预构建产物时回退 npm),以 --auth none --port 13337 后台启动;coder_app 声明代理地址、属主级共享,并基于 /healthz 做 3 秒间隔、10 次阈值的健康检查——该端点由 health.ts 提供,且健康检查流量被有意排除在 src/node/routes/index.ts 的心跳刷新之外,从而区分“进程存活”与“真实使用”。更省事的替代方案是引用官方 registry.coder.com/modules/code-server/coder 模块,通过 extensions 参数(底层即 --install-extension)预装扩展。两条路径均以上述 Terraform 配置为最小可复制起点。

登录后查看全文
热门项目推荐
相关项目推荐