当你不再把 Coder 只看作一个远程 IDE 平台,它就更容易理解了。Coder 的核心是按需给你可重复的开发环境。用户点一个按钮或跑一个 CLI 命令,Coder 就从预定义的模板部署一个工作空间。这个工作空间可以跑在 Docker 容器、Kubernetes 集群、云 VM 或其他基础设施目标里。开发者用 VS Code、JetBrains IDE、终端或浏览器编辑器连上去。
Coder 任务(Coder Tasks)建立在同一个基础上,但把它重新用于 AI 编程代理。任务不是给人类开发者交互用的工作空间,而是给自主代理跑的隔离、临时工作空间。这个区别很重要。Coder 任务不是一个独立的执行平台。它们就是标准的 Coder 工作空间,带一个面向 AI 代理的界面。
Coder 基本模型
理解 Coder 需要三个概念:工作空间(workspaces)、模板(templates)和配置器(provisioners)。
工作空间是一个隔离的开发环境。它是开发者或 AI 代理交互的计算实体。模板定义这个工作空间怎么构建。Coder 模板用 Terraform 写,这意味着它们可以描述本地容器、Kubernetes Pod、云 VM、持久卷、代理、暴露的应用、元数据和用户可配置的参数。
配置器是执行 Terraform 代码的进程。这个细节比看起来重要。你在模板里指定的基础设施提供商跑在配置器跑的地方,不是在新创建的工作空间里。
Coder server -> Provisioner runs Terraform -> Docker, Kubernetes, or cloud provider creates workspace如果你的模板用 Docker 提供商,跑配置器的机器上必须有 Docker 访问。如果配置器本身是容器化的,那个容器需要直接访问主机的 Docker socket、远程 Docker 主机,或你配置的任何 Docker 连接。
模板是带 Coder 数据源的 Terraform
一个最小的模板通常声明 Coder 提供商、一个基础设施提供商、工作空间数据源、一个 Coder 代理和构成工作空间的资源。
terraform {
required_providers {
coder = {
source = "coder/coder"
version = ">= 2.13"
}
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
provider "docker" {}
data "coder_provisioner" "me" {}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_agent" "main" {
arch = data.coder_provisioner.me.arch
os = "linux"
}Coder 代理让 Coder 服务器连进工作空间、暴露运行的 web 应用、执行启动脚本和上报状态。基础设施提供商创建环境。用 Docker,就是容器和卷。用 Kubernetes,就是 Pod 和持久卷声明。用云提供商,就是 VM 和持久磁盘。
Terraform 变量与 Coder 参数
最容易搞混的地方之一是 Terraform 变量和 Coder 参数的区别。
Terraform 变量由模板管理员在模板推送到服务器时设置。它们用于基础设施级配置和强制静态值,用户不应该选这些。Coder 参数则是在用户创建工作空间或任务时展示给用户。它们让用户选容器镜像、地理区域、CPU 大小、LLM 模型或自定义工作目录这类东西。
| 概念 | 设置者 | 设置时间 | 最适用于 |
|---|---|---|---|
| Terraform 变量 | 模板管理员 | 模板推送时 | 机密、默认值、基础设施配置 |
| Coder 参数 | 最终用户 | 工作空间或任务创建时 | 用户选项 |
这个例子同时展示两者:
variable "anthropic_api_key" {
type = string
sensitive = true
}
data "coder_parameter" "container_image" {
name = "container_image"
display_name = "Container Image"
type = "string"
default = "ubuntu:22.04"
mutable = false
}coder_parameter 块是一个数据源。在工作空间创建期间,它从 Coder UI 取用户选的值。
这里有一个安全细节值得知道。把变量标为 sensitive = true 能阻止 Terraform 和 Coder 日志打印这个值,但一旦这个值进了运行中的工作空间,它就不再受保护。如果你把机密作为纯环境变量注入容器,任何有 shell 访问权限的人都能读它。
模板执行的两个阶段
Coder 模板在模板导入时和在工作空间创建时表现不一样。在 coder templates push 期间,Coder 解析 Terraform 代码、提取定义的参数、构建 UI 表单、要 Terraform 变量的值,并检测模板是否支持 AI 任务。
在工作空间创建期间,Coder 把那个表单展示给用户、存下选的参数值、跑 terraform apply,让 data "coder_parameter" 块在执行中取这些值。这种分叉是 Coder 参数一开始让人感觉奇怪的原因。它们不是普通的 Terraform 变量。它们是 Coder 管理的值,Terraform 只在 apply 阶段读它们。
临时参数(Ephemeral parameters)加了一条严格规则。如果你声明 ephemeral = true,这个参数也必须标为 mutable,并且必须有默认值。
data "coder_parameter" "api_key" {
name = "api_key"
type = "string"
mutable = true
ephemeral = true
default = ""
}临时参数在你想要在创建时提供敏感或临时值、但不想存到工作空间长期元数据里时有用。
Coder 任务加了什么
Coder 任务提供一个简化界面,在隔离的工作空间里跑 AI 编程代理。用户用自然语言 prompt 创建一个任务。Coder 存下 prompt。Terraform 部署工作空间。模板取回存的 prompt,传给环境里的代理集成。
用户提交任务 prompt
|
v
Coder 存 prompt
|
v
Terraform 部署工作空间
|
v
代理收 prompt 并在工作空间里跑每个任务有自己的工作空间。这给 AI 代理一个隔离的文件系统、干净的依赖、工具访问和运行时环境。工作空间通常不需要 GPU,因为代理通过网络跟外部 LLM API 通信。Coder 模板在定义 coder_ai_task 资源时就变得支持任务了。
resource "coder_ai_task" "task" {
app_id = module.opencode.task_app_id
}存的任务 prompt 可以用 data "coder_task" 数据源取回。
data "coder_task" "me" {}
module "opencode" {
ai_prompt = data.coder_task.me.prompt
}从最终用户角度看,启动一个任务可以简单到一条命令:
coder tasks create \
--template my-template \
--parameter container_image="python:3.12" \
"Refactor the authentication module"关键部分是任务 prompt 变成基础设施的输入。工作空间围绕代理要做的那个工作来创建。
OpenCode 的定位
OpenCode Coder 模块把 OpenCode 集成进 Coder 工作空间。它可以把 OpenCode 暴露为交互式应用、在后台任务模式跑它、传 prompt 进去、配置认证或模型设置。
一个简单的模块配置长这样:
module "opencode" {
source = "registry.coder.com/coder-labs/opencode/coder"
version = "0.1.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
}用于任务时,它连到 data.coder_task.me.prompt 和 coder_ai_task 资源。
data "coder_task" "me" {}
resource "coder_ai_task" "task" {
app_id = module.opencode.task_app_id
}
module "opencode" {
source = "registry.coder.com/coder-labs/opencode/coder"
version = "0.1.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
ai_prompt = data.coder_task.me.prompt
auth_json = data.coder_parameter.opencode_auth_json.value
config_json = data.coder_parameter.opencode_config_json.value
opencode_version = "latest"
}如果你只想要 OpenCode 在工作空间终端里、不要 web UI 或任务报告,模块也支持纯 CLI 模式。
module "opencode" {
source = "registry.coder.com/coder-labs/opencode/coder"
version = "0.1.1"
agent_id = coder_agent.main.id
workdir = "/home/coder"
report_tasks = false
cli_app = true
}要点:Coder 提供工作空间并管理它的生命周期,OpenCode 提供工作空间里的代理体验。
Docker 提供商陷阱
很多本地 Coder 设置依赖 Docker,所以 Docker Terraform 提供商变成模板逻辑的一大块。默认它跟本地 Unix socket 通信。
provider "docker" {
host = "unix:///var/run/docker.sock"
}它也可以配置成通过 SSH 连远程主机。
provider "docker" {
host = "ssh://user@remote-host:22"
ssh_opts = ["-o", "StrictHostKeyChecking=no"]
}关键细节是 SSH 密钥和 Docker 访问必须在配置器跑的地方存在。如果配置器在容器里跑,把 SSH 密钥挂载到你的 laptop 上不够。配置器容器需要挂载访问它。
Docker socket 权限是另一个常见问题。如果 Coder 在 Docker 沙箱里跑,但需要通过主机的 Docker socket 创建兄弟容器,Coder 容器需要访问 /var/run/docker.sock 并有正确的组权限。
services:
coder:
image: ghcr.io/coder/coder:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
group_add:
- "998"组 ID 必须跟主机上的 Docker 组匹配。
网络也可能让你意外。如果工作空间容器需要到达绑在主机 localhost 上的 Coder 服务器,从容器内部解析 localhost 会指回容器自己。一个常见的修复是用 host.docker.internal 加手动 host gateway 映射。
基础镜像与工作空间生命周期
Coder 提供的基础镜像是有原因的。它们包含预配置的 coder 用户、预期的 home 目录行为、代理依赖、骨架文件和常见开发工具。
用像 ubuntu:latest 这样的纯镜像可以工作,但意味着你自己承担更多设置。你得创建用户、装工具、配置 home 目录,并确保 Coder 代理能正确启动。
工作空间生命周期也重要。有些资源应该只在工作空间启动时存在。Coder 暴露一个 start_count 指标,模板把它跟 Terraform 的 count 元参数配对。
resource "docker_container" "workspace" {
count = data.coder_workspace.me.start_count
}
module "opencode" {
count = data.coder_workspace.me.start_count
}持久卷是另一个问题。如果你想让用户的 home 目录或工作空间数据在停止和重建后存活,保护这个卷。
resource "docker_volume" "home" {
name = "coder-${data.coder_workspace.me.id}-home"
lifecycle {
ignore_changes = all
}
}把它放在一起
Coder 是一个管理可重复开发环境的控制平面。Terraform 是描述这些环境的语言。配置器跑 Terraform 代码。工作空间是结果 VM、容器或 Pod。任务是围绕 AI 代理 prompt 创建的专门工作空间。
OpenCode 作为跑在工作空间里的工具融入这个模型。它不是基础设施层本身;它是上层的代理层。一旦这个分离清楚了,让人困惑的部分就更容易调试。
如果 Docker 资源失败,看配置器和 Docker 主机。如果用户参数缺了,追 Coder 参数流。如果任务 prompt 没到代理,检查 data "coder_task" 和模块接线。如果机密出现在运行中的容器里,记住 Terraform 的敏感性概念不等于运行时机密。
Coder 任务强大是因为它把可重复的基础设施和隔离的代理执行结合起来。这个力量的代价是你要理解两半:Coder/Terraform 部署模型和跑在工作空间里的代理集成。
