feat: keep Docker images on computes consistent with the controller host

Only relying on the image name lets a moved tag (e.g. a newer :latest)
silently serve stale content from a compute that already has an image
under the same name. When creating a Docker node, the controller now
pins the image id (Id from the Docker daemon on the controller host)
into the create payload. A compute holding a different image under the
same tag reports the image as missing, which routes it through the
image sync added by the previous commit and re-aligns the tag.

No new template fields or database changes: the controller host daemon
remains the source of truth and the pin is resolved per creation. When
the image is not available on the controller host the pin is omitted
and behavior is unchanged (the compute pulls from the repository).
This commit is contained in:
YueGuobin committed 2026-09-14 00:55:18 +08:00
1 parent c477812332
commit be62e8c022
6 files changed
+101 -1

No files matched your search

+15
View File
@@ -122,6 +122,7 @@ class DockerVM(BaseNode):
extra_configs=None,
memory=0,
cpus=0,
image_digest=None,
):
if not is_rfc1123_hostname_valid(name):
@@ -135,6 +136,10 @@ class DockerVM(BaseNode):
if ":" not in image:
image = f"{image}:latest"
self._image = image
# image id expected by the controller (set when the image is available on
# the controller host): a different local image under the same tag is
# treated as missing so the controller re-syncs the expected version
self._image_digest = image_digest
# assign through the property setters so creation and updates apply
# the same value normalization (e.g. "" -> None)
self.start_command = start_command
@@ -583,6 +588,16 @@ class DockerVM(BaseNode):
if image_infos is None:
raise DockerError(f"Cannot get information for image '{self._image}', please try again.")
if self._image_digest and image_infos.get("Id") != self._image_digest:
# the tag exists but points to a different image (e.g. a moved :latest):
# report it as missing so the controller re-syncs the expected version
local_id = image_infos.get("Id")
log.info(
f"Image '{self._image}' version mismatch on this compute: "
f"local id '{local_id}' != expected '{self._image_digest}'"
)
raise ImageMissingError(self._image)
available_cpus = psutil.cpu_count(logical=True)
if self._cpus > available_cpus:
raise DockerError(