Skip to content

Refactor offline image management to use skopeo and OCI archives#13312

Open
Kibakus wants to merge 2 commits into
kubernetes-sigs:masterfrom
Kibakus:offline-download
Open

Refactor offline image management to use skopeo and OCI archives#13312
Kibakus wants to merge 2 commits into
kubernetes-sigs:masterfrom
Kibakus:offline-download

Conversation

@Kibakus

@Kibakus Kibakus commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

/kind feature

What this PR does / why we need it:

This PR refactors the offline image and file management scripts to improve portability, reliability, and cross-platform support.

Main changes include:

  • Replace docker/podman/nerdctl image operations with skopeo and OCI archives to preserve image metadata and manifest lists during offline transfers.
  • Add architecture detection (amd64/arm64) and Linux platform selection to improve cross-platform compatibility.
  • Use localhost instead of the system hostname when configuring and accessing the local registry.
  • Improve error handling by enabling set -euo pipefail.
  • Simplify runtime-specific logic in the offline scripts.
  • Simplify nginx.conf and related configuration files used by the offline file server.

The previous offline image workflow relied on docker save and docker load, which do not preserve OCI manifest lists and platform descriptors associated with multi-architecture images. As a result, some images could not be pushed to the destination registry after being transferred through the offline workflow.

Using OCI archives with skopeo preserves manifest lists and platform metadata throughout the transfer process, allowing both single-platform and multi-platform images to be transferred and registered consistently.

In addition, the previous nginx configuration loaded default server definitions from the container image through conf.d and default.d include directives. Depending on the image contents, this could result in multiple server blocks being loaded simultaneously, causing unexpected behavior and configuration conflicts. The configuration has been simplified to ensure that only the intended offline file server configuration is used.

Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

During validation, issues were observed with Kubernetes images distributed as OCI manifest lists.

With the previous implementation, the following scenario could be reproduced:

Loaded image: registry.k8s.io/kube-apiserver:v1.33.11
The push refers to repository [localhost:5000/registry.k8s.io/kube-apiserver]
image with reference localhost:5000/registry.k8s.io/kube-apiserver:v1.33.11 was found but does not provide any platform

The issue occurs because docker save/docker load lose the OCI manifest list information required to determine which platform-specific manifest should be pushed to the destination registry.

The OCI-based workflow implemented in this PR preserves manifest lists and platform metadata, eliminating these failures and providing consistent behavior across supported environments.

The changes were validated using both single-platform images and multi-architecture images represented by OCI manifest lists.

Does this PR introduce a user-facing change?:

Improved offline image and file management workflows by switching image handling to skopeo with OCI archives, preserving multi-architecture image metadata, adding cross-platform architecture support, using localhost for local registry configuration, simplifying nginx configuration, and improving offline runtime reliability.

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. kind/feature Categorizes issue or PR as related to a new feature. labels Jun 16, 2026
@k8s-ci-robot k8s-ci-robot requested review from ErikJiang and mzaian June 16, 2026 23:03
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Kibakus
Once this PR has been reviewed and has the lgtm label, please assign vannten for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jun 16, 2026
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

Hi @Kibakus. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Jun 16, 2026
@Kibakus Kibakus changed the title Refactor offline management scripts to use skopeo and improve portabi… Refactor offline image management to use skopeo and OCI archives Jun 16, 2026
@Kibakus Kibakus force-pushed the offline-download branch from 0fab5ca to 57238cb Compare June 17, 2026 01:20
@Kibakus Kibakus force-pushed the offline-download branch from 57238cb to 6c2763f Compare June 17, 2026 01:30
@yankay

yankay commented Jun 17, 2026

Copy link
Copy Markdown
Member

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jun 17, 2026
@Kibakus

Kibakus commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

@yankay

https://gitlab.com/kargo-ci/kubernetes-sigs-kubespray/-/jobs/14898252406

