Open Agent Management Protocol
状态: Beta
简介
Open Agent Management Protocol (OpAMP) 是一个用于大规模远程管理数据收集 Agent 的网络协议。
OpAMP 允许 Agent 向服务器报告其状态并从服务器接收配置,还可以从服务器接收 Agent 安装包更新。该协议是供应商无关的,因此服务器可以远程监控和管理大量实现 OpAMP 的不同 Agent,包括来自不同供应商的混合 Agent。
OpAMP 支持以下功能:
- 远程配置 Agent。
- 状态报告。该协议允许 Agent 报告 Agent 的属性,例如其类型和版本,或者其运行的操作系统类型和版本。状态报告还允许管理服务器定制针对单个 Agent 或 Agent 类型的远程配置。
- Agent 的自身遥测报告给一个 OTLP 兼容后端,以监控 Agent 的进程指标(如 CPU 或 RAM 使用量),以及 Agent 特定指标(如数据处理速率)。
- Agent 心跳。
- 可下载的 Agent 特定包的管理。
- 安全的自动更新功能(包括 Agent 的升级和降级)。
- 连接凭据管理,包括客户端 TLS 证书吊销和轮换。
上述功能使得能够对大量混合 Agent(例如 OpenTelemetry Collector、Fluentd 等)进行“单一视图”管理。
通信模型
OpAMP 服务器管理提供 OpAMP 协议客户端实现的 Agent,该实现进一步称为 OpAMP 客户端或简称为客户端。OpAMP 不假设 Agent 和客户端之间有任何特定的关系。客户端可以作为与 Agent 生命周期不同的独立进程运行,也可以作为 sidecar、插件或完全集成到 Agent 代码中。
Agent 在 OpAMP 服务器的指示下,可以选择性地将自己的遥测数据发送到 OTLP 目标。Agent 很可能还会连接到其他目的地,并在那里发送他们收集的数据。
┌────────────┬────────┐ ┌─────────┐
│ │ OpAMP │ OpAMP │ OpAMP │
│ │ ├──────────►│ │
│ │ Client │ │ Server │
│ └────────┤ └─────────┘
│ │
│ ┌────────┤ ┌─────────┐
│ │OTLP │ OTLP/HTTP │ OTLP │
│ Agent │ ├──────────►│ │
│ │Exporter│ │ Receiver│
│ └────────┤ └─────────┘
│ │
│ ┌────────┤
│ │Other ├──────────► Other
│ │Clients ├──────────► Destinations
└────────────┴────────┘
本规范定义了 OpAMP 网络协议以及 OpAMP Agent 和服务器的预期行为。有关 OTLP/HTTP 协议规范,请参阅 OTLP。Agent 用于连接到其他目的地的协议是 Agent 类型特定的,超出了本规范的范围。
OpAMP 协议通过 2 种支持的传输方式之一工作:普通 HTTP 连接和 WebSocket 连接。服务器实现应接受普通 HTTP 连接和 WebSocket 连接。OpAMP 客户端实现可以根据需要选择支持普通 HTTP 或 WebSocket 传输。
OpAMP 客户端代表 Agent 连接到 OpAMP 服务器。通常,单个服务器接受来自多个客户端的连接。Agent 由自分配或服务器分配的全局唯一实例标识符(或简称 instance_uid)标识。instance_uid 记录在从 Agent(通过客户端)发送到服务器以及从服务器发送到 Agent 的每个消息中。
连接的默认 URL 路径为 /v1/opamp。URL 路径可以在客户端和服务器上进行配置。
在 Agent 端实现 OpAMP 的一种典型方法是使用一个辅助的 Supervisor 进程,该进程控制 Agent 进程。Supervisor 通常也负责与 OpAMP 服务器通信。
┌───────────────────┐
│ Supervisor │
│ │
│ ┌────────┤ ┌────────┐
│ │ OpAMP │ OpAMP │ OpAMP │
│ │ ├──────────►│ │
│ │ Client │ │ Server │
└────┬─────┴────────┘ └────────┘
│
│
▼
┌──────────┐
│ │
│ Agent │
│ │
└──────────┘
OpAMP 规范不要求使用 Supervisor,也不定义 Supervisor 和 Agent 如何通信。
在 OpAMP 规范的其余部分,术语 *Agent* 用于指实现 OpAMP 客户端部分的实体,无论该实体是否包含 Supervisor。
WebSocket 传输
OpAMP 协议支持的传输方式之一是 WebSocket。OpAMP 客户端是 WebSocket 客户端,服务器是 WebSocket 服务器。客户端和服务器使用二进制数据 WebSocket 消息进行通信。每个 WebSocket 消息的内容是编码的 header,后跟 二进制编码的 Protobuf 消息 data(参见 WebSocket 消息格式)。
客户端代表 Agent 发送 AgentToServer 消息数据,服务器发送 ServerToAgent Protobuf 消息数据。
┌────────────\ \────────┐ ┌──────────────┐
│ / / │ Data:AgentToServer │ │
│ \ \ OpAmp ├───────────────────────►│ │
│ Agent / / │ │ Server │
│ \ \ Client │ Data:ServerToAgent │ │
│ / / │◄───────────────────────┤ │
└────────────\ \────────┘ └──────────────┘
WebSocket 消息格式
每个 WebSocket 消息的格式如下:
┌────────────┬────────────────────────────────────────┬───────────────────┐
│ header │ Varint encoded unsigned 64 bit integer │ 1-10 bytes │
├────────────┼────────────────────────────────────────┼───────────────────┤
│ data │ Encoded Protobuf message, │ 0 or more bytes │
│ │ either AgentToServer or ServerToAgent │ │
└────────────┴────────────────────────────────────────┴───────────────────┘
未编码的 header 是一个 64 位无符号整数。在 WebSocket 消息中,64 位未编码的 header 值使用 Base 128 Varint 格式编码为字节。编码的 header 使用的字节数取决于未编码 header 的值,范围可以从 1 到 10 字节。
在本规范版本中,未编码 header 的值设置为 0。所有其他 header 值保留供将来使用。这些值将在 OpAMP 规范的未来版本中定义。符合本规范的 OpAMP WebSocket 消息解码器应检查 header 的值是否为 0,如果不是,则应假定 WebSocket 消息格式错误。
data 字段包含以 Protobuf 二进制线格式 编码的 AgentToServer 或 ServerToAgent 消息的字节。
请注意,header 和 data 字段都包含可变数量的字节。header 的解码 Base 128 Varint 算法根据读取的字节知道何时停止。
要使用 Protobuf 解码逻辑解码 data 字段,实现需要知道 data 字段的字节数。为此,实现必须将 WebSocket 消息的大小(以字节为单位)减去 header 的大小(以字节为单位)。
请注意,由于 Protobuf 线格式的设计方式,如果编码的 AgentToServer 或 ServerToAgent 消息为空(即所有字段都未设置),则 data 的字节大小可能为 0。这是一个有效的情况。
WebSocket 消息交换
基于 WebSocket 的 OpAMP 是一种异步、全双工的消息交换协议。OpAMP 客户端和服务器交换的消息的顺序和序列在本文档的相应部分为每个特定功能定义。
序列通常由由某个外部事件触发的初始消息开始。例如,在建立连接后,客户端发送 AgentToServer 消息。在这种情况下,“连接已建立”是触发事件,AgentToServer 是初始消息。
客户端和服务器都可以通过发送初始消息来开始一个序列。
初始消息可能会触发接收方发送一条或多条消息作为响应,这反过来又可能触发反方向的消息,依此类推。这种双向消息交换将继续进行,直到序列结束,因为交换的目标已实现或序列因错误而失败。
请注意,在某些情况下,同一消息可能是序列的初始消息,而在其他情况下,它可能是在响应接收到另一条消息时触发的。与其他协议不同,OpAMP 没有严格区分“请求”和“响应”消息类型。消息的角色取决于序列的触发方式。
例如,AgentToServer 消息可能是客户端连接到服务器时发送的初始消息。AgentToServer 消息也可能由客户端发送,以响应服务器提出的远程配置请求,并且 Agent 报告它已接受配置。
如果客户端能够发送心跳,则客户端应设置 ReportsHeartbeat 功能。如果设置了 ReportsHeartbeat 功能,则客户端应定期发送心跳。心跳间隔应为 30 秒,除非在客户端配置了不同的值,或者服务器通过 OpAMPConnectionSettings.heartbeat_interval_seconds 字段提供了不同的间隔。
有关消息序列的详细信息,请参阅 Operation 部分下的各节。
通常使用 WebSocket 传输,当需要从服务器到 Agent 的即时通信能力,而不必等待客户端轮询服务器(如使用 HTTP 传输时)时。
普通 HTTP 传输
OpAMP 协议支持的第二种传输方式是普通 HTTP 连接。OpAMP 客户端是 HTTP 客户端,服务器是 HTTP 服务器。客户端向服务器发出 POST 请求。POST 请求和响应的主体是 二进制序列化的 Protobuf 消息。客户端在请求主体中发送 AgentToServer Protobuf 消息,服务器在响应主体中发送 ServerToAgent Protobuf 消息。
基于 HTTP 的 OpAMP 是一种同步、半双工的消息交换协议。当客户端有 AgentToServer 消息要传递时,它会发起一个 HTTP 请求。服务器响应每个 HTTP 请求,并返回它想传递给 Agent 的 ServerToAgent 消息。如果 Agent 没有要传递给服务器的内容,客户端必须通过发送一个仅设置了 instance_uid 字段的 AgentToServer 消息来定期轮询服务器。这给了服务器一个机会在响应中发送服务器想要传递给 Agent 的任何消息(例如,新的远程配置)。
当 Agent 没有要传递的内容时的默认轮询间隔为 30 秒。此轮询间隔应可在客户端上配置。如果客户端先前已收到并接受了 OpAMP 连接设置,则应使用 OpAMPConnectionSettings.heartbeat_interval_seconds 的值作为轮询间隔。
使用 HTTP 传输时,消息序列与使用 WebSocket 传输时完全相同。唯一的区别在于时机。
- 当服务器想向 Agent 发送消息时,服务器需要等待客户端轮询服务器并建立一个 HTTP 请求,通过该请求,服务器的消息可以作为 HTTP 响应发送回来。
- 当 Agent 想向服务器发送消息,并且 Agent 之前已向服务器发送了一个尚未响应的请求时,客户端必须等待响应收到后再发出新请求。请注意,在这种情况下,客户端可以在收到前一个响应后立即发出新请求,客户端无需等待请求之间的轮询周期。
当使用普通 HTTP 传输时,客户端必须设置“Content-Type: application/x-protobuf”请求头。当服务器收到设置了此头的 HTTP 请求时,它应假定这是一个普通 HTTP 传输请求,否则应假定这是一个 WebSocket 传输的初始化。
在这种情况下,客户端可以使用 gzip 方法压缩请求主体,并必须指定“Content-Encoding: gzip”。服务器实现必须遵守“Content-Encoding”头,并且必须支持 gzip 压缩或未压缩的请求主体。
如果客户端通过“Accept-Encoding”头指示可以接受压缩响应,则服务器应压缩响应。
AgentToServer 和 ServerToAgent 消息
AgentToServer 消息
OpAMP WebSocket 消息的载体或请求的 HTTP 主体是一个二进制序列化的 Protobuf 消息 AgentToServer,定义如下(本文档中的所有消息均以 Protobuf 3 语言指定)。
message AgentToServer {
bytes instance_uid = 1;
uint64 sequence_num = 2;
AgentDescription agent_description = 3;
uint64 capabilities = 4;
ComponentHealth health = 5;
EffectiveConfig effective_config = 6;
RemoteConfigStatus remote_config_status = 7;
PackageStatuses package_statuses = 8;
AgentDisconnect agent_disconnect = 9;
uint64 flags = 10;
ConnectionSettingsRequest connection_settings_request = 11; // Status: [Development]
CustomCapabilities custom_capabilities = 12; // Status: [Development]
CustomMessage custom_message = 13; // Status: [Development]
AvailableComponents available_components = 14; // Status: [Development]
ConnectionSettingsStatus connection_settings_status = 15; // Status: [Development]
}
服务器应按照相应 Operation 部分的描述处理每个字段。
AgentToServer.instance_uid
instance_uid 字段是 Agent 运行实例的全局唯一标识符。Agent 应自行生成此标识符,并尽最大努力避免创建可能与其他 Agent 创建的标识符冲突的标识符。instance_uid 在 Agent 进程的生命周期内应保持不变。instance_uid 必须为 16 字节长,并应使用 UUID v7 规范生成。
如果 Agent 想使用服务器生成的标识符,则该字段应设置为临时值,并且必须设置 RequestInstanceUid 标志。
AgentToServer.sequence_num
客户端发送的每个 AgentToServer 消息的序列号都会加 1。这使得服务器能够检测到它丢失了消息,当它注意到 sequence_num 不比之前收到的正好大 1 时。有关更多详细信息,请参阅 Agent 状态压缩。
AgentToServer.agent_description
描述 Agent、其类型、运行位置等数据。有关详细信息,请参阅 AgentDescription 消息。如果此信息自上次 AgentToServer 消息以来未更改,则应省略此字段。
AgentToServer.capabilities
AgentCapabilities 枚举定义的标志位掩码。客户端必须将 AgentCapabilities 枚举中未定义的位全部设置为 0。这允许将来扩展协议和 AgentCapabilities 枚举,使得旧 Agent 自动报告它们不支持新功能。此字段必须始终设置。
Agent 可以在第一次消息发送后随时更新其任何能力。服务器必须尊重 Agent 的更改,确保后续消息发送正确。如果服务器发送了对应 Agent 不支持的能力的消息部分,Agent 应忽略它。
enum AgentCapabilities {
// The capabilities field is unspecified.
UnspecifiedAgentCapability = 0;
// The Agent can report status. This bit MUST be set, since all Agents MUST
// report status.
ReportsStatus = 0x00000001;
// The Agent can accept remote configuration from the Server.
AcceptsRemoteConfig = 0x00000002;
// The Agent will report EffectiveConfig in AgentToServer.
ReportsEffectiveConfig = 0x00000004;
// The Agent can accept package offers.
// Status: [Beta]
AcceptsPackages = 0x00000008;
// The Agent can report package status.
// Status: [Beta]
ReportsPackageStatuses = 0x00000010;
// The Agent can report own trace to the destination specified by
// the Server via ConnectionSettingsOffers.own_traces field.
// Status: [Beta]
ReportsOwnTraces = 0x00000020;
// The Agent can report own metrics to the destination specified by
// the Server via ConnectionSettingsOffers.own_metrics field.
// Status: [Beta]
ReportsOwnMetrics = 0x00000040;
// The Agent can report own logs to the destination specified by
// the Server via ConnectionSettingsOffers.own_logs field.
// Status: [Beta]
ReportsOwnLogs = 0x00000080;
// The can accept connections settings for OpAMP via
// ConnectionSettingsOffers.opamp field.
// Status: [Beta]
AcceptsOpAMPConnectionSettings = 0x00000100;
// The can accept connections settings for other destinations via
// ConnectionSettingsOffers.other_connections field.
// Status: [Beta]
AcceptsOtherConnectionSettings = 0x00000200;
// The Agent can accept restart requests.
// Status: [Beta]
AcceptsRestartCommand = 0x00000400;
// The Agent will report Health via AgentToServer.health field.
ReportsHealth = 0x00000800;
// The Agent will report RemoteConfig status via AgentToServer.remote_config_status field.
ReportsRemoteConfig = 0x00001000;
// The Agent can report heartbeats.
// This is specified by the ServerToAgent.OpAMPConnectionSettings.heartbeat_interval_seconds field.
// If this capability is true, but the Server does not set a heartbeat_interval_seconds field, the
// Agent should use its own configured interval, which by default will be 30s. The Server may not
// know the configured interval and should not make assumptions about it.
// Status: [Development]
ReportsHeartbeat = 0x00002000;
// The agent will report AvailableComponents via the AgentToServer.available_components field.
// Status: [Development]
ReportsAvailableComponents = 0x00004000;
// The agent will report ConnectionSettingsOffers status via AgentToServer.connection_settings_status field.
// Status: [Development]
ReportsConnectionSettingsStatus = 0x00008000;
// Add new capabilities here, continuing with the least significant unused bit.
}
AgentToServer.health
状态: Beta
Agent 和子组件的当前健康状况。顶级 ComponentHealth 代表 Agent 的整体健康状况。如果自上次 AgentToServer 消息以来没有变化,则可以省略。请参阅 ComponentHealth 消息。如果自上次 AgentToServer 消息以来没有变化,则可以省略。
AgentToServer.effective_config
Agent 的当前有效配置。有效配置是 Agent 当前正在使用的配置。有效配置可能与之前从服务器收到的远程配置不同,例如因为 Agent 使用本地配置(或附加本地配置)。有关详细信息,请参阅 EffectiveConfig 消息。如果此信息自上次 AgentToServer 消息以来未更改,则应省略此字段。
AgentToServer.remote_config_status
先前从服务器收到的远程配置的状态。有关详细信息,请参阅 RemoteConfigStatus 消息。如果此信息自上次 AgentToServer 消息以来未更改,则应省略此字段。
AgentToServer.package_statuses
状态: Beta
Agent 包列表,包括包状态。如果此信息自上次 AgentToServer 消息以来未更改,则应省略此字段。
AgentToServer.agent_disconnect
AgentDisconnect 必须在客户端发送给服务器的最后一个 AgentToServer 消息中设置。
AgentToServer.flags
AgentToServerFlags 位掩码定义的位标志。
enum AgentToServerFlags {
FlagsUnspecified = 0;
// Flags is a bit mask. Values below define individual bits.
// The Agent requests Server go generate a new instance_uid, which will
// be sent back in ServerToAgent message
RequestInstanceUid = 0x00000001;
}
AgentToServer.connection_settings_request
状态: [开发中]
创建连接设置的请求。对于 Agent 发起创建连接设置的流程,设置此字段。
有关详细信息,请参阅 ConnectionSettingsRequest 消息。
AgentToServer.custom_capabilities
状态: [开发中]
指示 Agent 支持的自定义功能的的消息。
有关详细信息,请参阅 CustomCapabilities 消息。
AgentToServer.custom_message
状态: [开发中]
从 Agent 发送给服务器的自定义消息。
有关详细信息,请参阅 CustomMessage 消息。
AgentToServer.available_components
状态: [开发中]
列出 Agent 中可用组件的消息。只有在设置了 ReportsAvailableComponents 功能时,才应报告此字段。
有关详细信息,请参阅 AvailableComponents 消息。
AgentToServer.connection_settings_status
状态: [开发中]
先前从服务器收到的连接设置的状态。有关详细信息,请参阅 ConnectionSettingsStatus 消息。如果此信息自上次 AgentToServer 消息以来未更改,则应省略此字段。此字段是 连接设置管理 工作流的一部分。
ServerToAgent 消息
WebSocket 消息的载体或 HTTP 响应主体是一个二进制序列化的 Protobuf 消息 ServerToAgent。
ServerToAgent 消息是从服务器发送到 Agent 的,要么是响应 AgentToServer 消息,要么是服务器有数据要传递给 Agent 时。
如果服务器收到 AgentToServer 消息,并且服务器没有要发送回 Agent 的数据,则仍会发送 ServerToAgent 消息,但除 instance_uid 外的所有字段都将未设置(在这种情况下,ServerToAgent 仅作为收到确认)。
收到 ServerToAgent 消息后,Agent 必须处理它。需要执行的处理取决于消息中设置了哪些字段。有关详细信息,请参阅下面字段说明中指向本文档相应部分的链接。
作为此处理的结果,Agent 可能需要向服务器发送状态报告。Agent 可以自由地完全处理 ServerToAgent 消息,然后发送一个状态报告,或者它可以发送多个状态报告,以指示处理进度(例如,请参阅 下载包)。当处理时间较长时,多个状态报告可能是可取的,在这种情况下,状态报告可以使服务器保持最新。
请注意,服务器将回复每个状态报告一个 ServerToAgent 消息(或者如果出现问题则回复 ServerErrorResponse)。这些 ServerToAgent 消息可能与之前收到的消息内容相同,或者内容可能不同,如果服务器上的情况发生变化。Agent 应准备好处理这些其他 ServerToAgent 消息的到来。
如果 Agent 的状态因处理而未更改,则客户端不应发送任何状态报告。
ServerToAgent 消息具有以下结构:
message ServerToAgent {
bytes instance_uid = 1;
ServerErrorResponse error_response = 2;
AgentRemoteConfig remote_config = 3;
ConnectionSettingsOffers connection_settings = 4; // Status: [Beta]
PackagesAvailable packages_available = 5; // Status: [Beta]
uint64 flags = 6;
uint64 capabilities = 7;
AgentIdentification agent_identification = 8;
ServerToAgentCommand command = 9; // Status: [Beta]
CustomCapabilities custom_capabilities = 10; // Status: [Development]
CustomMessage custom_message = 11; // Status: [Development]
}
ServerToAgent.instance_uid
Agent 实例标识符。必须与先前在 AgentToServer 消息中收到的 instance_uid 字段匹配。当通信与多个 Agent 被复用到一个 WebSocket 连接中时(例如,当使用终止代理时),instance_uid 字段允许区分 ServerToAgent 消息是发给哪个 Agent 的。
注意:该值可以通过服务器发送新的值在 AgentIdentification 字段中覆盖。发生这种情况时,Agent 必须将其 instance_uid 更新为提供的值,并将其用于所有进一步的通信。
ServerToAgent.error_response
如果服务器想指示在处理 AgentToServer 消息时出现了问题,则设置 error_response。如果设置了 error_response,则下面所有其他字段都必须是未设置的,反之亦然;如果设置了下面任何字段,则 error_response 必须是未设置的。
ServerToAgent.remote_config
当服务器为 Agent 提供远程配置优惠时,设置此字段。有关详细信息,请参阅 Configuration。
ServerToAgent.connection_settings
状态: Beta
当服务器希望 Agent 更改其一个或多个客户端连接设置(目标、头部、证书等)时,设置此字段。有关详细信息,请参阅 连接设置管理。
ServerToAgent.packages_available
状态: Beta
当服务器有包提供给 Agent 时,设置此字段。有关详细信息,请参阅 Packages。
ServerToAgent.flags
ServerToAgentFlags 位掩码定义的位标志。
Report* 标志可供服务器使用,以防客户端在上次 AgentToServer 消息中未包含特定数据部分(这是一种允许的压缩方法),而服务器又没有该数据,例如服务器重启并丢失了代理状态(有关详细信息,请参阅 本节)。
enum Flags {
FlagsUnspecified = 0;
// Flags is a bit mask. Values below define individual bits.
// ReportFullState flag can be used by the Server if the Client did not include
// some sub-message in the last AgentToServer message (which is an allowed
// optimization) but the Server detects that it does not have it (e.g. was
// restarted and lost state). The detection happens using
// AgentToServer.sequence_num values.
// The Server asks the Agent to report the full status again by sending
// a new, full AgentToServer message.
ReportFullState = 0x00000001;
// ReportAvailableComponents flag can be used by the server if the Agent did
// not include the full AvailableComponents message, but only the hash.
// If this flag is specified, the agent will populate available_components.components
// with a full description of the agent's components.
// Status: [Development]
ServerToAgentFlags_ReportAvailableComponents = 0x00000002;
}
ServerToAgent.capabilities
由 ServerCapabilities 枚举定义的标志位掩码。Server 必须将 ServerCapabilities 枚举中未定义的位设置为 0。这样可以在未来扩展协议和 ServerCapabilities 枚举,以便旧服务器自动报告它们不支持新功能。此字段必须在服务器发送的第一个 ServerToAgent 消息中设置,并且可以通过将其设置为 UnspecifiedServerCapability 值在后续的 ServerToAgent 消息中省略。
enum ServerCapabilities {
// The capabilities field is unspecified.
UnspecifiedServerCapability = 0;
// The Server can accept status reports. This bit MUST be set, since all Server
// MUST be able to accept status reports.
AcceptsStatus = 0x00000001;
// The Server can offer remote configuration to the Agent.
OffersRemoteConfig = 0x00000002;
// The Server can accept EffectiveConfig in AgentToServer.
AcceptsEffectiveConfig = 0x00000004;
// The Server can offer Packages.
OffersPackages = 0x00000008;
// The Server can accept Packages status.
// Status: [Beta]
AcceptsPackagesStatus = 0x00000010;
// The Server can offer connection settings.
// Status: [Beta]
OffersConnectionSettings = 0x00000020;
// The Server can accept ConnectionSettingsRequest and respond with an offer.
// Status: [Development]
AcceptsConnectionSettingsRequest = 0x00000040;
// Add new capabilities here, continuing with the least significant unused bit.
}
ServerToAgent.agent_identification
与代理标识相关的属性,可在需要时由服务器覆盖。当设置 new_instance_uid 时,Agent 必须将 instance_uid 更新为提供的值,并在所有后续通信中使用它。new_instance_uid 必须为 16 字节长,并且应使用 UUID v7 规范生成。
message AgentIdentification {
bytes new_instance_uid = 1;
}
ServerToAgent.command
状态: Beta
设置此字段表示服务器希望代理执行重启。除了 instance_uid 或 capabilities 字段外,此字段不得与其他字段一起设置。所有其他字段都将被忽略,并且代理将执行该命令。有关详细信息,请参阅 ServerToAgentCommand 消息。
ServerToAgent.custom_capabilities
状态: [开发中]
指示服务器支持的自定义功能的 PDU。
有关详细信息,请参阅 CustomCapabilities 消息。
ServerToAgent.custom_message
状态: [开发中]
从服务器发送到代理的自定义消息。
有关详细信息,请参阅 CustomMessage 消息。
ServerErrorResponse Message
消息具有以下结构
message ServerErrorResponse {
enum Type {
UNKNOWN = 0;
BAD_REQUEST = 1;
UNAVAILABLE = 2
}
Type type = 1;
string error_message = 2;
oneof Details {
RetryInfo retry_info = 3;
}
}
ServerErrorResponse.type
此字段定义了服务器在尝试处理代理的请求时遇到的错误的类型。可能的值包括:
UNKNOWN:未知错误。发生了一些问题,但具体原因不明。error_message 字段可能包含问题的描述。
BAD_REQUEST:仅作为对先前收到的 AgentToServer 消息的响应发送,并指示 AgentToServer 消息格式错误。请参阅 “坏请求” 处理。
UNAVAILABLE:服务器过载,无法处理请求。请参阅 “限流”。
ServerErrorResponse.error_message
错误消息,通常是人类可读的。
ServerErrorResponse.retry_info
关于在 type==UNAVAILABLE 时重试的附加 RetryInfo 消息。
ServerToAgentCommand Message
状态: Beta
消息具有以下结构
// ServerToAgentCommand is sent from the Server to the Agent to request that the Agent
// perform a command.
message ServerToAgentCommand {
enum CommandType {
// The Agent should restart. This request will be ignored if the Agent does not
// support restart.
Restart = 0;
}
CommandType type = 1;
}
ServerToAgentCommand 消息在服务器希望代理重新启动时发送。此消息只能包含 command、instance_uid 和 capabilities 字段。所有其他字段将被忽略。
Operation
状态报告
客户端必须发送状态报告
- 首次连接到服务器后立即发送。状态报告必须是客户端发送的第一条消息。
- 之后,每次代理状态发生变化时发送。
状态报告作为 AgentToServer 消息发送。消息中的以下字段可以设置为反映状态的相应部分:agent_description、capabilities、health、effective_config、remote_config_status、package_statuses。
服务器必须通过发送 ServerToAgent 消息来响应 AgentToServer 消息。
如果状态报告处理失败,则 error_response 字段必须设置为 ServerErrorResponse 消息。
如果服务器成功处理了状态报告,则 error_response 字段必须未设置,并且其他字段可以根据需要进行填充。
这是显示状态报告工作原理的时序图(假设服务器端处理成功)
Client Server
│ │
│ │
│ WebSocket Connect │
├──────────────────────────────────────►│
│ │
│ AgentToServer │ ┌─────────┐
├──────────────────────────────────────►├──►│ │
│ │ │ Process │
│ ServerToAgent │ │ Status │
│◄──────────────────────────────────────┤◄──┤ │
│ │ └─────────┘
. ... .
│ AgentToServer │ ┌─────────┐
├──────────────────────────────────────►├──►│ │
│ │ │ Process │
│ ServerToAgent │ │ Status │
│◄──────────────────────────────────────┤◄──┤ │
│ │ └─────────┘
│ │
请注意,代理的状态可能会因收到来自服务器的消息而发生变化。例如,服务器可能会向代理发送远程配置。代理处理完此类请求后,代理的状态会发生变化(例如,代理的有效配置会发生变化)。这种状态变化应导致客户端向服务器发送状态报告。
因此,实际上在这些情况下,消息序列可能如下所示:
Agent Client Server
│ │ ServerToAgent │
┌───────┤◄──────────────────────────────────────┤
│ │ │
▼ │ │
┌────────┐ │ │
│Process │ │ │
│Received│ │ │
│Data │ │ │
└───┬────┘ │ │
│ │ │
│Status │ │
│Changed│ AgentToServer │ ┌─────────┐
└──────►├──────────────────────────────────────►├──►│ │
│ │ │ Process │
│ ServerToAgent │ │ Status │
│◄──────────────────────────────────────┤◄──┤ │
│ │ └─────────┘
当客户端收到 ServerToAgent 消息时,客户端不得发送状态报告,除非对从服务器收到的消息的处理导致代理状态发生实际更改(例如,代理的配置已更改)。在这种情况下,时序图如下所示:
Agent Client Server
│ │ ServerToAgent │
┌──────┤◄──────────────────────────────────────┤
│ │ │
▼ │ │
┌────────┐ │ │
│Process │ │ │
│Received│ │ │
│Data │ │ │
└───┬────┘ │ │
│ │ │
▼ │ │
No Status │ │
Changes │ │
│ │
│ │
重要提示:如果客户端不遵循这些规则,操作可能会导致客户端和服务器之间无限的消息循环。
Agent Status Compression
客户端通过发送 AgentToServer 消息向服务器通知代理的状态。状态包括代理描述、其有效配置、收到的远程配置状态以及包的状态。服务器使用 AgentToServer 消息中引用的子消息中指定的数据来跟踪代理的状态。
客户端可以通过省略自上次报告特定数据以来未更改的子消息来压缩 AgentToServer 消息。以下子消息可能受此类压缩的影响:AgentDescription、ComponentHealth、EffectiveConfig、RemoteConfigStatus、PackageStatuses、CustomCapabilities、AvailableComponents 和 ConnectionSettingsStatus。
压缩是通过省略 AgentToServer 消息中的子消息来完成的。如果子消息中的任何字段已更改,则不能对该特定子消息使用压缩,并且必须存在带有所有相关字段的子消息。
如果所有 AgentToServer 消息都可靠地传递到服务器,并且服务器正确地处理了它们,那么这种压缩是安全的,并且服务器应该始终拥有代理的正确最新状态。
但是,客户端和服务器有可能丢失同步,并且客户端认为服务器拥有最新数据,而实际上服务器没有。例如,如果服务器在客户端继续运行时重启,而服务器又因为暂时关闭而未收到 AgentToServer 消息,则这种情况是可能的。
为了检测这种情况并从中恢复,AgentToServer 消息包含一个 sequence_num 字段。该字段是一个整数,每次客户端有新的 AgentToServer 消息要发送时都会递增。
当服务器收到一个 AgentToServer 消息时,如果其 sequence_num 字段的值不恰好比先前收到的 sequence_num 值大一,服务器就知道它没有 AgentToServer 消息的完整状态。
遇到这种情况时,为了恢复丢失的状态,服务器必须请求代理报告被省略的数据。为了发出此请求,服务器必须向代理发送一个 ServerToAgent 消息,并在 ServerToAgent 消息的 flags 字段中设置 ReportFullState 位。
AgentDescription Message
AgentDescription 消息具有以下结构
message AgentDescription {
repeated KeyValue identifying_attributes = 1;
repeated KeyValue non_identifying_attributes = 2;
}
AgentDescription.identifying_attributes
标识代理的属性。
键/值根据 OpenTelemetry 资源语义约定。
对于独立运行的代理(如 OpenTelemetry Collector),应指定以下属性:
- service.name 应设置为代理在其自身遥测数据中使用的相同值。
- service.namespace 如果在代理运行的环境中使用。
- service.version 应设置为代理构建的版本号。
- service.instance.id 应设置。它可以设置为等于代理的实例 uid(等于 ServerToAgent.instance_uid 字段)或任何其他值,该值与其他属性结合起来可以唯一地标识代理。
- 其他任何对于唯一标识代理自身遥测数据所必需的属性。
代理还应将这些属性包含在其自身的遥测数据的 Resource 中。标识属性的组合应足以唯一地标识代理在代理发送其自身遥测数据的目标系统中进行自身遥测的身份。
AgentDescription.non_identifying_attributes
不一定标识代理,但有助于描述其运行位置的属性。
应包含以下属性:
- os.type, os.version - 用于描述代理的运行环境。
- host.* 用于描述代理运行的主机。
- cloud.* 用于描述主机所在的云。
- 任何其他相关的资源属性,用于描述此代理及其运行的环境。
- 最终用户希望与此代理关联的任何用户定义的属性。
ComponentHealth Message
状态: Beta
ComponentHealth 消息具有以下结构
message ComponentHealth {
bool healthy = 1;
fixed64 start_time_unix_nano = 2;
string last_error = 3;
string status = 4;
fixed64 status_time_unix_nano = 5;
map<string, ComponentHealth> component_health_map = 6;
}
ComponentHealth.healthy
如果代理正常运行且健康,则设置为 true。
ComponentHealth.start_time_unix_nano
代理启动以来的时间戳,即代理启动的时间。值为自 1970 年 1 月 1 日 00:00:00 UTC 以来的纳秒级 UNIX Epoch 时间。如果代理未运行,则必须设置为 0。
ComponentHealth.last_error
如果代理处于错误状态,则为人类可读的错误消息。当 healthy==false 时应设置。
ComponentHealth.status
以字符串形式表示的组件状态。状态值由特定于代理的语义定义,而不是在协议级别定义。
ComponentHealth.status_time_unix_nano
观察到组件状态的时间。值为自 1970 年 1 月 1 日 00:00:00 UTC 以来的纳秒级 UNIX Epoch 时间。
ComponentHealth.component_health_map
用于存储更细粒度的子组件健康的映射。它可以根据需要深入嵌套,以描述底层系统。
EffectiveConfig Message
EffectiveConfig 消息具有以下结构
message EffectiveConfig {
AgentConfigMap config_map = 1;
}
EffectiveConfig.config_map
代理的有效配置。
请参阅 “配置” 部分中的 AgentConfigMap 消息定义。
RemoteConfigStatus Message
RemoteConfigStatus 消息具有以下结构
message RemoteConfigStatus {
bytes last_remote_config_hash = 1;
enum Status {
// The value of status field is not set.
UNSET = 0;
// Remote config was successfully applied by the Agent.
APPLIED = 1;
// Agent is currently applying the remote config that it received earlier.
APPLYING = 2;
// Agent tried to apply the config received earlier, but it failed.
// See error_message for more details.
FAILED = 3;
}
Status status = 2;
string error_message = 3;
}
RemoteConfigStatus.last_remote_config_hash
此代理在 AgentRemoteConfig.config_hash 字段中最后接收到的远程配置的哈希值。服务器应将此哈希值与服务器为代理拥有的配置哈希值进行比较,如果哈希值不同,则服务器必须在 ServerToAgent 消息中的响应中包含 remote_config 字段。
RemoteConfigStatus.status
代理尝试应用先前收到的远程配置的状态。
RemoteConfigStatus.error_message
如果 status==FAILED,则为可选的错误消息。
ConnectionSettingsStatus Message
ConnectionSettingsStatus 消息具有以下结构
message ConnectionSettingsStatus {
bytes last_connection_settings_hash = 1;
enum Status {
// The value of status field is not set.
UNSET = 0;
// offered connection settings were successfully applied by the Agent.
APPLIED = 1;
// Agent is currently applying the offered connection settings that it received earlier.
APPLYING = 2;
// Agent tried to apply the offered connection settings recieved earlier, but it failed.
// See error_message for more details.
FAILED = 3;
}
Status status = 2;
string error_message = 3;
}
ConnectionSettingsStatus.last_connection_settings_hash
此代理在 connection_settings.hash 字段中最后接收到的提供的连接设置的哈希值。服务器应将此哈希值与服务器为代理拥有的配置哈希值进行比较,如果哈希值不同,则服务器必须在 ServerToAgent 消息中的响应中包含 connection_settings 字段。
ConnectionSettingsStatus.status
代理尝试应用先前收到的连接设置的状态。
ConnectionSettingsStatus.error_message
如果 status==FAILED,则为可选的错误消息。
PackageStatuses Message
状态: Beta
PackageStatuses 消息描述了代理拥有或已被提供的所有包的状态。消息具有以下结构:
message PackageStatuses {
map<string, PackageStatus> packages = 1;
bytes server_provided_all_packages_hash = 2;
string error_message = 3;
}
PackageStatuses.packages
PackageStatus 消息的映射,其中键是包名。键必须与 PackageStatus 消息的 packages 字段中的 name 字段匹配。
PackageStatuses.server_provided_all_packages_hash
此代理先前通过 PackagesAvailable 消息从服务器接收到的所有包的聚合哈希值。
服务器应将此哈希值与服务器为该代理拥有的所有包的聚合哈希值进行比较,如果哈希值不同,则服务器应向代理发送 PackagesAvailable 消息。
PackageStatuses.error_message
如果在处理 PackagesAvailable 消息时代理遇到了错误,并且该错误与任何单个包无关,则设置此字段。
如果没有处理错误,则必须取消设置此字段。
PackageStatus Message
状态: Beta
PackageStatus 具有以下结构
message PackageStatus {
string name = 1;
string agent_has_version = 2;
bytes agent_has_hash = 3;
string server_offered_version = 4;
bytes server_offered_hash = 5;
enum Status {
INSTALLED = 0;
INSTALL_PENDING = 1;
INSTALLING = 2;
INSTALL_FAILED = 3;
DOWNLOADING = 4;
}
Status status = 6;
string error_message = 7;
PackageDownloadDetails download_details = 8;
}
PackageStatus.name
包名。必须始终设置,并且必须与 PackageStatuses 消息的 packages 字段中的键匹配。
PackageStatus.agent_has_version
代理拥有的包的版本。
如果代理拥有此包,则必须设置。
如果代理不拥有此包,则必须为空。例如,如果服务器提供了该包但安装失败,并且代理以前不拥有该包,则可能出现这种情况。
PackageStatus.agent_has_hash
代理拥有的包的哈希值。
如果代理拥有此包,则必须设置。
如果代理不拥有此包,则必须为空。例如,如果服务器提供了该包但安装失败,并且代理以前不拥有该包,则可能出现这种情况。
PackageStatus.server_offered_version
服务器向代理提供的包的版本。
如果包的安装是由服务器先前提供的安装此包的请求触发的,则必须设置。
如果代理拥有此包但它是本地安装的并且未由服务器提供,则必须为空。
请注意,agent_has_version 和 server_offered_version 字段都可能被设置并具有不同的值。例如,如果代理已成功安装了某个版本的包,服务器提供了另一个版本,但代理未能安装该版本,则可能出现这种情况。
PackageStatus.server_offered_hash
服务器向代理提供的包的哈希值。
如果包的安装是由服务器先前提供的安装此包的请求触发的,则必须设置。
如果代理拥有此包但它是本地安装的并且未由服务器提供,则必须为空。
请注意,agent_has_hash 和 server_offered_hash 字段都可能被设置并具有不同的值。例如,如果代理已成功安装了某个版本的包,服务器提供了另一个版本,但代理未能安装该版本,则可能出现这种情况。
PackageStatus.status
此包的状态。可能的值为:
INSTALLED:包已由代理成功安装。error_message 字段不得设置。
INSTALLING:代理当前正在下载和安装包。server_offered_hash 字段必须设置为指示代理正在安装的版本。error_message 字段不得设置。
INSTALL_FAILED:代理尝试安装包但安装失败。server_offered_hash 字段必须设置为指示代理尝试安装的版本。error_message 也可能包含有关失败的更多详细信息。
PackageStatus.error_message
如果状态有误,则为错误消息。
PackageStatus.download_details
状态: [开发中]
download_details 包含描述包下载的其他详细信息。只有当状态为 DOWNLOADING 时才应设置。
message PackageDownloadDetails {
double download_percent = 1;
double download_bytes_per_second = 2;
}
Connection Settings Management
状态: Beta
OpAMP 包含允许服务器管理代理与所有代理连接的目的地的连接设置,以及 OpAMP 客户端的连接设置的功能。
下图显示了一个典型的由 OpAMP 服务器管理的代理,它将其自身的遥测数据发送到 OTLP 后端,并连接到其他目的地执行其工作。
┌────────────┬────────┐ ┌─────────┐
│ │ OpAMP │ OpAMP │ OpAMP │
│ │ ├──────────►│ │
│ │ Client │ │ Server │
│ └────────┤ └─────────┘
│ │
│ ┌────────┤ ┌─────────┐
│ │OTLP │ OTLP/HTTP │OTLP │
│ Agent │ ├──────────►│Telemetry│
│ │Client │ │Backend │
│ └────────┤ └─────────┘
│ │
│ ┌────────┤
│ │Other ├──────────► Other
│ │ ├──────────►
│ │Clients ├──────────► Destinations
└────────────┴────────┘
在连接到 OpAMP 服务器和其他目的地时,通常期望代理(或代表代理连接的 OpAMP 客户端)使用某种基于标头的授权机制(例如,“Authorization” HTTP 标头或自定义标头中的访问令牌),并且可选地也使用 TLS 连接的客户端证书(也称为双向 TLS)。
OpAMP 协议允许服务器为这些连接中的每一个提供设置,并允许代理接受或拒绝这些提议。此机制可用于将代理定向到特定目的地,以及按需进行访问令牌和 TLS 证书的注册、吊销和轮换。
服务器可以为以下 3 类目的地提供连接设置:
- OpAMP 服务器本身。这通常用于管理凭据,例如 TLS 证书或用于授权的请求标头。服务器还可以提供不同的目的地端点,将 OpAMP 客户端定向到不同的 OpAMP 服务器。
- 代理用于发送其自身遥测数据的目的地:使用 OTLP/HTTP 协议的指标、跟踪和日志。
- 一组其他“其他”连接设置,每个设置都带有一个字符串名称。代理类型如何使用这些设置是特定于代理的。通常,名称代表代理已知的连接目的地名称。例如,OpenTelemetry Collector 可以使用命名的连接设置来配置其导出器,每个命名连接设置对应一个命名导出器。
服务器仅当代理通过 AgentToServer.capabilities 字段报告使用相应连接的功能时,才可以为特定连接类进行提议。
- 如果设置了 ReportsOwnTraces capability 位,则服务器可以使用 own_traces 字段来提供跟踪的连接设置。
- 如果设置了 ReportsOwnMetrics capability 位,则服务器可以使用 own_metrics 字段来提供指标的连接设置。
- 如果设置了 ReportsOwnLogs capability 位,则服务器可以使用 own_logs 字段来提供日志的连接设置。
- 如果设置了 AcceptsOpAMPConnectionSettings capability 位,则服务器可以使用 opamp 字段来提供 OpAMP 连接的连接设置。
- 如果设置了 AcceptsOtherConnectionSettings capability 位,则服务器可以使用 other_connections 字段来提供其他目的地的连接设置。
此外,ReportsConnectionSettingsStatus capability 用于代理指示它能够向服务器报告是否按预期应用了提供的连接设置。
根据提供的连接设置的不同,操作顺序略有不同。自身遥测数据的连接设置处理描述在 “自身遥测报告” 中。“其他”目的地的连接设置处理描述在 “其他目的地连接设置” 中。OpAMP 连接设置的处理如下所述。
代理也可以请求服务器启动连接设置的创建。此过程描述在 “代理发起的 CSR 流” 部分。
OpAMP Connection Setting Offer Flow
服务器发起的 OpAMP 连接设置更改流程如下:
Client Server
│ │ Initiate
│ Connect │ Settings
├──────────────────────────────────────►│ Change
│ ... │ │
│ │◄───────┘
│ │ ┌───────────┐
│ ├─────────►│ │
│ │ Generate │Credentials│
┌───────────┐ │ServerToAgent{ConnectionSettingsOffers}│ and Save │ Store │
│ │◄───────┤◄──────────────────────────────────────┤◄─────────┤ │
│Credentials│ Save │ │ └───────────┘
│ Store │ │ Disconnect │
│ ├───────►├──────────────────────────────────────►│
└───────────┘ │ │
│ Connect, New settings │ ┌───────────┐
├──────────────────────────────────────►├─────────►│ │
│ │ Delete │Credentials│
┌───────────┐ │ Connection established │ old │ Store │
│ │◄───────┤◄─────────────────────────────────────►│◄─────────┤ │
│Credentials│Delete │ │ └───────────┘
│ Store │old │ │
│ ├───────►│ │
└───────────┘ │ │
- 服务器生成新的连接设置,并将其保存在服务器的凭据存储中,将新设置与代理实例 UID 相关联。
- 服务器发送包含 ConnectionSettingsOffers 消息的 ServerToAgent 消息。opamp 字段包含新的 opamp 提供的 OpAMPConnectionSettings。
- 客户端接收设置提议,并将更新的连接设置保存在本地存储中,将其标记为“候选”(如果客户端崩溃,它将重试候选验证步骤 5-9)。
- 客户端断开与服务器的连接。如果客户端具有 ReportsConnectionSettingsStatus 功能,则客户端应指示它正在应用新的连接设置。
- 客户端使用新设置连接到服务器。
- 成功建立连接,所有必需的 TLS 验证都通过,并且服务器指示授权成功。
- 服务器从其凭据存储中删除此代理的旧连接设置(使用代理实例 UID)。
- 客户端从其凭据存储中删除旧设置,并将新连接设置标记为“有效”。如果客户端具有 ReportsConnectionSettingsStatus 功能,则客户端必须报告连接设置已应用。
- 如果步骤 6 失败,客户端将删除新设置,恢复到旧设置并重新连接。如果客户端具有 ReportsConnectionSettingsStatus 功能,则客户端必须报告连接设置失败。
注意:无法持久化新连接设置且仅具有临时存储访问权限的客户端应拒绝证书提议,否则它们在重新启动后可能会丢失访问权限并丢失提供的证书。
Trust On First Use
想要使用带客户端证书的 TLS,但最初没有证书的 OpAMP 客户端可以使用“首次使用信任”(TOFU)流程。流程如下:
- 客户端使用常规 TLS(验证服务器身份)连接到服务器,但不带客户端证书。客户端发送代理的状态报告,以便被识别。
- 服务器接受连接和状态,并等待批准为 OpAMP 客户端生成客户端证书。
- 服务器要么等待人工批准,要么自动批准所有 TOFU 请求(如果服务器配置为这样做,这可以是一个服务器端选项)。
- 一旦获得批准,流程实际上与 OpAMP 连接设置提议流程的步骤相同,只是没有要删除的旧客户端证书。
TOFU 流程允许在无需执行客户端证书安装的情况下引导安全环境。
对于没有必要授权标头访问服务器的 OpAMP 客户端,也可以使用完全相同的 TOFU 方法。服务器可以检测到此类访问,并在批准后将授权标头发送给客户端。
Registration On First Use
在某些用例中,最好为新安装的代理提供初始连接设置,这些设置在首次使用时是有效的,但在建立首次连接后生成一套新的连接凭据。
这可以通过与 TOFU 流程非常相似的方式实现。唯一的区别是首次连接将得到正确身份验证,但服务器将立即为代理生成并提供新的连接设置。然后,客户端将持久化该设置,并将其用于所有后续操作。
这样,就可以使用一套预定义的连接凭据(授权标头、证书等)部署大量代理,但在成功连接后,每个代理将立即获得其自己的唯一连接凭据。这样,可以吊销单个代理的凭据,而不会中断所有其他代理的访问。
Agent-initiated CSR Flow
状态: [开发中]
这是一个代理发起的流程,允许客户端将证书签名请求(CSR)发送到服务器,并获取一个自签名或 CA 签名的客户端证书,客户端可以在后续的 OpAMP 连接中使用该证书。
此流程目前仅支持 OpAMP 连接。代理无法为自身遥测连接或其他连接类型发送 CSR 请求。
Client Server
│ (1) Connect │
├──────────────────────────────────────►│
│ ... │
┌───────────┐ │ │ ┌───────────┐
│ Generate │ (2) │ (3) AgentToServer{CSR} │(4) │ │
│ Keypair ├───────►├──────────────────────────────────────►├─────────►│ Approve │
│ and CSR │ │ ... │ │ │
└───────────┘ │ │ └─────┬─────┘
│ │ │(5)
│ │ │
│ │ ▼
│ │ ┌───────────┐
┌───────────┐ │ServerToAgent{ConnectionSettingsOffers}│ (7) │Create │
│ │◄───────┤◄──────────────────────────────────────┤◄─────────┤Certificate│
│Credentials│ Save │ │ │ (6) │
│ Store │ │ Disconnect │ └───────────┘
│ ├───────►├──────────────────────────────────────►│
└───────────┘ │ │
│ Connect, New settings │ ┌───────────┐
├──────────────────────────────────────►├─────────►│ │
│ │ Delete │Credentials│
┌───────────┐ │ Connection established │ old │ Store │
│ │◄───────┤◄─────────────────────────────────────►│◄─────────┤ │
│Credentials│Delete │ │ └───────────┘
│ Store │old (8) │ │
│ ├───────►│ │
└───────────┘ │ │
流程如下:
- (1) 客户端连接到服务器。客户端应使用常规 TLS 并验证服务器的身份。代理还可以使用服务器已信任的引导客户端证书。(注意:引导证书的分发和安装方法不属于本规范的范畴)。
- (2) 代理生成密钥对和证书签名请求(CSR)。CSR 包含客户端希望用于后续身份验证的信息。
- (3) 客户端发送包含 CSR 的 AgentToServer 消息。
- (4) 如果提供了引导证书,则服务器将对其进行验证(例如,使用受信任的 CA),并等待批准为代理生成客户端证书。
- (5) 服务器要么等待人工批准,要么自动批准所有 TOFU 请求(如果服务器配置为这样做,这可以是一个服务器端选项)。
- (6) 获得批准后,服务器创建一个客户端证书。服务器通过颁发自签名证书(充当本地 CA)或将 CSR 代理给 CA 并从 CA 获取客户端证书来完成此操作。
- (7) 从 CA 获取客户端证书后,流程实际上与 OpAMP 连接设置提议流程的步骤相同,从提供包含客户端证书的连接设置开始。OpAMPConnectionSettings.certificate 消息将 cert 字段设置为客户端证书。如果使用 CA,则 ca_cert 字段将设置为 CA 的证书。private_key 字段不会被设置,因为在此流程中,代理拥有私钥,而服务器不拥有。
- (8) 在成功验证了提议的新客户端证书后,代理会删除(如果使用过)引导证书,并为将来的连接使用新证书。
在向代理发送 OpAMPConnectionSettings 时,服务器可以包含除 certificate 以外的字段,从而允许服务器一次性替换代理的证书、连接标头和其他设置。
如果步骤 4-6 中的任何一步失败,服务器必须向代理响应一个 ServerErrorResponse,其中 type 字段设置为 ServerErrorResponseType_BadRequest。
代理可以随时使用相同的流程重新请求新证书。例如,代理可以在当前证书到期日期临近时执行此操作。
Using instance_uid in the CSR
状态: [开发中]
实现可以选择使用代理的 instance_uid 作为 CSR 字段(或字段的一部分),并且在这些实现中,服务器可以验证负载中的连接代理的 instance_uid 是否与证书内容匹配。这可以防止代理冒充其他代理。
当服务器收到包含 instance_uid 的 CSR 字段时,服务器必须验证 AgentToServer 消息中的 instance_uid 字段是否与 CSR 字段中的 instance_uid 匹配。这强制执行了代理只能为其自身请求证书。
如果 instance_uid 是 CSR 和颁发的客户端证书的一部分,那么 instance_uid 的任何更改都需要重新生成客户端证书。例如,如果服务器通过 new_instance_uid 字段指示代理使用新的 instance_uid,则可能发生此类更改。
当被服务器指示更改其 instance_uid 时,代理还必须重复 “代理发起的 CSR 流”,这次使用新的 instance_uid 作为 CSR 字段之一。然后,服务器必须准备好接收 CSR,而此时代理仍在使用的旧证书中包含旧的 instance_uid。
换句话说:传入的 CSR 可能会请求一个新的证书,该证书引用一个 instance_uid,该 instance_uid 与用于同一传入连接的客户端证书中引用的 instance_uid 不同。这是一种有效的情况,服务器不得拒绝。在这种情况下,服务器还可以查看连接的客户端证书中的 instance_uid,并将其与旧的已知 instance_uid 进行比较,以确认代理是否按照服务器的指示正在更改 instance_uid。
撤销访问
由于服务器知道客户端使用哪些访问标头和客户端证书,因此服务器可以通过将相应的连接设置标记为“已撤销”并断开客户端连接来撤销对单个代理的访问。后续使用被撤销凭据的连接可能会被服务器拒绝,从而基本上禁止客户端访问服务器。
由于服务器可以控制代理所有 3 种目标类型的连接设置(因为它可以提供连接设置),因此可以为这 3 种目标类型中的任何一种执行此撤销,前提是服务器先前已提供并且代理已接受该特定类型的目标。
对于自身的遥测和“其他”目标,服务器还必须将撤销事实告知相应的目标,以便它们可以开始拒绝使用被撤销凭据的连接的访问。
证书生成
服务器生成的客户端证书可以是自签名的、由私有证书颁发机构签名的或由公共证书颁发机构签名的。服务器负责生成客户端证书,使其能够被证书的目标所信任。这要求目标要么直接记住并信任单个自签名客户端证书,要么信任用于签名客户端证书的证书颁发机构,以便可以验证信任链。
客户端证书的确切生成方式超出了 OpAMP 规范的范围。
“其他”目标的连接设置
待定
ConnectionSettingsRequest 消息
状态: [开发中]
ConnectionSettingsRequest 是代理向服务器发出的请求,用于创建连接设置并响应代理的连接设置。
message ConnectionSettingsRequest {
OpAMPConnectionSettingsRequest opamp = 1;
}
opamp 字段设置为指示请求 OpAMP 连接设置。如果此字段未设置,则 ConnectionSettingsRequest 消息为空,服务器无法处理。
OpAMPConnectionSettingsRequest 消息
状态: [开发中]
OpAMPConnectionSettingsRequest 是请求服务器在其响应中生成 OpAMPConnectionSettings。
用于代理发起的 CSR 流。
message OpAMPConnectionSettingsRequest {
CertificateRequest certificate_request = 1;
}
当代理请求服务器创建客户端证书时,将设置 certificate_request。此字段是必需的。
CertificateRequest 消息
状态: [开发中]
message CertificateRequest {
bytes csr = 1;
}
csr 字段是 PEM 编码的客户端证书签名请求 (CSR),由客户端私钥签名。
服务器应验证请求,并应响应 OpAMPConnectionSettings,其中 certificate.cert 包含颁发的证书。
ConnectionSettingsOffers 消息
ConnectionSettingsOffers 消息描述了供代理使用的连接设置。
message ConnectionSettingsOffers {
bytes hash = 1;
OpAMPConnectionSettings opamp = 2;
TelemetryConnectionSettings own_metrics = 3;
TelemetryConnectionSettings own_traces = 4;
TelemetryConnectionSettings own_logs = 5;
map<string,OtherConnectionSettings> other_connections = 6;
}
ConnectionSettingsOffers.hash
所有设置的哈希值,包括可能因未更改而从本消息中省略的设置。
ConnectionSettingsOffers.opamp
连接到 OpAMP 服务器的设置。如果此字段未设置,则客户端应假定设置未更改,并应继续使用现有设置。客户端必须通过实际连接来验证提供的连接设置,以确保其不会因无效设置而失去对 OpAMP 服务器的访问权限。
ConnectionSettingsOffers.own_metrics
连接到 OTLP 指标后端以将代理自身指标发送到该后端的设置。如果此字段未设置,则代理应假定设置未更改。
ConnectionSettingsOffers.own_traces
连接到 OTLP 跟踪后端以将代理自身跟踪发送到该后端的设置。如果此字段未设置,则代理应假定设置未更改。
ConnectionSettingsOffers.own_logs
连接到 OTLP 日志后端以将代理自身日志发送到该后端的设置。如果此字段未设置,则代理应假定设置未更改。
ConnectionSettingsOffers.other_connections
另一组连接设置,每个设置都关联一个字符串名称。代理如何使用这些设置是特定于代理的。通常,名称表示代理所知的目标连接的名称。如果此字段未设置,则代理应假定 other_connections 设置未更改。
OpAMPConnectionSettings
OpAMPConnectionSettings 消息是一组字段,构成服务器向 OpAMP 客户端提供的使用指定设置进行 OpAMP 连接的声明。
message OpAMPConnectionSettings {
string destination_endpoint = 1;
Headers headers = 2;
TLSCertificate certificate = 3;
uint64 heartbeat_interval_seconds = 4;
TLSConnectionSettings tls = 5;
ProxyConnectionSettings proxy = 6;
}
OpAMPConnectionSettings.destination_endpoint
OpAMP 服务器 URL。这必须是 WebSocket 或 HTTP URL,并且不能为空,例如 wss://example.com:4318/v1/opamp。
OpAMPConnectionSettings.headers
连接时使用的可选标头。通常用于设置访问令牌或其他授权标头。对于基于 HTTP 的协议,客户端应在请求标头中设置这些标头。例如:key=“Authorization”,Value=“Basic YWxhZGRpbjpvcGVuc2VzYW1l”。
OpAMPConnectionSettings.certificate
客户端应从此以后使用提供的证书连接到目标。如果客户端能够使用提供的证书进行验证和连接,则客户端应忘记此连接的任何先前客户端证书。此字段是可选的:如果省略,客户端不应使用客户端证书。此字段可用于执行客户端证书撤销/轮换。
OpAMPConnectionSettings.heartbeat_interval_seconds
状态: [开发中]
如果 ReportsHeartbeat 能力为 true,则客户端必须使用提供的心跳间隔定期发送 AgentToServer 消息。如果能力为 true 并且服务器将 heartbeat_interval_seconds 设置为 0,则应禁用代理心跳。至少必须设置 heartbeat 中的 AgentToServer.instance_uid 字段。基于 HTTP 的客户端必须使用心跳间隔作为其轮询间隔。
任何 instance_uid 字段已设置的 AgentToServer 消息都被视为有效心跳。请注意,如果另一个包含其他数据的 AgentToServer 消息刚刚发送,则无需仅出于心跳目的发送单独的 AgentToServer 消息。代理必须从最后发送的 AgentToServer 消息开始计算心跳间隔。
心跳用于保持连接处于活动状态,并告知服务器代理仍然存活且处于活动状态。服务器可以使用心跳来做出有关连接的代理的生存能力的决策。
心跳协商的流程如下:
┌──────────┐ ┌──────────┐
│ │ (1) Connect │ │
│ ├──────────────────────►│ │
│ │ │ │
│ │ (2) Set Heartbeat │ │
│ │◄──────────────────────┤ │
│ │ Interval │ │
│ │ │ │
│ Agent │ (3) Send Heartbeat │ Server │
│ ├──────────────────────►│ │
│ │ │ │
│ │ ... heartbeat │ │
│ │ interval │ │
│ │ │ │
│ │ (4) Send Heartbeat │ │
│ ├──────────────────────►│ │
│ │ │ │
└──────────┘ └──────────┘
- 代理连接到服务器,并可选择设置 ReportsHeartbeat 能力。如果代理未设置此能力,则服务器不应期望收到心跳。
- 如果代理设置了 ReportsHeartbeat 能力,服务器可以通过在 OpAMPConnectionSettings 消息中的 heartbeat_interval_seconds 字段中设置一个间隔来响应。该值可以是所需的间隔,也可以是
0,表示客户端不应发送心跳。30 秒是推荐的默认间隔。 - 如果代理设置了 ReportsHeartbeat 能力 并且服务器未禁用心跳,则代理必须每隔服务器设置的间隔(或使用代理配置的心跳间隔)发送一次心跳消息。
- 代理将在其配置的间隔内继续发送心跳,只要其处于活动状态。
代理可以通过不设置 ReportsHeartbeat 能力来决定不发送心跳。服务器可以通过在 OpAMPConnectionSettings.heartbeat_interval_seconds 字段中响应 0 秒的值来决定不接收心跳。
OpAMPConnectionSettings.tls
状态: [开发中]
可选的 OpAMP 特定 TLS 设置。
OpAMPConnectionSettings.proxy
状态: [开发中]
可选的 OpAMP 特定代理设置。
TelemetryConnectionSettings
TelemetryConnectionSettings 消息是一组字段,构成服务器向代理提供的用于报告自身遥测的网络连接设置声明。
message TelemetryConnectionSettings {
string destination_endpoint = 1;
Headers headers = 2;
TLSCertificate certificate = 3;
TLSConnectionSettings tls = 4;
ProxyConnectionSettings proxy = 5;
}
TelemetryConnectionSettings.destination_endpoint
值必须是 OTLP/HTTP/Protobuf 接收器的完整 URL,并带有路径。Schema 应以 https:// 开头,例如 https://example.com:4318/v1/metrics。代理可能会拒绝发送遥测数据,如果 URL 以 http:// 开头。
TelemetryConnectionSettings.headers
连接时使用的可选标头。通常用于设置访问令牌或其他授权标头。对于基于 HTTP 的协议,代理应在请求标头中设置这些标头。例如:key=“Authorization”,Value=“Basic YWxhZGRpbjpvcGVuc2VzYW1l”。
TelemetryConnectionSettings.certificate
代理应从此以后使用提供的证书连接到目标。如果代理能够使用提供的证书进行验证和连接,则代理应忘记此连接的任何先前客户端证书。此字段是可选的:如果省略,客户端不应使用客户端证书。此字段可用于执行客户端证书撤销/轮换。
TelemetryConnectionSettings.tls
状态: [开发中]
可选的遥测特定 TLS 设置。
TelemetryConnectionSettings.proxy
状态: [开发中]
可选的遥测特定代理设置。
OtherConnectionSettings
OtherConnectionSettings 消息是一组字段,构成服务器向代理提供的网络连接设置声明。此消息中并非所有字段都必须指定。服务器可能仅指定其中一些字段,在这种情况下,表示服务器希望代理仅更改这些字段,而其余字段保持不变。
例如,服务器可以发送一个仅设置了 certificate 字段而未设置其他所有字段的 ConnectionSettings 消息。这意味着服务器希望代理使用新证书,并使用当前的标头和其他设置继续发送到当前正在发送的目标。
对于引用其他消息的字段,当引用未设置时,该字段被视为未设置。
对于原始字段(字符串),我们依赖“flags”来描述字段未设置(这是为了克服旧 protoc 编译器不生成允许检查字段存在性的方法的限制)。
message OtherConnectionSettings {
string destination_endpoint = 1;
Headers headers = 2;
TLSCertificate certificate = 3;
map<string, string> other_settings = 4;
TLSConnectionSettings tls = 5;
ProxyConnectionSettings proxy = 6;
}
OtherConnectionSettings.destination_endpoint
URL、主机:端口或其他目标指定符。
OtherConnectionSettings.headers
连接时使用的可选标头。通常用于设置访问令牌或其他授权标头。对于基于 HTTP 的协议,代理应在请求标头中设置这些标头。例如:key=“Authorization”,Value=“Basic YWxhZGRpbjpvcGVuc2VzYW1l”。
OtherConnectionSettings.certificate
代理应从此以后使用提供的证书连接到目标。如果代理能够使用提供的证书进行验证和连接,则代理应忘记此连接的任何先前客户端证书。此字段是可选的:如果省略,客户端不应使用客户端证书。此字段可用于执行客户端证书撤销/轮换。
OtherConnectionSettings.other_settings
其他连接设置。这些设置特定于代理,由代理自行解释。
OtherConnectionSettings.tls
状态: [开发中]
可选的连接特定 TLS 设置。
OtherConnectionSettings.proxy
状态: [开发中]
可选的连接特定代理设置。
TLSConnectionSettings 消息
状态: [开发中]
该消息携带用于配置客户端连接的可选 TLS 设置。如果代理能够验证连接设置,则代理应忘记任何先前已有的 TLS 设置。如果未包含此消息,客户端应假定设置未更改,并继续使用现有设置。
message TLSConnectionSettings {
string ca_pem_contents = 1;
bool include_system_ca_certs_pool = 2;
bool insecure_skip_verify = 3;
string min_version = 4;
string max_version = 5;
repeated string cipher_suites = 6;
}
TLSConnectionSettings.ca_pem_contents
ca_pem_contents 属性可用于在 TLS 配置中提供 CA 的公钥证书。
TLSConnectionSettings.include_system_ca_pool
include_system_ca_pool 要求代理在构建 TLS 配置时使用系统的默认 CA 池。
TLSConnectionSettings.insecure_skip_verify
此设置禁用连接的所有 TLS 验证。
TLSConnectionSettings.min_version
此设置定义客户端将使用的最低支持的 TLS 版本。例如:1.2,TLSv1.2。
TLSConnectionSettings.max_version
此设置定义客户端将使用的最高支持的 TLS 版本。例如:1.2,TLSv1.2。
TLSConnectionSettings.ciper_suites
此设置定义连接可能使用的支持的密码套件。例如:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA。
ProxyConnectionSettings 消息
状态: [开发中]
该消息携带用于配置客户端连接的可选代理设置。如果代理能够验证连接设置,则代理应忘记任何先前已有的代理设置。如果未包含此消息,客户端应假定设置未更改,并继续使用现有设置。
如果代理需要 mTLS 证书或任何 TLS 设置,客户端应使用相关连接的设置。
message ProxyConnectionSettings {
string url = 1;
Headers connect_headers = 2;
}
ProxyConnectionSettings.url
URL 必须不能为空,例如:https://example.com:8443 或 127.0.0.1:8443。
ProxyConnectionSettings.connect_headers
客户端应为基于 HTTP 的代理初始 CONNECT 请求设置的可选标头。其他代理类型(如 SOCKS5)可能会忽略这些标头。例如:key=“Authorization”,Value=“Basic YWxhZGRpbjpvcGVuc2VzYW1l”。
Headers 消息
message Headers {
repeated Header headers = 1;
}
message Header {
string key = 1;
string value = 2;
}
TLSCertificate 消息
该消息携带一个 TLS 证书,可用作客户端证书。
证书(cert、private_key)对应颁发和签名,并由目标服务器识别的证书颁发机构 (CA) 签名。
或者,证书可以是自签名的,假设服务器可以验证该证书。在这种情况下,可以省略 ca_cert 字段。
message TLSCertificate {
bytes cert = 1;
bytes private_key = 2;
bytes ca_cert = 3;
}
TLSCertificate.cert
PEM 编码的证书。必需。
TLSCertificate.private_key
证书的 PEM 编码私钥。必需。
TLSCertificate.ca_cert
签名 CA 的 PEM 编码证书。可选,如果证书是 CA 签名的,则必须指定。可以由中间 TLS 终止代理存储,以便将来验证连接的客户端证书。
自身遥测报告
状态: Beta
自身遥测报告是 OpAMP 协议的可选功能。服务器可以向代理提供一个目标,代理可以将自身的遥测数据(指标、跟踪或日志)发送到该目标。如果代理有能力生成遥测数据并希望这样做,则它应使用 OTLP/HTTP 协议将其遥测数据发送到提供的目标。
┌────────────┬────────┐ ┌─────────┐
│ │ OpAMP │ OpAMP │ OpAMP │
│ │ ├──────────►│ │
│ │ Client │ │ Server │
│ └────────┤ └─────────┘
│ Agent │
│ ┌────────┤ ┌─────────┐
│ │OTLP │ OTLP/HTTP │ OTLP │
│ │ ├──────────►│ Metric │
│ │Exporter│ │ Backend │
└────────────┴────────┘ └─────────┘
服务器通过发送带有已填充 connection_settings 字段的 ServerToAgent 消息来发出声明,该字段包含一个或多个 own_metrics、own_traces、own_logs 字段。这些字段中的每一个都描述了一个可以接收 OTLP 协议遥测数据 else 的目标。
服务器应在向特定代理发送第一个 ServerToAgent 消息时(通常是在响应客户端的第一个状态报告时)填充 connection_settings 字段(除非没有可用的 OTLP 后端)。服务器还应在后续的 ServerToAgent 消息中填充该字段(如果目标已更改)。如果目标未更改,则 connection_settings 字段不应设置。当代理收到未设置 connection_settings 字段的 ServerToAgent 消息时,代理应继续将其遥测数据发送到先前声明的目标。如果代理具有 ReportsConnectionSettingsStatus 能力,则在收到新设置时,它应相应地设置 connection_settings_status。
代理应定期向 own_metrics 字段中声明的目标报告其指标。推荐的报告间隔为 10 秒。以下图表显示了操作序列:
Agent Client Server
Metric
│ │ │ Backend
│ │ServerToAgent{ConnectionSettingsOffer}│
┌─────────│◄─────────────────────────────────────┤ │
│ │ │
▼ │
┌────────┐ │
│Collect │ OTLP Metrics │ ──┐
│Own ├───────────────────────────────────────────────►│ │
│Metrics │ │ │
└────────┘ ... . │ Repeats
│ │
┌────────┐ │ │ Periodically
│Collect │ OTLP Metrics │ │
│Own ├───────────────────────────────────────────────►│ │
│Metrics │ │ ──┘
└────────┘ │
代理应报告代理进程(或进程)的指标以及任何描述代理状态的自定义指标。报告的进程指标必须遵循 OpenTelemetry 进程约定。
类似地,代理应向 own_traces 字段中声明的目标报告其跟踪,并向 own_logs 字段中声明的目标报告日志。
AgentDescription 消息中的 identifying_attributes 字段中指定的所有属性也应在报告的 OTLP 遥测数据的 Resource 中指定。
AgentDescription 消息中的 non_identifying_attributes 字段中指定的属性也可能在报告的 OTLP 遥测数据的 Resource 中指定,在这种情况下,它们应具有完全相同的值。
配置
代理配置是 OpAMP 协议的可选功能。远程配置功能可以根据需要禁用(例如,在使用编排系统(如 Kubernetes)现有的配置功能时)。
服务器可以通过在 ServerToAgent 消息中设置 remote_config 字段来向代理提供远程配置。由于 ServerToAgent 消息通常由服务器响应代理的状态报告而发送,因此服务器拥有代理的描述,并可以根据需要为特定代理定制所提供的配置。
如果代理能够接受远程配置,OpAMP 客户端必须设置 AgentToServer.capabilities 的 AcceptsRemoteConfig 位。如果该位未设置,服务器不得向代理提供远程配置。
代理实际用于运行的配置可能与服务器提供的远程配置不同。这种实际配置称为代理的有效配置。有效配置通常由代理在合并远程配置与其他可用输入(例如,本地可用的配置)后形成。
一旦形成有效配置,代理将使用它进行操作,客户端还将通过状态报告的 effective_config 字段向 OpAMP 服务器报告有效配置。服务器通常允许最终用户查看有效配置以及客户端在状态报告中报告的其他数据。
如果代理能够报告有效配置,客户端必须设置 AgentToServer.capabilities 的 ReportsEffectiveConfig 位。如果该位未设置,服务器不应期望 AgentToServer.effective_config 字段被设置。
这是典型的配置序列图:
Agent Client Server
│ │ AgentToServer{} │ ┌─────────┐
│ ├──────────────────────────────────►├──►│ Process │
│ │ │ │ Status │
Local │ Remote │ │ │ and │
Config │ Config │ ServerToAgent{AgentRemoteConfig} │ │ Fetch │
│ │ ┌────────┤◄──────────────────────────────────┤◄──┤ Config │
▼ │ ▼ │ │ └─────────┘
┌─────────┐ │ │
│ Config │ │ │
│ Merger │ │ │
└─────┬───┘ │ │
│ │ │
│Effective │ │
│Config │ AgentToServer{} │
└──────────►├──────────────────────────────────►│
│ │
│ │
如果在 AgentToServer 消息中已更改,则会包含 EffectiveConfig 和 RemoteConfigStatus 字段。
注意:如果有效配置或其他通过 AgentToServer 消息报告的字段未更改,客户端不应发送 AgentToServer 消息。如果客户端不遵守此规则,操作可能会导致客户端和服务器之间的消息无限循环。
服务器也可以主动发送远程配置,而无需等待客户端的状态报告。这可用于重新配置已连接但没有任何新内容可报告的代理。在这种情况下,序列图如下所示:
Agent Client Server
│ │ │
│ │ │
│ │ │ ┌────────┐
Local │ Remote │ │ │Initiate│
Config │ Config │ ServerToAgent{AgentRemoteConfig} │ │and │
│ │ ┌────────┤◄──────────────────────────────────┤◄──┤Send │
▼ │ ▼ │ │ │Config │
┌─────────┐ │ │ └────────┘
│ Config │ │ │
│ Merger │ │ │
└────┬────┘ │ │
│ │ │
│Effective │ │
│Config │ AgentToServer{} │
└──────────►├──────────────────────────────────►│
│ │
│ │
如果代理不想被服务器远程控制其配置,则可以忽略远程配置的声明。
配置文件
代理的配置是一组命名的配置文件(这适用于远程配置和有效配置)。
文件名在集合中必须是唯一的。远程配置和本地配置可能包含同名但内容不同的文件。如何将这些文件合并以形成有效配置是特定于代理类型的,不属于 OpAMP 协议的一部分。
如果集合中只有一个配置文件,则文件名可以为空。
配置文件集合使用 AgentConfigMap 消息表示。
message AgentConfigMap {
map<string, AgentConfigFile> config_map = 1;
}
AgentConfigSet 的 config_map 字段是一个配置文件映射,其中键是文件名。
对于使用单个配置文件的代理,config_map 字段应包含一个条目,并且键可以为空字符串。
AgentConfigFile 消息代表一个配置文件,并具有以下结构:
message AgentConfigFile {
bytes body = 1;
string content_type = 2;
}
body 字段包含配置文件的原始字节。原始字节的内容、格式和编码特定于代理类型,不属于 OpAMP 协议的范围。
content_type 是一个可选字段。它是描述 body 字段内容的 MIME Content-Type,例如“text/yaml”。在代理状态报告中报告的有效配置的 content_type 可用于例如服务器在 UI 中以良好的方式可视化报告的配置。
安全注意事项
远程配置是一项潜在危险的功能,可能会被恶意攻击者利用。例如,如果代理能够收集本地文件并通过网络发送,那么被攻破的 OpAMP 服务器可能会向代理提供恶意远程配置,并迫使代理收集敏感的本地文件并发送到特定的网络目标。
请参阅安全部分以获取通用建议以及针对远程重新配置功能的建议。
AgentRemoteConfig 消息
消息具有以下结构
message AgentRemoteConfig {
AgentConfigMap config = 1;
bytes config_hash = 2;
}
软件包
状态: Beta
每个代理由一个或多个软件包组成。软件包具有名称和存储在文件中的内容。文件内容、软件包提供的功能、它们如何在代理端存储和使用,这些都特定于代理类型,不属于 OpAMP 协议的范围。
有两种类型的软件包:顶级软件包和子软件包。
通常只有一个顶级软件包,它实现了代理的主要功能。当只有一个顶级软件包时,它可以有一个空名称。
子软件包也称为附加组件或插件。子软件包可以安装在代理上以增加功能(因此得名附加组件)。
代理可以安装一个或多个软件包。每个软件包都有一个名称。代理不能安装具有相同名称的软件包超过一个。
不同的软件包可能具有同名的文件,文件名不是全局唯一的,它们仅在特定软件包的范围内是唯一的。
软件包可以由本地提供和安装(例如,由本地用户)。软件包也可以由服务器远程提供给代理,在这种情况下,代理可以下载和安装软件包。
为了向代理提供软件包,服务器会在发送给代理的 ServerToAgent 消息中设置 packages_available 字段,该消息可以响应代理的状态报告,或者由服务器主动发送,如果服务器希望将软件包推送到代理。
PackagesAvailable 消息描述了服务器为该代理可用的软件包。对于每个软件包,该消息描述了包含软件包内容的文件的信息,并提供了一个可以通过 HTTP GET 请求下载文件的 URL。这些 URL 指向下载服务器(可能是与 OpAMP 服务器相同的服务器或不同的服务器)上的软件包文件。
该协议仅支持每个软件包一个可下载文件。如果代理的软件包概念上由多个文件组成,那么代理和服务器可以约定将文件存储在任何允许将多个文件存储在单个文件中的文件格式中,例如 zip 或 tar 文件。下载单个软件包文件后,代理可能会提取其中包含的文件。具体如何实现是特定于代理的,超出了协议的范围。
仅当 OpAMP 客户端在 AgentToServer.capabilities 字段的 AcceptsPackages 位中指示代理可以接受软件包时,服务器才允许提供软件包声明。
下载软件包
在收到 PackagesAvailable 消息后,代理应遵循以下下载过程:
步骤 1
将它拥有的所有软件包的聚合哈希值与服务器在 all_packages_hash 字段中提供的聚合哈希值进行比较。
如果聚合哈希值相同,则认为下载过程已完成,因为这意味着代理上的所有软件包都与服务器提供的相同。否则,转到步骤 2。
步骤 2
对于服务器提供的每个软件包,代理应检查是否应下载该特定软件包。
- 如果代理没有具有指定名称的软件包,则应下载该软件包。有关如何下载每个软件包文件的信息,请参阅步骤 3。
- 如果代理拥有该软件包,代理应将其拥有的软件包的哈希值与服务器在
PackageAvailable消息的 hash 字段中提供的软件包的哈希值进行比较。如果哈希值相同,则软件包相同,该软件包的处理完成,继续处理下一个软件包。如果哈希值不同,则按步骤 3 中所述检查软件包文件。
最后,如果代理程序有服务器未提供的任何程序包,则代理程序应删除这些程序包。
步骤 3
对于服务器提供的程序包文件,代理程序应检查是否应下载该文件。
- 如果代理程序没有指定名称的文件,则应下载该文件。
- 如果代理程序拥有该文件,则代理程序应将其本地拥有的文件的哈希与DownloadableFile消息中的hash字段进行比较。如果哈希相同,则此文件的处理完成。否则,提供的文件不同,应从DownloadableFile消息的download_url字段中指定的download_url位置下载该文件。代理程序应使用HTTP GET消息下载该文件。代理程序应在GET请求中包含DownloadableFile消息的headers字段中提供的HTTP头。
上述过程允许代理程序仅高效地下载新程序包或已更改的程序包,并且仅下载新文件或已更改的文件。
下载程序包后,代理程序可以执行任何特定于代理程序类型的附加处理(例如,“安装”或“激活”程序包,以任何特定于代理程序的方式)。
程序包状态报告
在下载和安装过程中,代理程序可能会定期报告过程状态。为此,OpAMP客户端应发送AgentToServer消息,并相应地设置package_statuses字段。
一旦所有程序包的下载和安装完成(成功或失败),客户端应将所有程序包的状态报告给服务器。
以下是程序包下载和状态报告过程的典型序列图。
Download Agent/Client OpAMP
Server Server
│ │ │
│ │ ServerToAgent{PackagesAvailable}│
│ │◄─────────────────────────────────┤
│ HTTP GET │ │
│◄────────────────┤ │
│ Download file #1│ │
├────────────────►│ │
│ │ AgentToServer{PackageStatuses} │
│ ├─────────────────────────────────►│
│ HTTP GET │ │
│◄────────────────┤ │
│ Download file #2│ │
├────────────────►│ │
│ │ AgentToServer{PackageStatuses} │
│ ├─────────────────────────────────►│
│ │ │
. . .
│ HTTP GET │ │
│◄────────────────┤ │
│ Download file #N│ │
├────────────────►│ │
│ │ AgentToServer{PackageStatuses} │
│ ├─────────────────────────────────►│
│ │ │
客户端必须始终在PackageStatuses消息中包含代理程序拥有或正在处理(下载或安装)的所有程序包。
请注意,客户端还可以报告代理程序已本地安装的程序包的状态,而不仅仅是已从服务器提供和下载的程序包。
计算哈希
代理程序和服务器使用哈希来标识文件和程序包的内容,以便代理程序可以决定需要下载哪些文件和程序包。
哈希的计算由服务器执行。服务器必须选择一种具有最小冲突概率的强哈希计算方法(如果实现需要此类保证,则可以在计算中播种随机值以保证哈希的唯一性)。
哈希对代理程序来说是不透明的,代理程序从不计算哈希,它只存储和比较它们。
有3个级别的哈希。
文件哈希
程序包文件内容的哈希。这存储在DownloadableFile消息的content_hash字段中。代理程序应使用此值来确定其拥有的特定文件是否与服务器上的文件不同,是否需要重新下载。
程序包哈希
标识整个程序包(程序包名称和文件内容)的程序包哈希。此哈希存储在PackageAvailable消息的hash字段中。
代理程序应使用此值来确定其拥有的特定程序包是否与服务器上的程序包不同,是否需要重新下载。
所有程序包哈希
所有程序包哈希是特定于代理程序的PackagesAvailable消息的all_packages_hash字段中存储的,所有包名称和内容的聚合哈希。
代理程序应使用此值来确定其拥有的任何程序包是否与服务器上可用的程序包不同,是否需要重新下载。
请注意,聚合哈希不包括代理程序本地可用的程序包,这些程序包未从下载服务器下载。
安全注意事项
远程下载程序包是一项潜在危险的功能,可能会被恶意行为者利用。如果程序包包含可执行代码,则被入侵的OpAMP服务器可能会向代理程序提供恶意程序包,并迫使代理程序执行任意代码。
PackagesAvailable消息
消息具有以下结构
message PackagesAvailable {
map<string, PackageAvailable> packages = 1;
bytes all_packages_hash = 2;
}
PackagesAvailable.packages
程序包的地图。键是程序包名称。
PackagesAvailable.all_packages_hash
远程安装的所有程序包的聚合哈希。
客户端应在后续的PackageStatuses消息中包含此值。这反过来允许服务器识别代理程序有不同的程序包集,并在下一条ServerToAgent消息中指定可用的程序包。
如果服务器支持向代理程序发送程序包,并且代理程序已表明其能够接受程序包,则此字段必须始终设置。
PackageAvailable消息
此消息是服务器向代理程序提供安装新程序包或启动代理程序已有的程序包的升级或降级。消息结构如下:
message PackageAvailable {
PackageType type = 1;
string version = 2;
DownloadableFile file = 3;
bytes hash = 4;
}
PackageAvailable.type
程序包的类型,可以是插件或顶级程序包。
enum PackageType {
TopLevelPackage = 0;
AddonPackage = 1;
}
PackageAvailable.version
服务器端可用的程序包版本。例如,代理程序可以使用此信息避免下载先前已下载但安装失败的程序包。
PackageAvailable.file
程序包的可下载文件。
PackageAvailable.hash
程序包的哈希。应根据PackageAvailable消息的所有其他字段和程序包文件的内容来计算。代理程序使用此哈希来确定其拥有的程序包是否与服务器提供的程序包不同。
DownloadableFile消息
消息具有以下结构
message DownloadableFile {
string download_url = 1;
bytes content_hash = 2;
bytes signature = 3;
Headers headers = 4; // Status: [Development]
}
DownloadableFile.download_url
可以使用HTTP GET请求下载文件的URL。指定URL的服务器应支持范围请求,以允许恢复下载。
DownloadableFile.content_hash
文件内容的SHA256哈希。代理程序可以使用此哈希来验证文件是否已正确下载。
DownloadableFile.signature
文件内容的可选签名。代理程序可以使用此签名来验证下载文件的真实性,例如可以是GPG分离签名。确切的签名和验证方法是代理程序特定的。有关建议,请参阅代码签名。
DownloadableFile.headers
状态: [开发中]
用于HTTP GET请求的可选头。通常用于设置访问令牌或其他授权头。对于基于HTTP的协议,代理程序应在请求头中设置这些头。例如:键=“Authorization”,值=“Basic YWxhZGRpbjpvcGVuc2VzYW1l”。
自定义消息
状态: [开发中]
动机
OpAMP协议旨在涵盖远程管理和配置代理程序的各个方面。如果在此核心功能中识别出更多需求,则预计该协议将在未来版本中扩展以支持此附加行为。但是,某些用例需要与代理程序进行通信,而这些通信将不受OpAMP协议支持,因为它们不在代理程序远程管理范围内,或者仅对某个代理程序非常具体,而不能推广到其他代理程序类型。CustomCapabilities和CustomMessage允许在服务器和代理程序之间实现自定义行为,而无需打开另一个连接或定义全新的协议。
虽然许多代理程序和代理程序管理服务器之间的互操作性是OpAMP的目标,但额外的自定义功能可能仅由特定的代理程序和服务器支持。在适当的情况下,鼓励实现者记录其CustomMessage的使用情况,以便可以在其他平台上添加支持。如果适当,广泛受服务器和代理程序实现支持的自定义功能可能会在未来被采纳为标准的OpAMP功能。
CustomCapabilities
代理程序和服务器都应使用此消息来指示它们支持特定的自定义功能。它在ServerToAgent和AgentToServer上均受支持。此消息仅在支持的自定义功能列表发生更改时发送。发送此消息时,支持的自定义功能列表应更新为与此列表匹配。如果从未发送此消息,则假定不支持任何自定义功能。
功能必须通过反向FQDN和可选的版本信息来标识。这可以避免不同组织引入的功能之间的冲突。功能名称由创建自定义功能的组织的注册域名(FQDN)的反向拼写、一个点和一个简短的名称组成,该名称标识该功能。版本是可选的,并通过斜杠与功能名称分隔。
例如,“com.company.capability/v2”标识由“company.com”创建的“capability”的第2版。
如果收到具有不支持的功能的CustomMessage,则可以忽略该消息。支持一项功能意味着支持该功能的所有相应的CustomMessages。确切的行为取决于自定义功能。
message CustomCapabilities {
repeated string capabilities = 1; // Status: [Development]
}
CustomCapabilities.capabilities
一项支持的自定义功能列表。每个功能都是一个反向FQDN,并带有可选的版本信息,该信息唯一标识自定义功能,并且应与支持的CustomMessage中指定的功能匹配。
CustomMessage
CustomMessage允许在代理程序和服务器之间发送自定义消息。它在ServerToAgent和AgentToServer上均受支持,以允许双向自定义通信。它要求代理程序和服务器都就消息的内容和编码达成一致。如果不支持该功能或无法识别消息类型,则可以忽略该消息。
CustomMessage标识消息类型,并包含消息内容的二进制数据。数据的格式取决于消息类型,并且超出了OpAMP协议的范围。
message CustomMessage {
string capability = 1;
string type = 2;
bytes data = 3;
}
CustomMessage.capability
一个反向FQDN,唯一地标识功能,并匹配CustomCapabilities消息中支持的功能之一。
CustomMessage.type
功能内的消息类型。该功能定义了用于实现该功能的消息类型。该类型在功能内必须是唯一的。
CustomMessage.data
消息的二进制数据。功能必须为它定义的每种自定义消息类型指定数据内容的格式。
示例
以下示例描述了CustomCapabilities和CustomMessage的可能用法,但并非旨在成为任何规范的一部分。在这些示例中,“io.opentelemetry.”用于创建消息类型的FQDN,但每个供应商都应使用自己的FQDN。
暂停/恢复示例
假设一个代理程序支持暂停和恢复遥测收集的功能。暂停时,不收集或发送遥测数据。为了让服务器控制此行为,可以使用CustomMessages引入CustomCapability。
代理连接
连接时,代理程序发送一个AgentToServer消息,其中包含CustomCapabilities,包括“io.opentelemetry.pause”功能。作为响应,服务器发送一个ServerToAgent消息,其中包含一个CustomCapabilities消息,包括“io.opentelemetry.pause”。
CustomCapabilities {
capabilities: ["io.opentelemetry.pause"]
}
暂停
服务器发送一个ServerToAgent消息,其中包含类型为“pause”的以下CustomMessage。不发送数据,因为这是一个简单的命令,没有额外信息。
CustomMessage {
capability: "io.opentelemetry.pause"
type: "pause"
}
如果代理程序支持此消息并成功暂停,则它会返回一个AgentToServer消息,其中包含类型为“status”的CustomMessage,其数据包含二进制JSON编码的响应,其中包含新的暂停状态。
CustomMessage {
capability: "io.opentelemetry.pause"
type: "status"
data: {
"paused": true
}
}
如果代理程序支持此消息但尝试暂停时遇到错误,则它会返回一个AgentToServer消息,其中包含类型为“error”的CustomMessage,其数据包含二进制JSON编码的响应,其中包含错误消息。
CustomMessage {
capability: "io.opentelemetry.pause"
type: "error"
data: {
"error": "Unable to pause"
}
}
恢复
与暂停类似,但ServerToAgent消息包含类型为“resume”的CustomMessage,成功后,新状态将为{ "paused": false }。
CustomMessage {
capability: "io.opentelemetry.pause"
type: "resume"
}
服务发现示例
服务发现涉及发现可供代理程序访问的正在运行的服务,代理程序可以为这些服务收集遥测数据。在此示例中,服务器将发送一条消息给代理程序,以请求可用服务。它将期望代理程序从消息中进行响应,其中包含有关可用服务的信息。
代理连接
连接时,代理程序发送一个CustomCapabilities消息,包括“io.opentelemetry.discovery”功能。作为响应,服务器发送一个CustomCapabilities消息,其中也包括“io.opentelemetry.discovery”功能。由于此功能同时受代理程序和服务器支持,因此可以发送与此功能相关的消息来支持发现。
FindServices
服务器发送一个ServerToAgent消息,其中包含以下CustomMessage。不发送数据,因为这是一个简单的请求,没有额外信息。
CustomMessage {
capability: "io.opentelemetry.discovery"
type: "find_services"
}
FindServicesResponse
发现服务后,代理程序发送一个AgentToServer消息,其中包含一个CustomMessage,其二进制数据包含复杂数据结构的JSON编码,该结构包含有关其发现的所有服务的信息。此数据结构还将包括一个可选字段,用于报告发现过程中遇到的错误。
CustomMessage {
capability: "io.opentelemetry.discovery"
type: "available_services"
data: { ... }
}
AvailableComponents消息
状态: [开发中]
每个代理程序可能由多个子组件组成。这些组件可能有自己的相关元数据。AvailableComponents消息允许代理程序告知OpAMP服务器代理程序包含哪些组件。
AvailableComponents消息具有以下结构:
message AvailableComponents {
map<string, ComponentDetails> components = 1;
bytes hash = 2;
}
AvailableComponents.components
components字段包含一个从唯一ID到ComponentsDetails消息的映射。如果OpAMP服务器没有通过在前面的ServerToAgent消息中设置ReportAvailableComponents标志明确请求此字段,则可以从消息中省略此字段。
示例
OpenTelemetry Collector
以下是一个示例,说明ComponentDetails如何包含有关自定义构建的OpenTelemetry Collector的包含组件的信息。
{
"receivers": {
"sub_component_map": {
"hostmetrics": {
"metadata": {
"code.namespace": "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver@v0.107.0",
}
}
}
},
"processors": {
"sub_component_map": {
"batch": {
"metadata": {
"code.namespace": "go.opentelemetry.io/collector/processor/batchprocessor@v0.107.0"
}
},
"transform": {
"metadata": {
"code.namespace": "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor@v0.107.0"
}
},
}
},
"exporters": {
"sub_component_map": {
"nop": {
"metadata": {
"code.namespace": "go.opentelemetry.io/collector/exporter/nopexporter@v0.107.0"
}
}
}
}
// ... Component list continues for extensions and collectors ...
}
Fluent Bit
以下是一个示例,说明Fluent Bit如何报告其拥有的可用组件。
{
"input": {
"sub_component_map": {
"tail": {}
}
},
"parser": {
"sub_component_map": {
"json": {}
}
},
"filter": {
"sub_component_map": {
"lua": {},
"modify": {}
}
},
"output": {
"sub_component_map": {
"null": {},
"file": {},
}
}
}
AvailableComponents.hash
代理程序计算的components字段的哈希。如果代理程序能够报告其拥有的可用组件,则必须包含此哈希。其格式由代理程序确定。具有相同组件集的任何代理程序的哈希应相同。
初始握手
为了减少传输的数据量,AvailableComponents消息最初不包含整个components映射。相反,AvailableComponents消息将包含代理程序计算的哈希,而components映射为空。
初始AgentToServer消息应包含AvailableComponents消息,其中仅设置了代理程序计算的哈希。
OpAMP服务器可以使用此哈希来确定它是否还记得AvailableComponents集。如果服务器上找不到此哈希,则服务器可以通过在ServerToAgent消息中设置ReportAvailableComponents标志来请求报告完整的components映射。如果指定了此标志,则代理程序将用可用组件的完整描述填充components字段。
服务器然后可以选择性地为该哈希持久化组件,以便服务器在后续连接上不再需要再次请求它们。
AvailableComponents消息也受状态压缩的影响,如果哈希与之前的值未更改,则可以省略。
ComponentDetails消息
ComponentDetails消息描述了代理程序的组件。
ComponentDetails的结构不必与ComponentHealth结构一一对应。ComponentHealth通常指当前运行的组件实例,而ComponentDetails指可用的组件类型,这些组件可能不一定当前正在运行。同一组件类型也可能拥有多个正在运行的实例。
ComponentDetails消息具有以下结构:
message ComponentDetails {
repeated KeyValue metadata = 1;
map<string, ComponentDetails> sub_component_map = 2;
}
ComponentDetails.metadata
用于描述组件的额外键/值对。
键/值对应遵循语义约定(如果适用),有关更多信息,请参阅OpenTelemetry语义约定。
ComponentDetails.sub_component_map
唯一组件ID到子组件详细信息的映射。它可以嵌套任意深度,以描述底层系统。
连接管理
建立连接
客户端通过建立HTTP(S)连接来连接到服务器。
如果使用WebSocket传输,则连接将根据WebSocket标准升级为WebSocket。
建立连接后,客户端必须发送第一个状态报告并期望收到响应。
如果客户端无法建立与服务器的连接,则应重试连接尝试,并使用指数退避策略和抖动,以避免使服务器过载。
在重试连接尝试时,客户端应遵守从服务器收到的任何限流响应。
关闭连接
WebSocket传输,OpAMP客户端发起
要关闭连接,客户端必须首先发送一个设置了agent_disconnect字段的AgentToServer消息。然后,客户端必须发送一个WebSocketClose控制帧,并遵循WebSocket标准定义的Close控制帧。
WebSocket传输,服务器发起
要关闭连接,服务器必须发送一个WebSocketClose控制帧,并遵循WebSocket标准定义的Close控制帧。
纯HTTP传输
一旦OpAMP HTTP响应完成,客户端就被认为逻辑上断开连接。客户端无需发送设置了agent_disconnect字段的AgentToServer消息,因为在HTTP响应完成后,客户端连接始终被假定为已消失。
客户端和服务器可以使用HTTP keep-alive,但它对OpAMP协议的逻辑操作没有影响。
服务器可以根据其自身的业务逻辑来决定它如何将活动代理程序(例如,持续轮询的客户端)与非活动代理程序(例如,在特定时间段内未进行HTTP请求的客户端)区分开。此业务逻辑超出了OpAMP规范的范围。
恢复WebSocket连接
如果建立的WebSocket连接意外断开,客户端应立即尝试重新连接。如果重新连接失败,客户端应继续尝试连接,并带有建立连接中所述的退避策略。
重复的WebSocket连接
每个客户端实例连接到服务器的次数不应超过一次。如果客户端需要重新连接到服务器,则客户端必须确保首先发送AgentDisconnect消息,然后关闭现有连接,之后再尝试重新连接。
如果服务器检测到同一客户端实例拥有多个并发连接,或者多个代理程序实例使用相同的instance_uid,则服务器可以断开连接或拒绝提供服务。
服务器应检测重复的instance_uid(这可能发生,例如,当代理程序使用错误的UID生成器或由于运行代理程序的VM被克隆时)。当检测到重复的instance_uid时,服务器应生成一个新的instance_uid,并将其作为AgentIdentification的new_instance_uid值发送。
身份验证
状态: Beta
客户端和服务器可以使用HTTP支持的身份验证方法,例如Basic身份验证或Bearer身份验证。身份验证在HTTP连接建立后、升级到WebSocket连接之前进行。
如果客户端身份验证失败,服务器必须响应401 Unauthorized。
错误请求
如果服务器收到格式错误的AgentToServer消息,服务器应响应一个ServerToAgent消息,并相应地设置error_response。type字段必须设置为BAD_REQUEST,而error_message应是AgentToServer消息问题的可读描述。
客户端不应重试发送已收到BAD_REQUEST响应的AgentToServer消息。
重试消息
如果出现以下情况,客户端可以重试发送AgentToServer消息:
- 发送了需要响应的AgentToServer消息,但在合理的时间内(超时可能是可配置的)未收到响应。
- 发送了需要响应的AgentToServer消息,但在收到响应之前连接丢失。
- 收到服务器的UNAVAILABLE响应,如限流部分所述。
对于需要响应的消息,如果服务器收到同一消息多次,则服务器必须对每条消息做出响应,而不仅仅是第一条消息,即使服务器检测到重复并只处理一次消息。
请注意,如果连接不可用,客户端不需要维护一个不断增长的要发送到服务器的消息队列。客户端通常只需要保留一种要发送到服务器的最新消息,并在连接可用时立即发送。
例如,客户端应跟踪代理程序的状态,并准备好发送AgentToServer消息。如果客户端无法发送AgentToServer消息(例如,因为连接尚未可用),则代理程序状态更改时,客户端不需要创建新的AgentToServer消息并将其放入队列准备发送。客户端只需保留一个最新的AgentToServer消息,并在有机会时发送。这当然要求AgentToServer消息包含自上次报告以来的所有更改,并正确反映代理程序的当前(最后)状态。
同样,所有其他代理程序报告功能,如插件状态报告或代理程序包安装状态报告,要求客户端仅保留一个最新的状态消息,并在最早有机会时发送。
反之亦然:服务器通常只需要保留一种特定的消息的最新版本,它想要传递给代理程序,并在与客户端的连接可用时立即发送。
限流
WebSocket传输
当服务器过载且无法处理AgentToServer消息时,它应响应一个ServerToAgent消息,其中error_response字段设置为UNAVAILABLE。error_response字段应设置为UNAVAILABLE。type字段设置为UNAVAILABLE。客户端应断开连接,等待,然后重新连接并恢复其操作。retry_info字段可以包含retry_after_nanoseconds字段,指定客户端应等待多长时间后才能重新连接。
message RetryInfo {
uint64 retry_after_nanoseconds = 1;
}
如果未设置retry_info,则客户端应实施指数退避策略,以逐渐增加重试之间的间隔。
纯HTTP传输
在纯HTTP传输以及WebSocket使用且服务器过载且无法将HTTP连接升级为WebSocket的情况下,服务器可以返回HTTP 503 Service Unavailable或HTTP 429 Too Many Requests响应,并且可以可选地设置Retry-After头来指示客户端何时应尝试重新连接。客户端应遵守HTTP规范中的相应要求。
注意:Retry-After头仅应用于客户端尝试重新连接到服务器。在代理程序重新连接时,客户端不应尝试发送常规的心跳消息。
建议的最小重试间隔为30秒。
安全
远程配置和可下载程序包存在重大的安全风险。通过发送恶意的服务器端配置或恶意程序包,服务器可能会迫使代理程序执行不期望的工作。本节定义了降低代理程序安全风险的建议。
本节中的指南是可选的,但对于敏感应用程序强烈推荐。
一般性建议
我们建议代理程序采用零信任安全模型,并且不自动信任从服务器收到的远程配置或其他提议。代理程序应验证和清理从服务器接收的数据,以限制和防止恶意行为者可能造成的损害。我们建议如下:
- 代理程序应以最低的可能权限运行,以防止其访问敏感文件或执行高权限操作。代理程序不应以root用户身份运行,否则被入侵的代理程序可能导致恶意行为者完全控制机器。
- 如果代理程序能够收集本地数据,它应该将其限制在特定目录集。此限制应在本地指定,并且不应通过远程配置覆盖。如果不遵守此规则,可能会利用远程配置功能访问代理程序机器上的敏感信息。
- 如果代理程序能够执行位于其运行所在机器上的外部代码,并且此功能可以在代理程序的配置中指定,那么代理程序应仅将此类功能限制在位于有限目录集中的特定脚本。此限制应在本地指定,并且不应通过远程配置覆盖。如果不遵守此规则,可能会利用远程配置功能在代理程序机器上执行任意代码。
配置限制
建议代理程序限制其可能通过远程配置被强制执行的操作。
特别地,如果可以通过配置要求代理程序从其运行的机器收集数据(正如遥测收集代理程序通常所做的那样),那么我们建议在代理程序端设置限制,规定代理程序允许收集哪些目录或文件。在收到远程配置后,代理程序必须根据限制列表验证配置,并在违反限制时拒绝完全或部分应用配置,或者清理配置,使其不从受禁止的目录或文件中收集数据。
同样,如果配置提供了命令代理程序在其运行的机器上执行进程或脚本的手段,我们建议在代理程序端设置限制,规定代理程序允许从哪些目录运行哪些可执行文件。
建议将限制设置为“允许列表”而不是“拒绝列表”。限制可以是硬编码的,也可以是用户在本地配置文件中定义的。不应通过从服务器向代理程序发送远程配置来覆盖这些限制。
选择性远程配置
建议远程配置功能默认不在代理程序中启用。这些功能应由用户选择启用。
代码签名
程序包中的任何可执行代码都应进行签名,以防止被入侵的服务器传递恶意代码给代理程序。我们建议如下:
- 任何可下载的可执行代码(例如,可执行包)都需要进行代码签名。实际的代码签名和验证机制是 Agent 特定的,并且超出了 OpAMP 规范的范畴。
- Agent 应验证下载文件中的可执行代码,以确保代码签名有效。
- 可下载的代码可以带有包含在文件内容中的签名,或者在 DownloadableFile 消息的 <a href="#downloadablefilesignature">signature</a> 字段中记录一个分离的签名。例如,可以使用 <a href="https://www.gnupg.org/gph/en/manual/x135.html#AEN160" target="_blank" rel="noopener" class="external-link">GPG 签名</a> 来使用分离的签名。
- 如果使用证书颁发机构 (CA) 进行代码签名,则建议不要将证书颁发机构及其私钥与 OpAMP 服务器共置,以免受损的服务器签署恶意代码。
- Agent 应以最低可能权限运行任何下载的可执行代码(包和/或作为外部进程运行的任何代码),以防止代码访问敏感文件或执行高权限操作。Agent 不应以 root 用户身份运行下载的代码。
互操作性<a class="td-heading-self-link" href="#interoperability" aria-label="Heading self-link"></a>
部分实现的互操作性<a class="td-heading-self-link" href="#interoperability-of-partial-implementations" aria-label="Heading self-link"></a>
OpAMP 为 Agent 和 Server 定义了许多功能。这些功能大多是可选的。Agent 或 Server 应准备好对方不支持特定功能。
Agent 和 Server 在初始消息交换期间都会指示它们支持的功能。Client 在 AgentToServer 消息中设置功能位字段,Server 在 ServerToAgent 消息中设置功能位字段。
功能字段中每个设置的位表示支持该特定功能。Agent 功能列表<a href="#agenttoservercapabilities">在此处</a>。Server 功能列表<a href="#servertoagentcapabilities">在此处</a>。
在 Server 了解特定 Agent 的功能后,Server 必须停止使用 Agent 不支持的功能。
同样,在 Agent 了解 Server 的功能后,Agent 必须停止使用 Server 不支持的功能。
在此文档的相关部分将描述在检测到对方不支持特定功能时,Agent 和 Server 的行为需要如何改变的具体细节。
未来功能的互操作性<a class="td-heading-self-link" href="#interoperability-of-future-capabilities" aria-label="Heading self-link"></a>
OpAMP 通过两种方式实现当前规范版本实现与 OpAMP 未来扩展版本(增加了此规范中未描述的更多功能)实现的互操作性。
可忽略的功能扩展<a class="td-heading-self-link" href="#ignorable-capability-extensions" aria-label="Heading self-link"></a>
对于可以被对方静默忽略的新功能,可以通过向任何 Protobuf 消息添加新字段来扩展。实现此新功能的发送方将设置新字段。实现旧版本规范且不知道新功能的接收方将简单地忽略新字段。Protobuf 编码确保其余字段仍能被接收方成功反序列化。
不可忽略的功能扩展<a class="td-heading-self-link" href="#non-ignorable-capability-extensions" aria-label="Heading self-link"></a>
对于无法被对方静默忽略的新功能,则采用不同的方法。
AgentToServer 和 ServerToAgent 消息中的功能字段包含一些保留位。这些位应保留用于指示对 OpAMP 未来将添加的新功能的支持。
Client 和 Server 发送消息时必须将这些保留位设置为 0。这使得实现较新版本 OpAMP 的接收方能够了解到发送方不支持新功能,并相应地调整其行为。
AgentToServer 和 ServerToAgent 消息是 Client 和 Server 交换的第一批消息,使它们能够了解对方的功能并相应地调整其行为。行为如何确切地调整以适应未来功能,必须在新增功能的未来规范中定义。
Protobuf 架构稳定性<a class="td-heading-self-link" href="#protobuf-schema-stability" aria-label="Heading self-link"></a>
本规范为 OpAMP 1.0 的<a href="https://github.com/open-telemetry/opamp-spec/blob/main/proto/opamp.proto" target="_blank" rel="noopener" class="external-link">Protobuf 定义</a>提供了以下稳定性保证:
- 字段类型、编号和名称不会改变。
- 消息和枚举的名称不会改变。
- 分配给枚举选项的编号不会改变。
- 枚举选项的名称不会改变。
- 消息和枚举的位置,即它们是声明在顶层词法范围还是嵌套在另一个消息中,都不会改变。
- 包名和目录结构不会改变。
- 现有字段的 `optional` 和 `repeated` 声明器不会改变。
- 不会删除任何现有符号。
请注意,上述保证不适用于标记为 `[Beta]` 的消息和字段。<a href="https://github.com/open-telemetry/community/blob/47813530864b9fe5a5146f466a58bd2bb94edc72/maturity-matrix.yaml#L57" target="_blank" rel="noopener" class="external-link">Beta</a> 消息和字段的保证较弱,如<a href="https://github.com/open-telemetry/community/blob/47813530864b9fe5a5146f466a58bd2bb94edc72/maturity-matrix.yaml#L57" target="_blank" rel="noopener" class="external-link">成熟度矩阵</a>中所述。
OpAMP 规范的未来版本可以通过修改此规范版本中定义的 Protobuf 架构来扩展。允许进行以下 Protobuf 架构更改,前提是它们符合本规范其他地方定义的互操作性要求。
- 向现有消息添加新字段。
- 添加新消息或枚举。
- 向现有枚举添加新选项。
- 向现有 `oneof` 字段添加新选项。
未来可能性<a class="td-heading-self-link" href="#future-possibilities" aria-label="Heading self-link"></a>
为集中式代理定义规范,该代理可作为中介,在需要管理数百万甚至更多 Agent 时,减少与服务器的连接数。
参考
Agent 管理<a class="td-heading-self-link" href="#agent-management" aria-label="Heading self-link"></a>
- Splunk <a href="https://docs.splunk.com/Documentation/Splunk/latest/Updating/Aboutdeploymentserver" target="_blank" rel="noopener" class="external-link">Deployment Server</a>
- vRealize <a href="https://docs.vmware.com/en/vRealize-Log-Insight/8.10/com.vmware.log-insight.agent.admin.doc/GUID-40C13E10-1554-4F1B-B832-69CEBF85E7A0.html" target="_blank" rel="noopener" class="external-link">Log Insight Agents</a> 的集中化配置
- Google Cloud <a href="https://github.com/GoogleCloudPlatform/guest-agent" target="_blank" rel="noopener" class="external-link">Guest Agent</a> 使用 HTTP <a href="https://cloud.google.com/compute/docs/metadata/querying-metadata#waitforchange" target="_blank" rel="noopener" class="external-link">长轮询</a>
配置管理<a class="td-heading-self-link" href="#configuration-management" aria-label="Heading self-link"></a>
- Uber Flipr
- Facebook 的<a href="https://research.facebook.com/file/877841159827226/holistic-configuration-management-at-facebook.pdf" target="_blank" rel="noopener" class="external-link">整体配置管理</a> (push)