Dagger TypeScript SDK NetworkProtocol 枚举完全指南:容器端口的 TCP/UDP 协议声明与使用
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
导读
NetworkProtocol是 Dagger TypeScript SDK(@dagger.io/dagger)中用于描述端口所关联的传输层网络协议的核心枚举类型,其两个取值Tcp与Udp分别对应标准传输协议 "TCP" 与 "UDP"。它是Container.withExposedPort()、Port对象以及服务健康检查等核心 API 的公共参数类型。读完本文,你将掌握该枚举的定义、底层默认值、在端口暴露/取消暴露/查询全流程中的调用方式,以及它在 Dagger 引擎 OCI 层与各语言 SDK 中如何保持一致地序列化与传递。
本文以 version-0.19 的 API 参考文档 为骨架,结合当前仓库中 TypeScript SDK 生成代码、Go 引擎 schema 实现与集成测试用例,深入展开该枚举的完整技术细节。
枚举定义与成员值
在 v0.19 的 TypeScript SDK 参考文档中,NetworkProtocol被描述为:
Transport layer network protocol associated to a port.(与端口关联的传输层网络协议)
它仅包含两个枚举成员:
| 枚举成员 | 字面量值 | 含义 |
|---|---|---|
NetworkProtocol.Tcp | "TCP" | 面向连接的 TCP 协议,支持可靠传输、连接建立与健康检查 |
NetworkProtocol.Udp | "UDP" | 无连接的 UDP 协议,面向数据报,开销更小 |
该枚举的实际生成代码位于 sdk/typescript/src/api/client.gen.ts:
/** * Transport layer network protocol associated to a port. */ export enum NetworkProtocol { Tcp = "TCP", Udp = "UDP", }从源码结构看,client.gen.ts是由 Dagger 的代码生成器(codegen)依据引擎 GraphQL schema 自动生成的客户端文件,因此该枚举是 Dagger 官方 schema 的一等公民类型,而非某个模块私有定义。这意味着:无论你编写的是模块还是普通 Dagger 脚本,只要引用dag客户端,就能直接使用NetworkProtocol.Tcp/NetworkProtocol.Udp作为类型安全的协议参数。
枚举的两条转换辅助函数
由于 GraphQL 请求在网络上传输的是字符串字面量("TCP"/"UDP"),而模块运行时(module runtime)需要的是枚举类型,SDK 在枚举定义之后自动生成了两个方向转换的辅助函数,同样位于 sdk/typescript/src/api/client.gen.ts:
/** * Utility function to convert a NetworkProtocol value to its name so * it can be uses as argument to call a exposed function. */ export function NetworkProtocolValueToName(value: NetworkProtocol): string { switch (value) { case NetworkProtocol.Tcp: return "TCP" case NetworkProtocol.Udp: return "UDP" default: return value } } /** * Utility function to convert a NetworkProtocol name to its value so * it can be properly used inside the module runtime. */ export function NetworkProtocolNameToValue(name: string): NetworkProtocol { switch (name) { case "TCP": return NetworkProtocol.Tcp case "UDP": return NetworkProtocol.Udp default: return name as NetworkProtocol } }两者共同保证了“引擎侧字符串 ↔ 客户端侧枚举”的双向映射,default 分支采用原样透传,因此对未知值不会抛错而是保持原值,便于向前兼容。
使用场景一:withExposedPort()暴露端口时声明协议
NetworkProtocol最主要的消费方是Container.withExposedPort()。在 sdk/typescript/src/api/client.gen.ts 中,其方法签名与 JSDoc 如下:
/** * - For health checks and introspection, when running services * - For setting the EXPOSE OCI field when publishing the container * @param port Port number to expose. Example: 8080 * @param opts.protocol Network protocol. Example: "tcp" * @param opts.description Port description. Example: "payment API endpoint" * @param opts.experimentalSkipHealthcheck Skip the health check when run as a service. */ withExposedPort = ( port: number, opts?: ContainerWithExposedPortOpts, ): Container => { ... }其中opts.protocol的类型正是NetworkProtocol(可选参数)。注意方法内部通过__metadata: { protocol: { is_enum: true, value_to_name: NetworkProtocolValueToName } }标记该参数为枚举,调用 GraphQL 前会先用NetworkProtocolValueToName把枚举转换为"TCP"/"UDP"字符串。
实际用法示例(TS 模块场景,可参考 type-enum-type-03 测试模块):
import { dag, NetworkProtocol } from "@dagger.io/dagger" // 默认暴露 8080/tcp(TCP 是默认协议,可省略) const web = dag .container() .from("nginx:alpine") .withExposedPort(8080) // 显式指定 UDP 协议 const dns = dag .container() .from("coredns/coredns") .withExposedPort(53, { protocol: NetworkProtocol.Udp, description: "DNS over UDP" })端口暴露的两大用途(见上文 JSDoc):一是作为服务运行时用于健康检查与探活;二是在发布容器镜像时写入 OCIEXPOSE字段,等价于 Dockerfile 中的EXPOSE 8080/tcp或EXPOSE 53/udp。
使用场景二:Port对象与exposedPorts()读取
协议信息会随Port对象一起返回。TypeScript SDK 中的Port类持有_protocol?: NetworkProtocol字段(见 client.gen.ts),并通过protocol异步访问器返回枚举:
protocol = async (): Promise<NetworkProtocol> => { const response: Awaited<NetworkProtocol> = await ctx.execute() return NetworkProtocolNameToValue(response) }配合Container.exposedPorts()可列出容器所有已暴露端口(包括镜像本身 EXPOSE 的端口):
const ports = await dag .container() .from("nginx:alpine") .withExposedPort(8080) .exposedPorts() for (const p of ports) { console.log(`${await p.port()}/${await p.protocol()}`) // 例如 8080/TCP }使用场景三:withoutExposedPort()撤销暴露
协议枚举同样用于精确撤销某个端口的暴露。引擎侧签名位于 core/container.go:
func (container *Container) WithoutExposedPort(port int, protocol NetworkProtocol) (*Container, error)由于同一端口号可同时以 TCP 与 UDP 暴露,撤销时必须同时给出端口号与协议,才能唯一确定要移除的暴露项。在 schema 层 core/schema/container.go 中,withoutExposedPort的参数定义同样带有default:"TCP"默认值:
type containerWithoutExposedPortArgs struct { Port int Protocol core.NetworkProtocol `default:"TCP"` }引擎侧实现:默认协议、OCI 端口键与持久化
默认协议为 TCP
在 core/schema/container.go 中,withExposedPort的参数结构体显式声明了默认值:
type containerWithExposedPortArgs struct { Port int Protocol core.NetworkProtocol `default:"TCP"` Description *string ExperimentalSkipHealthcheck bool `default:"false"` }也就是说:调用方不传协议时,端口一律按 TCP 暴露——这与 DockerEXPOSE的默认行为(未标注协议即视为 TCP)保持一致。
OCI 端口键格式port/protocol
引擎在合并“SDK 主动暴露的端口”与“镜像 OCI 配置中的 EXPOSE 端口”时,会以"%d/%s"格式作为统一键(见 core/schema/container.go 中exposedPorts的实现):
ociPort := fmt.Sprintf("%d/%s", p.Port, p.Protocol.Network())即端口 8080、协议 TCP 对应 OCI 键8080/tcp;NewPortFromOCI负责把8080/tcp、53/udp这类 OCI 字符串反向解析为Port对象。这是exposedPorts()能同时返回镜像自带端口与 Dagger 显式暴露端口的关键机制。
持久化与惰性求值
协议信息还会随容器的惰性状态(LazyState)一起持久化。在 core/container.go 中可以看到两个持久化结构:
type persistedContainerWithExposedPortLazy struct { ParentResultID uint64 `json:"parentResultID"` Port Port `json:"port"` } type persistedContainerWithoutExposedPortLazy struct { ParentResultID uint64 `json:"parentResultID"` Port int `json:"port"` Protocol NetworkProtocol `json:"protocol"` }其中Port结构体(Protocol、Port、Description、ExperimentalSkipHealthcheck四字段)整体序列化保存,确保了跨会话、跨进程恢复缓存时协议信息不丢失。
各语言 SDK 的一致性:Go 侧生成代码对照
NetworkProtocol并非 TypeScript 独有。Dagger 的 codegen 会为每个官方 SDK 生成等价枚举。以 Go 模块为例,在生成的 dagger.gen.go 中:
const ( NetworkProtocolTcp NetworkProtocol = "TCP" NetworkProtocolUdp NetworkProtocol = "UDP" )同时生成了Name()、Value()、MarshalJSON()/UnmarshalJSON()等方法:序列化时校验枚举值合法性,反序列化时对未知字符串返回invalid enum value %q错误。这意味着在 Go / Python / Java 等 SDK 中,协议同样以"TCP"/"UDP"字符串在引擎与客户端之间传递,语义完全一致。
集成测试也印证了枚举在模块接口中的用法,例如 module_typescript_test.go 中定义带默认参数的模块函数:
proto(p: NetworkProtocol = NetworkProtocol.Udp): NetworkProtocol { ... }而 hello-with-services-dang 测试数据 展示了多服务场景下的典型调用链:
container.from(image).withExposedPort(80) container.from(...).withExposedPort(6379) container.from(...).withExposedPort(5432)这些端口默认全部按 TCP 暴露,供服务编排与健康检查使用。
实践要点与注意事项
- 协议大小写敏感:
NetworkProtocol.Tcp的底层值是全大写的"TCP",不要写成"tcp"。虽然withExposedPort的 JSDoc 示例写的是"tcp",但 SDK 内部最终通过NetworkProtocolValueToName转换后传输的仍是"TCP";直接使用枚举是类型安全且零歧义的做法。 - TCP 是默认值:无论是 TypeScript 的
withExposedPort(port)还是引擎侧Protocol default:"TCP",不传协议都等价于 TCP。只有需要 UDP 时才必须显式传NetworkProtocol.Udp。 - 撤销暴露要带协议:
withoutExposedPort(port, protocol)的协议参数是必需的,因为同一个端口号可能同时存在 TCP 与 UDP 两种暴露记录。 - 协议与端口查询联动:
Port.protocol()返回枚举(经NetworkProtocolNameToValue反序列化),配合Port.port()可拼出8080/TCP形式的完整描述。 - 向前兼容:两条转换函数对未知字符串采用透传而非抛错,便于在引擎 schema 未来扩展协议时保持旧版客户端可用。
小结
NetworkProtocol虽然只有Tcp与Udp两个成员,却是 Dagger 容器端口模型(暴露、查询、撤销、服务健康检查、OCI 发布)中贯穿始终的协议标识。通过本文你可以看到:它在 TypeScript SDK 生成代码 中定义了枚举与双向转换函数,在 引擎 schema 中决定了默认值(TCP)与 OCI 端口键格式,并作为Port对象的一部分随惰性状态持久化。理解这一枚举的底层行为,能帮助你写出协议声明准确、跨 SDK 行为一致的 Dagger 容器与服务编排代码。
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考