Uploading cache.zip to http://minio.minio.svc.cluster.local:9000/runner-cache/project/9659330/pre-commit-2-4-protected
FATAL: received: 503 Service Unavailable. Request failed with code: SlowDownWrite, message: Resource requested is unwritable, please reduce your request rate

@Kibakus

Kibakus commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

The failing CI jobs appear unrelated to this change. Waiting for review.

Comment thread contrib/offline/manage-offline-files.sh Outdated
echo " create - download files and nginx image"
echo " serve - start nginx server"
;;
esac No newline at end of file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HI @Kibakus

Thanks for checking. The MinIO 503 happened after the job had already failed. The actual failure is end-of-file-fixer: it added missing trailing newlines to the two offline scripts.

Could you please run pre-commit run --all-files locally and push the fix?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! fixed!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the contrib/offline tooling to improve offline portability and multi-arch image handling by switching image transfer logic to skopeo with OCI artifacts, and simplifying local registry / nginx configuration.

Changes:

  • Switch offline container image collection/registration to skopeo copy with oci-archive to preserve manifest lists and platform metadata.
  • Add basic OS/arch detection and use localhost:5000 for local registry configuration.
  • Harden scripts with set -euo pipefail and simplify nginx configuration by removing default includes.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
contrib/offline/registries.conf Update insecure registry entry to localhost:5000.
contrib/offline/nginx.conf Remove includes that could unintentionally load extra server blocks.
contrib/offline/manage-offline-files.sh Refactor into create/serve modes; add skopeo-based nginx image handling and stricter bash settings.
contrib/offline/manage-offline-container-images.sh Replace runtime pull/save/push flow with skopeo OCI archive workflow; add platform detection and stricter bash settings.
contrib/offline/docker-daemon.json Update insecure registry entry to localhost:5000.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread contrib/offline/manage-offline-container-images.sh
Comment thread contrib/offline/manage-offline-container-images.sh Outdated
Comment thread contrib/offline/manage-offline-container-images.sh Outdated
Comment thread contrib/offline/manage-offline-container-images.sh Outdated
Comment thread contrib/offline/manage-offline-container-images.sh Outdated
Comment thread contrib/offline/manage-offline-files.sh Outdated
Comment thread contrib/offline/manage-offline-files.sh Outdated
Comment thread contrib/offline/manage-offline-files.sh Outdated
Comment thread contrib/offline/manage-offline-files.sh Outdated
Comment thread contrib/offline/manage-offline-container-images.sh Outdated
@linux-foundation-easycla

linux-foundation-easycla Bot commented Jun 22, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

@kubernetes-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Kibakus
Once this PR has been reviewed and has the lgtm label, please assign vannten for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubernetes-prow kubernetes-prow Bot added cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. and removed cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jun 22, 2026
@Kibakus Kibakus force-pushed the offline-download branch from 73983f8 to 3406420 Compare June 22, 2026 08:36
@kubernetes-prow kubernetes-prow Bot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels Jun 22, 2026
@Kibakus

Kibakus commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Hi @yankay @ErikJiang , just a gentle ping on this when you have a chance.

@Kibakus

Kibakus commented Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

Hi @yankay,

I wanted to follow up on this PR. It's been waiting for about a week now.

The Kubespray CI Pipeline check is still showing "Waiting for status to be reported", while the other checks have already completed.

Is there anything I should do on my side to trigger it again, or is this something that needs attention from a maintainer?

@yankay

yankay commented Jul 1, 2026

Copy link
Copy Markdown
Member

HI @Kibakus

Thanks for following up, and thanks for the work on this PR.

I think we should discuss the compatibility impact before moving forward. This PR introduces a fairly large refactor of the offline workflow, and one breaking change is switching the image handling path from the existing container runtimes (docker/podman/nerdctl) to skopeo.

