Dozzle Container Actions 实战指南:容器启停/删除/更新操作与镜像更新检测
【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle
Dozzle 提供了面向 Docker 容器的“容器动作(Container Actions)”能力,允许你在 Web 界面上直接对容器执行start、stop、restart、remove与update操作。本文基于 docs/guide/actions.md 展开,结合仓库源码讲解如何开启该功能、update的底层实现原理、镜像更新检测机制(Image Update Check)及其相关配置项,帮助你安全地在生产环境中启用这套运维能力。
[!WARNING] 本文涉及的操作(尤其
remove与update)会重建容器。写入anonymous volumes(匿名卷)或容器可写层(writable layer)的数据将会丢失,而命名卷(named volumes)与 bind mounts(绑定挂载)会被保留。请在操作前确认数据落盘位置。
启用 Container Actions:从 Docker CLI 与 docker-compose 两个角度
Container Actions 是默认关闭的。开启方式有两种等价途径:
- 设置环境变量
DOZZLE_ENABLE_ACTIONS=true; - 或在启动命令中传入
--enable-actions参数。
从 internal/support/cli/args.go 可以看到,命令行参数与环境变量的对应关系正是:
EnableActions bool `arg:"--enable-actions,env:DOZZLE_ENABLE_ACTIONS" default:"false" help:"enables essential actions on containers from the web interface."`默认值为false,也就是说你不显式开启,界面上就不会出现动作菜单。
方式一:docker run
docker run --volume=/var/run/docker.sock:/var/run/docker.sock -p 8080:8080 amir20/dozzle --enable-actions方式二:docker-compose.yml
services: dozzle: image: amir20/dozzle:latest volumes: - /var/run/docker.sock:/var/run/docker.sock ports: - 8080:8080 environment: DOZZLE_ENABLE_ACTIONS: true动作背后的 HTTP 接口与权限模型
前端下拉菜单发起的动作请求,最终落在 internal/web/actions.go 中的两个路由上(路由注册见 internal/web/routes.go):
POST /hosts/{host}/containers/{id}/actions/{action}:执行start/stop/restart/remove;POST /hosts/{host}/containers/{id}/actions/update:执行镜像更新(走 SSE 流式返回进度)。
动作类型在 internal/container/types.go 中被严格限定:
const ( Start ContainerAction = "start" Stop ContainerAction = "stop" Restart ContainerAction = "restart" Remove ContainerAction = "remove" )ParseContainerAction会拒绝任何未知动作,返回unknown action错误,从服务端杜绝了非法动作注入。
值得强调的是权限模型:当配置了认证(Authorization.Provider != NONE)时,findContainerWithActions 会校验当前用户的角色是否包含auth.Actions,未授权用户直接返回403 Forbidden。也就是说,开启 Actions 后,匿名模式人人可用,但一旦启用认证,只有被授予 Actions 角色的用户才能操作容器。这在多人共享 Dozzle 实例时是重要的安全边界。
另外,动作请求还需要找到目标容器所属的 host,通过hostKey(r)定位主机、再以当前用户的容器标签过滤条件查找容器,未找到返回404。
update 动作:原地升级容器的原理与数据风险
update动作会拉取该容器镜像的最新版本,并以相同配置重建容器。它非常适合在不修改 compose 文件的情况下原地升级容器版本。
需要明确的是:update只有当镜像使用移动标签(moving tag)(例如latest、stable)时才有实际意义;如果镜像固定在某一个具体标签(pinned tag),update只是重新拉取同一镜像,容器内容不会有变化。
update 的底层实现
在 internal/support/container/agent_service.go 中,动作最终被代理到 Docker 客户端:
func (a *agentService) UpdateContainer(ctx context.Context, c container.Container, progressCh chan<- container.UpdateProgress) (bool, error) { return a.client.UpdateContainer(ctx, c.ID, progressCh) }更新过程是流式的:服务端通过 SSE(Server-Sent Events)将进度推送给前端。进度结构定义在 internal/container/types.go:
type UpdateProgress struct { Status string `json:"status"` // "pulling", "recreating", "done", "error", "up-to-date" Layer string `json:"layer"` // Docker layer ID (pull events only) Current int64 `json:"current"` // Bytes downloaded Total int64 `json:"total"` // Total bytes for layer Error string `json:"error"` // Only when Status="error" }整个过程包含pulling(拉取镜像)→recreating(重建容器)→done(完成)等阶段,Current/Total提供每一层的下载字节进度,前端据此渲染进度条。containerUpdate 中创建了容量为 50 的progressCh通道,并在 goroutine 中执行更新、在主循环中逐个推送update-progressSSE 事件,最后统一返回结果。
数据安全提醒
由于update与remove都会删除并重建容器:
- 匿名卷(anonymous volumes):数据会丢失;
- 容器可写层(writable layer):其中写入的数据会丢失;
- 命名卷(named volumes)与 bind mounts:会保留。
因此,依赖容器可写层存储状态的应用(例如某些缓存型中间件)在使用update前,务必确认其状态数据是否已迁移到命名卷或宿主机挂载目录。
镜像更新检测(Update Checking)
Dozzle 会检查容器当前运行的镜像是否仍然是其 registry 上提供的最新版本。当两者不一致时,容器菜单上会出现一个圆点,同时菜单提示“有可用更新”。
检测原理:HEAD 请求 + digest 对比
检测逻辑位于 internal/imagecheck/checker.go,核心流程如下:
- 解析镜像引用(internal/imagecheck/reference.go 中的
ParseReference),解析失败标记为unknown; - 若引用是digest 固定的(
ref.Pinned()),直接标记为pinned——按定义它不会漂移; - 若本地没有 registry digest(典型如本地构建的镜像),标记为
not-checkable; - 向 registry 发起HEAD 请求获取 manifest,仅读取
Docker-Content-Digest响应头,不下载任何层,因此不会计入 Docker Hub 的 pull 速率限制; - 将远程 digest 与 Docker 本地记录的
RepoDigests做成员比较(注意是集合成员判断而非简单相等,因为同一个 tag 可能对应多个 repo digest),命中则为up-to-date,否则为update-available。
internal/imagecheck/registry.go 中的 HEAD 请求还会处理401 Unauthorized挑战:先从 registry 返回的WWW-Authenticate中解析realm与service,获取 bearer token 后重试。同时它对认证 realm 做了TLS 校验(validateRealm),只允许 https 或回环地址,避免被恶意 registry 引导向内网明文端点。
缓存、去重与错误容忍
- 答案缓存 6 小时:
DefaultTTL = 6 * time.Hour(checker.go),因为 tag 很少漂移,长 TTL 能把 registry 流量压到极低; - 同一镜像全局只查一次:即使多个容器、多个 host 运行同一镜像,也只发起一次请求。这通过两部分实现:
singleflight.Group将并发请求合并为一次(checker.go);- 进程级共享的
Shared()checker(checker.go),缓存刻意设为全局;
- 失败也缓存:注册表不可达或需要认证的错误会被缓存(
errorTTL = 5 * time.Minute),避免每次页面浏览都重试;但短暂抖动不会让整个成功 TTL 静默; - 缓存上限:
maxCacheEntries = 500,超限时先淘汰过期条目,若全是活条目则整体清空(checker.go); - 缓存只存远端 digest,不存判定结论:因此如果本地拉取了新镜像,容器会立即报告
up-to-date,无需等缓存过期。
一个关键语义:对比的是“正在运行的”镜像
检测对比的对象是容器当前正在运行的镜像 digest。因此即使主机上已经拉取了更新的镜像,只要容器没有重建,它依然会被标记为“过期(out of date)”。也就是说,只有update或手动重建容器才能消除过期状态。
检测与动作是相互独立的
更新检测是独立于动作能力的:即便DOZZLE_ENABLE_ACTIONS关闭,只要检测开启,菜单依然会显示“有可用更新”的提示——知道容器过期本身就有价值;只是此时没有Update按钮,因为Update需要动作权限。
关闭或调整检测:DOZZLE_IMAGE_CHECK_MODE
DOZZLE_IMAGE_CHECK_MODE控制 Dozzle 是否联系 registry,有三种取值:
| 取值 | 行为 |
|---|---|
automatic | 后台检测:当容器被查看时自动检查。 |
manual | 从不自动检查,菜单提供“Check for updates”手动动作。 |
off | 功能完全移除:不注册任何端点,不发起任何请求。 |
该配置在 internal/support/cli/args.go 中定义:
ImageCheckMode string `arg:"--image-check-mode,env:DOZZLE_IMAGE_CHECK_MODE" help:"controls checking container registries for newer images (automatic, manual, off). Defaults to --release-check-mode."`默认值继承自DOZZLE_RELEASE_CHECK_MODE(args.go):如果你已经告诉 Dozzle 不要自动获取 release 信息,那么镜像检测也会默认关闭自动模式。同时启动时会调用imagecheck.ParseMode校验取值,非法值直接报错退出。
off模式的彻底性从路由层即可验证:internal/web/routes.go 中,只有ImageCheckMode != off时才注册检测端点与相关中间件,真正做到“零端点、零出网”。
docker-compose 关闭示例:
services: dozzle: image: amir20/dozzle:latest environment: DOZZLE_IMAGE_CHECK_MODE: off为单个容器静默检测:dev.dozzle.update-check 标签
某些容器是刻意固定版本的(例如固定版本的数据库镜像),此时可以通过标签让其退出检测:
services: database: image: postgres:18-alpine labels: dev.dozzle.update-check: false标签常量定义在 internal/imagecheck/checker.go,Skipped()函数接受false、off、no三种取值(checker.go),检测命中该标签时返回skipped状态。这与 Dozzle 既有的dev.dozzle.*标签约定保持一致。
更新告警通知
当检测到更新时,还可以通过 Dozzle 的通知系统(Settings 下的告警配置)推送提醒。该通知默认关闭,需要在设置中手动启用。通知机制的实现位于 internal/notification 目录,检测结果(update-available)可作为告警事件的触发条件之一。
哪些镜像无法被检测
有些容器根本没有可比对的对象,此时 Dozzle 选择保持沉默而不是猜测。从 checker.go 的状态枚举与Check流程可以梳理出四类情况:
- 本地构建的镜像:没有 registry digest(
RepoDigests为空),标记为not-checkable; - digest 固定的引用:
sha256:...形式按定义不会漂移,标记为pinned; - 私有 registry:Dozzle 自身没有任何凭据,匿名请求被拒绝时标记为
auth-required,不做重试; - Kubernetes:镜像滚动更新属于集群编排职责,Dozzle 不介入(容器动作本身也是 Docker Only 功能)。
更新 Dozzle 自身时的特殊行为
Dozzle 无法“停止自己并原地更新”,因此:
- 单机模式下的 Dozzle 容器:只显示更新提示 + release notes 链接,没有
Update按钮; - 以 Swarm service 运行 Dozzle:更新操作会交给编排器处理,工作正常;
- 其他主机上的 Dozzle agents:它们是普通容器,可以像其他容器一样被更新。
从源码看,agents 目前也不承担动作工具的角色(internal/support/cli/agent_command.go 中EnableActions: false),动作由主实例统一调度到目标 host 执行。
小结
Container Actions 是 Dozzle 从“日志查看器”走向“容器管理面板”的关键能力。启用时记住三个要点:
- 显式开启:
DOZZLE_ENABLE_ACTIONS=true或--enable-actions,并注意认证模式下需要Actions角色; - 警惕重建数据丢失:
remove/update会重建容器,状态数据务必放在命名卷或 bind mounts; - 理解检测语义:镜像检测对比的是“正在运行的镜像”与 registry 当前 digest,6 小时缓存 + HEAD 请求保证零 pull 配额消耗,
DOZZLE_IMAGE_CHECK_MODE可在automatic/manual/off间自由切换,并可借助dev.dozzle.update-check标签为固定版本容器静默。
如果需要深入源码验证上述行为,推荐依次阅读 internal/web/actions.go、internal/imagecheck/checker.go、internal/imagecheck/registry.go 以及 internal/support/cli/args.go,并结合 internal/imagecheck/checker_test.go 中的测试用例理解各状态分支的判定逻辑。
【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考