通知(notifications)
registry支持webhook类型的通知架构。在manifest的推送和拉取、layer推送和拉取的时候,会触发通知。这些操作会被转换成一系列的事件。这些事件会被存放到一个registry内部的广播系统的有序队列中,等待被转发到端点中(Endpoints)。
注意:这个图片官网上没有显示。
端点(endpoints)
通知是通过HTTP请求方式发送给端点的。在每个registry实例里,每一个配置的端点都拥有独立的事件队列、重做配置和http目标。当在registry里进行一个操作时,它会转换成一个事件,下抛到一个内存队列中。当事件到达队列的末端时,会发起一个http请求将其发送到端点的队列中,这个发送的操作会持续到成功为止。事件的发送虽然是顺序的,但是不能保证它们在端点出的顺序。
配置(cofiguration)
让registry实例发送通知到端点,必须进行相应的设置。下面是一个简单的例子:
notifications:
endpoints:
- name: alistener
url: https://mylistener.example.com/event
headers:
Authorization: [Bearer <your token, if needed>]
timeout: 500ms
threshold: 5
backoff: 1s
上面的例子中,在registry设置了一个端点https://mylistener.example.com/event
来接收事件,http请求头携带信息“Authorization: Bearer ”。这个请求500毫秒超时。如果连续5次请求失败,等待一秒后再重试。
想了解更加详细的设置,请看《registry配置参考》notifications。
一个有效的端点配置在registry启动的时候会输出相关的日志信息:
INFO[0000] configuring endpoint alistener (https://mylistener.example.com/event), timeout=500ms, headers=map[Authorization:[Bearer <your token if needed>]] app.id=812bfeb2-62d6-43cf-b0c6-152f541618a3 environment=development service=registry
事件(events)
事件是一个定义良好的JSON数据,其作为通知请求的body。发送一个或多个事件时,它们被封装在一个envelope的结构体中。每一个事件都有唯一的ID。一个活动(action)还提供一个target,用来记录事件过程的变化。
可以通过查看docker的源码,去了解event这个字段的详细信息。
下面是一个push操作的响应JSON信息清单:
{
"id": "asdf-asdf-asdf-asdf-0",
"timestamp": "2006-01-02T15:04:05Z",
"action": "push",
"target": {
"mediaType": "application/vnd.docker.distribution.manifest.v1+json",
"size": 1,
"digest": "sha256:0123456789abcdef0",
"length": 1,
"repository": "library/test",
"url": "http://example.com/v2/library/test/manifests/latest"
},
"request": {
"id": "asdfasdf",
"addr": "client.local",
"host": "registrycluster.local",
"method": "PUT",
"useragent": "test/0.1"
},
"actor": {
"name": "test-actor"
},
"source": {
"addr": "hostname.local:port"
}
}
注意:自2.1版本后,target中的
length
属性已经被size
替代,来迎合命名的习惯。两种设置方式在很长的一段时间都可以兼容,不过在新的代码中更加钟爱size
。
Envelope
envelope用json数据结构封装了一个或多个事件信息:
{
"events": [ ... ],
}
虽然多个事件可能通过同一个envelope发送,但是事件和envelope之间没有任何必然的关系。例如,registry可以选择一组没有关系的事件,通过一个envelope组装它们,然后发送出去以减少发送请求次数。
一个完整的数据包还包含媒体类型 “application/vnd.docker.distribution.events.v1+json”,它被设置在请求的头部发送到端点。.
下面是一个完整事件的信息清单:
GET /callback
Host: application/vnd.docker.distribution.events.v1+json
Authorization: Bearer <your token, if needed>
Content-Type: application/vnd.docker.distribution.events.v1+json
{
"events": [
{
"id": "asdf-asdf-asdf-asdf-0",
"timestamp": "2006-01-02T15:04:05Z",
"action": "push",
"target": {
"mediaType": "application/vnd.docker.distribution.manifest.v1+json",
"length": 1,
"digest": "sha256:0123456789abcdef0",
"repository": "library/test",
"url": "http://example.com/v2/library/test/manifests/latest"
},
"request": {
"id": "asdfasdf",
"addr": "client.local",
"host": "registrycluster.local",
"method": "PUT",
"useragent": "test/0.1"
},
"actor": {
"name": "test-actor"
},
"source": {
"addr": "hostname.local:port"
}
},
{
"id": "asdf-asdf-asdf-asdf-1",
"timestamp": "2006-01-02T15:04:05Z",
"action": "push",
"target": {
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
"length": 2,
"digest": "tarsum.v2+sha256:0123456789abcdef1",
"repository": "library/test",
"url": "http://example.com/v2/library/test/manifests/latest"
},
"request": {
"id": "asdfasdf",
"addr": "client.local",
"host": "registrycluster.local",
"method": "PUT",
"useragent": "test/0.1"
},
"actor": {
"name": "test-actor"
},
"source": {
"addr": "hostname.local:port"
}
},
{
"id": "asdf-asdf-asdf-asdf-2",
"timestamp": "2006-01-02T15:04:05Z",
"action": "push",
"target": {
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
"length": 3,
"digest": "tarsum.v2+sha256:0123456789abcdef2",
"repository": "library/test",
"url": "http://example.com/v2/library/test/manifests/latest"
},
"request": {
"id": "asdfasdf",
"addr": "client.local",
"host": "registrycluster.local",
"method": "PUT",
"useragent": "test/0.1"
},
"actor": {
"name": "test-actor"
},
"source": {
"addr": "hostname.local:port"
}
}
]
}
响应(responses)
registry只是接受端点返回的响应码。如果端点返回一2xx或3xx的响应码(之后做重定向),消息将被视为交付和丢弃。
监控(monitoring)
端点的状态信息是通过一个http接口debug/vars进行报告,通常配置为“http://localhost:5001/debug/vars"。
下面提供一个几个端点经历失败到自动恢复的例子:
"notifications":{
"endpoints":[
{
"name":"local-5003",
"url":"http://localhost:5003/callback",
"Headers":{
"Authorization":[
"Bearer \u003can example token\u003e"
]
},
"Timeout":1000000000,
"Threshold":10,
"Backoff":1000000000,
"Metrics":{
"Pending":76,
"Events":76,
"Successes":0,
"Failures":0,
"Errors":46,
"Statuses":{
}
}
},
{
"name":"local-8083",
"url":"http://localhost:8083/callback",
"Headers":null,
"Timeout":1000000000,
"Threshold":10,
"Backoff":1000000000,
"Metrics":{
"Pending":0,
"Events":76,
"Successes":76,
"Failures":0,
"Errors":28,
"Statuses":{
"202 Accepted":76
}
}
}
]
}
如果在一个大型的应用中使用通知(notification),检测断点队列的"Pending"参数的大小是一个关键点。如果“failures”或队列的大小不断的增长,这表明有一更大的问题。
日志也是一个很有价值的监控信息。一个失败的端点将会输出下面类似的日志:
ERRO[0340] retryingsink: error writing events: httpSink{http://localhost:5003/callback}: error posting: Post http://localhost:5003/callback: dial tcp 127.0.0.1:5003: connection refused, retrying
WARN[0340] httpSink{http://localhost:5003/callback} encountered too many errors, backing off
上面的日志信息表明几个错误导致一个重试休眠,registry休眠结束后尝试重做。
注意事项
目前,队列都是常驻内存的,所以端点还是相当的可靠的。虽然,它们采用"尽力而为"思想去设计的消息发送,但是如果一个实例丢失了,消息还是会有被丢弃的风险。如果一个端点宕掉了,应该要确保在端点恢复之前不要让registry实例停止工作或者丢失端点的信息。