That means users would need to install skopeo themselves, including in offline or restricted environments. In practice, offline installation of skopeo can be difficult because it has many package dependencies, and the upstream podman-container-tools/skopeo project does not provide standalone binary releases. See:

Docker can have similar installation concerns, but it is much more widely used and users generally have more existing experience installing and operating it.

So my main concern is that this is a big user-facing change: target machines may not have skopeo, and requiring it could make offline deployment harder for some users. Could we keep the existing runtime-based path as a fallback, make skopeo optional, or split this refactor into smaller PRs?

Comment thread contrib/offline/manage-offline-files.sh Outdated
TARGET_OS="linux"

echo "[INFO] Downloading nginx:alpine image"
if command -v skopeo &>/dev/null 2>&1; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The nginx:alpine image is downloaded during create , but only used in serve .
For users who only need offline files without running an nginx server, this adds unnecessary download and package bloat.
Suggest moving the nginx image download into serve_offline_files() where it actually belongs.

@Kibakus Kibakus force-pushed the offline-download branch from 4e6ebc3 to 1f8e850 Compare July 1, 2026 08:56
@Kibakus

Kibakus commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@yankay Check pls, good?

@Kibakus

Kibakus commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@yankay
Сan you look please?

1 similar comment
@Kibakus

Kibakus commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

@yankay
Сan you look please?

@ErikJiang

Copy link
Copy Markdown
Member

@Kibakus good job !

/lgtm

@kubernetes-prow kubernetes-prow Bot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 6, 2026
@yankay yankay requested a review from Copilot July 6, 2026 03:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.

Comment thread contrib/offline/manage-offline-skopeo-files.sh
Comment thread contrib/offline/manage-offline-skopeo-files.sh
Comment thread contrib/offline/manage-offline-skopeo-container-images.sh
Comment thread contrib/offline/manage-offline-skopeo-container-images.sh Outdated
Comment thread contrib/offline/manage-offline-skopeo-container-images.sh
Comment thread contrib/offline/docker-daemon.json Outdated
Comment thread contrib/offline/registries.conf
Comment thread contrib/offline/README.md Outdated
Comment thread contrib/offline/README.md
@Kibakus

Kibakus commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Quick question: do you think I should go through the new Copilot recommendations? As far as I remember, I already addressed the earlier comments, and the PR has already received /lgtm. Or do you think it's worth reviewing the latest suggestions as well?

@Kibakus

Kibakus commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@ErikJiang, @yankay
Сan you look please?

@yankay

yankay commented Jul 8, 2026

Copy link
Copy Markdown
Member

HI @Kibakus
Thanks for the follow-up. I think this still needs a few fixes before approval.

The main blocker is that the shared registry config change can break the existing offline workflow. docker-daemon.json and registries.conf now hard-code localhost:5000, but the existing manage-offline-container-images.sh register still defaults DESTINATION_REGISTRY to $(hostname):5000 and previously relied on replacing the HOSTNAME placeholder. With this PR, Docker/Podman can be configured for localhost:5000 while the push still goes to $(hostname):5000.

There is also an offline correctness issue with the skopeo-created docker archives. Both registry:latest and nginx:alpine are copied as docker-archive:<path> without an explicit reference. That can load as an untagged image, so runtime run registry:latest or runtime run nginx:alpine may try to pull from the network and fail in an offline environment. Please include the reference in the archive destination, for example docker-archive:${IMAGE_DIR}/registry-latest.tar:registry:latest and docker-archive:${NGINX_IMAGE_ARCHIVE}:nginx:alpine.

A few smaller things:

  • The PR says it preserves multi-architecture metadata, but the default path uses --override-os/--override-arch, so it saves one platform unless the first copy fails.
  • The README still shows manage-offline-files.sh create, but that command does not exist in the legacy script.
  • The new skopeo container-image script requires docker/podman/nerdctl even for create or pushing to an existing registry, where skopeo alone should be enough.

Once the two blockers are fixed, this should be much closer.

