-
Notifications
You must be signed in to change notification settings - Fork 33
Add Podman quadlet .image file support #501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -52,23 +52,173 @@ IOP (Insights Operating Platform) deploys on-premise Insights services for advis | |||||||||||
|
|
||||||||||||
| See [IOP Architecture](iop.md) for details on the services deployed and configuration options. | ||||||||||||
|
|
||||||||||||
| ### Authenticated Registry Handling | ||||||||||||
| ### Image Management | ||||||||||||
|
|
||||||||||||
| If you need to pull images from private or authenticated container registries, you can configure registry authentication using Podman's auth file. | ||||||||||||
| foremanctl uses Podman quadlet [`.image` units](https://docs.podman.io/en/latest/markdown/podman-image.unit.5.html) to separate image sourcing from container definitions. Each unique container image (foreman, candlepin, pulp, etc.) gets a corresponding `.image` file deployed to `/etc/containers/systemd/`. Container roles reference these by name rather than by full image URL: | ||||||||||||
|
|
||||||||||||
| #### Setting up Registry Authentication | ||||||||||||
| See the [podman-systemd.unit(5)](https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html) reference for the full quadlet unit format. | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /etc/containers/systemd/foreman.image | ||||||||||||
| [Image] | ||||||||||||
| Image=quay.io/foreman/foreman:nightly | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /etc/containers/systemd/foreman.container (excerpt) | ||||||||||||
| [Container] | ||||||||||||
| Image=foreman.image | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| All containers that share a base image (e.g., foreman, dynflow-sidekiq, foreman-recurring) reference the same `.image` unit. systemd ensures the image is pulled before any dependent container starts. | ||||||||||||
|
|
||||||||||||
| #### Image Overrides via Drop-ins | ||||||||||||
|
|
||||||||||||
| foremanctl uses quadlet's native drop-in mechanism for image overrides. Each `.image` file has a corresponding `.image.d/` directory. Drop-in `.conf` files placed there are merged on top of the base in lexicographic order — last wins. | ||||||||||||
|
|
||||||||||||
| The quadlet generator reads from two directory tiers, with `/etc/` taking precedence over `/usr/share/`: | ||||||||||||
|
|
||||||||||||
| ``` | ||||||||||||
| /usr/share/containers/systemd/ | ||||||||||||
| foreman.image.d/ | ||||||||||||
| 10-product.conf # vendor/RPM layer | ||||||||||||
| 20-archive.conf # local media layer | ||||||||||||
|
|
||||||||||||
| /etc/containers/systemd/ | ||||||||||||
| foreman.image # base, always generated by foremanctl | ||||||||||||
| foreman.image.d/ | ||||||||||||
| 90-user.conf # user override layer | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| Precedence (last wins): | ||||||||||||
|
|
||||||||||||
| 1. `foreman.image` — foremanctl default from `images.yml` | ||||||||||||
| 2. `10-product.conf` — vendor/RPM provided | ||||||||||||
| 3. `20-archive.conf` — local media provided | ||||||||||||
| 4. `90-user.conf` — user provided (highest priority) | ||||||||||||
|
|
||||||||||||
| #### registries.conf vs .image.d drop-ins | ||||||||||||
|
|
||||||||||||
| Both `registries.conf` and `.image.d` drop-ins can redirect where an image is pulled from, but they behave differently and suit different use cases. | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documenting both use cases is excellent. |
||||||||||||
|
|
||||||||||||
| `registries.conf` applies a transparent redirect at pull time — the image is fetched from the `location` registry but stored in local storage under the original `prefix` name. This means `podman images` shows the upstream name (e.g., `quay.io/foreman/foreman:nightly`), and the `.image` quadlet continues to reference that same name. This works well when the private registry mirrors upstream image names and tags exactly. | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Link to https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md as the official docs? |
||||||||||||
|
|
||||||||||||
| `.image.d` drop-ins directly replace the `Image=` value in the quadlet unit. The image is pulled from and stored under the new reference. This is required when the image name or tag changes completely (e.g., `quay.io/foreman/foreman:nightly` → `registry.example.com/org/foreman-rhel9:stream`), since `registries.conf` cannot remap image names — only registry/namespace locations. | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this point to https://docs.podman.io/en/latest/markdown/podman-image.unit.5.html? |
||||||||||||
|
|
||||||||||||
| #### Use Cases | ||||||||||||
|
|
||||||||||||
| ##### Upstream default (no user action) | ||||||||||||
|
|
||||||||||||
| foremanctl generates `.image` files from its built-in `images.yml`: | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /etc/containers/systemd/foreman.image (generated by foremanctl) | ||||||||||||
| [Image] | ||||||||||||
| Image=quay.io/foreman/foreman:nightly | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ##### RPM-provided images | ||||||||||||
|
|
||||||||||||
| When a product uses different image names or tags from the upstream references (e.g., `foreman-rhel9:stream` instead of `foreman:nightly`), the RPM ships `.image.d` drop-ins to override each image unit directly: | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /usr/share/containers/systemd/foreman.image.d/10-product.conf (from RPM) | ||||||||||||
| [Image] | ||||||||||||
| Image=registry.example.com/org/foreman-rhel9:stream | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /usr/share/containers/systemd/candlepin.image.d/10-product.conf (from RPM) | ||||||||||||
| [Image] | ||||||||||||
| Image=registry.example.com/org/candlepin-rhel9:stream | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| No user action required beyond installing the RPM and logging into the product registry: | ||||||||||||
|
|
||||||||||||
| 1. **Login to your registry** using Podman and save credentials to the default auth file location: | ||||||||||||
| ```bash | ||||||||||||
| podman login <registry> --authfile=/etc/foreman/registry-auth.json | ||||||||||||
| podman login --authfile=/etc/foreman/registry-auth.json registry.example.com | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ##### Disconnected install from local media | ||||||||||||
|
|
||||||||||||
| In air-gapped environments, container images must be brought in without network access. `registries.conf` cannot express non-registry sources, but Podman can read images from local archive files. Images can be transported via USB or other local media using `podman save` / `podman load`, then referenced via drop-ins: | ||||||||||||
|
|
||||||||||||
| See [podman-export(1)](https://docs.podman.io/en/latest/markdown/podman-export.1.html) for producing archive files. | ||||||||||||
|
Comment on lines
+144
to
+146
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use the example below you don't need
Suggested change
|
||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /usr/share/containers/systemd/foreman.image.d/20-archive.conf (from local media) | ||||||||||||
| [Image] | ||||||||||||
| Image=docker-archive:/opt/foreman/images/foreman-6.17.tar | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ##### User's own registry | ||||||||||||
|
|
||||||||||||
| When the private registry mirrors upstream image names and tags exactly, `registries.conf.d` handles namespace-level remapping. foremanctl images span two upstream namespaces, so two entries are needed at minimum. See [containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md) for the full format. | ||||||||||||
|
|
||||||||||||
| ```toml | ||||||||||||
| # /etc/containers/registries.conf.d/50-foremanctl-mirror.conf | ||||||||||||
| [[registry]] | ||||||||||||
| prefix = "quay.io/foreman" | ||||||||||||
| location = "katello.example.com/Default_Organization" | ||||||||||||
|
|
||||||||||||
| [[registry]] | ||||||||||||
| prefix = "quay.io/sclorg" | ||||||||||||
| location = "katello.example.com/Default_Organization" | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| When the private registry uses different image names or tags, use `.image.d` drop-ins instead: | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /etc/containers/systemd/foreman.image.d/90-user.conf | ||||||||||||
| [Image] | ||||||||||||
| Image=katello.example.com/Default_Organization/foreman-rhel9:stream | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| ##### Developer testing a container build | ||||||||||||
|
|
||||||||||||
| An `.image.d` drop-in overrides a single image without affecting others: | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /etc/containers/systemd/foreman.image.d/90-user.conf | ||||||||||||
| # To test https://github.com/theforeman/foreman-oci-images/pull/12345 | ||||||||||||
| [Image] | ||||||||||||
| Image=quay.io/foreman/stage/foreman:pr-12345 | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| 2. **Deploy as usual** - foremanctl will automatically detect and use the authentication file: | ||||||||||||
| #### Authenticated Registry Handling | ||||||||||||
|
|
||||||||||||
| foremanctl uses `/etc/foreman/registry-auth.json` as the default credential store. When pulling images from an authenticated registry, log in using that file: | ||||||||||||
|
|
||||||||||||
| ```bash | ||||||||||||
| ./foremanctl deploy | ||||||||||||
| podman login --authfile=/etc/foreman/registry-auth.json <registry> | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| Credentials must be for the registry the image is **physically pulled from**. When using `registries.conf` redirects, that is the `location` registry. When using `.image.d` drop-ins, that is the registry in the `Image=` value. | ||||||||||||
|
|
||||||||||||
| foremanctl sets `REGISTRY_AUTH_FILE` in the `[Service]` section of each generated `.image` file. Quadlet propagates this setting to the generated `*-image.service`, so podman uses the auth file whenever the image service runs — including during `pull-images`: | ||||||||||||
|
|
||||||||||||
| ```ini | ||||||||||||
| # /etc/containers/systemd/foreman.image (generated by foremanctl, excerpt) | ||||||||||||
| [Service] | ||||||||||||
| Environment=REGISTRY_AUTH_FILE=/etc/foreman/registry-auth.json | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| This approach integrates seamlessly with both the happy path and advanced deployment paths described above. The authentication is handled transparently during image pulling operations. | ||||||||||||
| If the auth file does not exist (unauthenticated registry), podman ignores it gracefully — no error is raised. | ||||||||||||
|
|
||||||||||||
| #### Image Pulling (pull-images) | ||||||||||||
|
|
||||||||||||
| The `foremanctl pull-images` command is an optional pre-deployment step that pulls all container images before running `foremanctl deploy`. This reduces deploy time and allows pre-staging images separately from deployment. | ||||||||||||
|
|
||||||||||||
| `pull-images` deploys the `.image` unit files (making them available for quadlet to merge with any existing drop-ins from installed RPMs), then starts each `*-image.service` to perform the actual pull. To ensure mutable tags (such as `nightly`, `latest`, or `stream`) are always refreshed, `pull-images` temporarily creates a `Policy=always` drop-in before starting each service and removes it afterward, restoring `Policy=missing` for normal operation. See the [`Policy` field in `podman-image.unit(5)`](https://docs.podman.io/en/latest/markdown/podman-image.unit.5.html) for the full list of pull policies. | ||||||||||||
|
|
||||||||||||
| ``` | ||||||||||||
| /etc/containers/systemd/ | ||||||||||||
| foreman.image.d/ | ||||||||||||
| 00-pull-always.conf # created by pull-images, removed after pull | ||||||||||||
| 90-user.conf # permanent user override (if present) | ||||||||||||
| ``` | ||||||||||||
|
|
||||||||||||
| Because the pull goes through the image services, any `.image.d` drop-ins already in place (e.g., from a product RPM) are respected — the image is pulled from whatever source the merged configuration specifies. | ||||||||||||
|
|
||||||||||||
| ## Deployer Stages | ||||||||||||
|
|
||||||||||||
|
|
@@ -81,7 +231,7 @@ Some of the stages will be made available to the user to run independently. | |||||||||||
| a. system requirements | ||||||||||||
| b. tuning requirements | ||||||||||||
| c. certificate requirements | ||||||||||||
| 4. Place `.container` files | ||||||||||||
| 4. Place `.image` and `.container` files | ||||||||||||
| 5. Create podman secrets | ||||||||||||
| 6. Reload systemd | ||||||||||||
| 7. (re)start services | ||||||||||||
|
|
@@ -103,7 +253,9 @@ When the user provides parameters to alter the deployment, the deployment utilit | |||||||||||
|
|
||||||||||||
| ## Container changes (Upgrades) | ||||||||||||
|
|
||||||||||||
| When the running containers change because the stream was changed in the configuration, the deployment utility will pull the new images and use the new images when starting services. | ||||||||||||
| When the running containers change because the stream was changed in the configuration, the deployment utility regenerates `.image` units with the new image references and restarts services to pull and use the updated images. | ||||||||||||
|
|
||||||||||||
| User drop-in overrides in `.image.d/90-user.conf` take precedence over the base `.image` values — if a user-provided drop-in pins a specific tag, it will not be changed by an upgrade. | ||||||||||||
|
|
||||||||||||
| As there is currently no way for the deployment utility to verify which image version is used by a running service, the user is advised to stop all services before performing an upgrade. | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| - name: Deploy candlepin image unit | ||
| ansible.builtin.include_role: | ||
| name: images | ||
| tasks_from: deploy_image.yaml | ||
| vars: | ||
| images_definition: | ||
| name: candlepin | ||
| image: "{{ candlepin_container_image }}:{{ candlepin_container_tag }}" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| - name: Deploy foreman image unit | ||
| ansible.builtin.include_role: | ||
| name: images | ||
| tasks_from: deploy_image.yaml | ||
| vars: | ||
| images_definition: | ||
| name: foreman | ||
| image: "{{ foreman_container_image }}:{{ foreman_container_tag }}" |
Uh oh!
There was an error while loading. Please reload this page.