I also think the High and Medium findings from the Copilot review are worth checking before approval, especially the legacy workflow regression and the README/script behavior mismatch.

@Kibakus Kibakus force-pushed the offline-download branch from 1f8e850 to 85e010d Compare July 8, 2026 16:06
@kubernetes-prow kubernetes-prow Bot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 8, 2026
@kubernetes-prow

Copy link
Copy Markdown
Contributor

New changes are detected. LGTM label has been removed.

…lity

- Replace docker/podman/nerdctl with skopeo for image operations (OCI format)
- Add architecture detection (amd64/arm64) for cross-platform support
- Use localhost instead of hostname for local registry
- Improve error handling with set -euo pipefail
- Simplify nginx.conf and config files
- Introduce optional nginx download via DOWNLOAD_NGINX variable

Add new scripts:
- manage-offline-skopeo-files.sh: alternative to manage-offline-files.sh using skopeo
- manage-offline-skopeo-container-images.sh: alternative to manage-offline-container-images.sh
@Kibakus Kibakus force-pushed the offline-download branch from 85e010d to 10aa707 Compare July 8, 2026 17:21
@Kibakus

Kibakus commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@yankay
Thanks for the detailed review! I've addressed all of the issues you pointed out.

Blocker 1 - Legacy workflow regression (docker-daemon.json, registries.conf)

The shared config files were previously hard-coded to localhost:5000, which broke the existing manage-offline-container-images.sh register workflow because it relies on:

sed -i "s@HOSTNAME@$(hostname)@"

to substitute the actual hostname.

Both configuration files now include HOSTNAME:5000 as a placeholder (alongside localhost:5000), so the legacy register workflow continues to inject the real hostname while Docker/Podman still treats localhost:5000 as an insecure registry.

For consistency, the new skopeo-based register workflow now performs the same HOSTNAME$(hostname) replacement.

Blocker 2 - Untagged Docker archives

The Docker archive destinations now include explicit image references, ensuring images are loaded with the correct tags instead of becoming untagged:

  • manage-offline-skopeo-container-images.shdocker-archive:...:registry:latest
  • manage-offline-skopeo-files.shdocker-archive:...:nginx:alpine

Additional fixes

  • Removed the invalid manage-offline-files.sh create example from the README (the legacy script does not provide a create subcommand).
  • manage-offline-skopeo-container-images.sh no longer requires docker/podman/nerdctl during startup. Runtime detection is now only performed in the register path where it is actually required, so create works with skopeo alone.

The changes are included in:

  • contrib/offline/docker-daemon.json
  • contrib/offline/registries.conf
  • contrib/offline/manage-offline-skopeo-container-images.sh
  • contrib/offline/manage-offline-skopeo-files.sh
  • contrib/offline/README.md

Please let me know if you see anything else that should be adjusted. Thanks again for the review!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Comment thread contrib/offline/manage-offline-skopeo-container-images.sh Outdated
Comment thread contrib/offline/manage-offline-skopeo-container-images.sh
Comment thread contrib/offline/manage-offline-skopeo-files.sh
Comment thread contrib/offline/manage-offline-skopeo-files.sh
Comment thread contrib/offline/README.md Outdated
Comment thread contrib/offline/README.md Outdated
…rify, wget layout, container state

- Fix registry container port mapping (host:5000) in skopeo container images script
- Add SRC_TLS_VERIFY env var for skopeo TLS verification control
- Fix wget -nH to -x in skopeo files script to preserve URL directory structure
- Fail on wget download errors instead of masking them
- Fix container state check in serve subcommand (detect stopped containers)
- Update README to reflect optional skopeo requirement and separate create/serve steps
@Kibakus

Kibakus commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@yankay
Сan you look please?

2 similar comments
@Kibakus

Kibakus commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@yankay
Сan you look please?

@Kibakus

Kibakus commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@yankay
Сan you look please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants