Skip to content
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

fix: use temporary dir for package manager files #6

Merged
merged 12 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/__pycache__/
209 changes: 153 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@ There are two supported modes at the moment, a "base" type layer and an "ansible

# Running

The recommended way to run `image-build` is through the container as it avoids any Python dependency troubles.
The recommended and official way to run `image-build` is using the `ghcr.io/openchami/image-build` container (specifically using [Podman](https://podman.io)) as it avoids Python versioning/dependency troubles. Running bare-metal is not officially supported, though it is possible to do at one's own risk. Using Docker has caused issues and is not officially supported, though it is probably possible (again, at one's own risk) with some tweaking.

## Container

The supported way for running the container is via [Podman](https://podman.io/).
To build an image using the container, the config file needs to be mapped into the container, as well as the FUSE filesystem device:

```
Expand All @@ -21,28 +18,13 @@ podman run \
image-build --config config.yaml
```

If the config.yaml pushes to S3, specify the credentials by adding `-e S3_ACCESS=<s3-user>` and `-e S3_SECRET=<s3-password>` to the command above.

## Bare Metal

> [!WARNING]
> Python >= 3.7 is required!

Install the Python package dependencies:
```
pip install -r requirements.txt
```

Run the tool:
```
image-build --config /path/to/config.yaml
```
If the config.yaml pushes to S3, specify the credentials by adding `-e S3_ACCESS=<s3-user>` and `-e S3_SECRET=<s3-password>` to the command above. See [S3](#s3) below.

# Building Container

From the root of the repository:
```
buildah bud -t ghcr.io/openchami/image-buildi:latest -f src/dockerfiles/Dockerfile .
buildah bud -t ghcr.io/openchami/image-build:latest -f src/dockerfiles/Dockerfile .
```

# Configuration
Expand All @@ -51,73 +33,188 @@ buildah bud -t ghcr.io/openchami/image-buildi:latest -f src/dockerfiles/Dockerfi

The premise here is very simple. The `image-build` tool builds a base layer by starting a container, then using the provided package manager to install repos and packages. There is limited support for running basic commands inside the container. These settings are provided in a config file and command line options

An example config file:
```
An example config file that builds a base OS image based on Rocky 8.10:

```yaml
# Example image-build config for a base-type image.

# Global image-build options for this image
options:
# Build a "normal" layer (as opposed to an Ansible-type layer)
layer_type: 'base'

# Name and tag for this image, used in publishing to OCI registries
# and S3 for identification.
name: 'rocky-base'
# One or more tags to publish image with. If one, value is a string.
# If multiple, the value is a YAML array of strings.
publish_tags: '8.10'

# Distribution flavor of image.
pkg_manager: 'dnf'

# Starting filesystem of image. 'scratch' means to start with a blank
# filesystem. Currently, only OCI images can be used as parents. In
# this example, the image is pushed to:
#
# registry.mysite.tld/openchami/rocky-base:8.10
#
# This value can be used as the value to 'parent' if one wished to use
# the 'rocky-base:8.10' image as a parent.
parent: 'scratch'

# Publish OCI image to local podman registry. Note that if running
# the image-build container, this option will not be a benefit if
# the container is removed after running, since the container gets
# deleted after the build process exits.
#publish_local: true

# Publish OCI image to container registry. This image can be used
# as a parent for child images. Use this when this image should
# be used as a parent for subsequent images.
#
# The below config, combined with 'name' and 'publish_tags', will
# publish this OCI image to:
#
# registry.mysite.tld/openchami/rocky-base:8.10
#
publish_registry: 'registry.mysite.tld/openchami'
registry_opts_push:
- '--tls-verify=false'

# Publish to S3 instance. This image be used for booting. Use this
# if an image is to be used for booting.
#
# The below config, combined with 'name' and 'publish_tags', will
# publish this SquashFS image to:
#
# http://s3.mysite.tld/boot-images/compute/base/rocky8.10-rocky-base-8.10
#
publish_s3: 'http://s3.mysite.tld'
s3_prefix: 'compute/base/'
s3_bucket: 'boot-images'

# Package repositories to add. This example uses YUM/DNF repositories.
repos:
- alias: 'Rock_BaseOS'
url: 'http://<repo_server>/repo/pub/rocky/8/BaseOS/x86_64/os'
- alias: 'Rock_AppStream'
url: 'http://<repo_server>/repo/pub/rocky/8/AppStream/x86_64/os'
- alias: 'Rock_PowerTools'
url: 'http://<repo_server>/repo/pub/rocky/8/PowerTools/x86_64/os'
- alias: 'Epel'
url: 'http://<repo_server>/repo/pub/rocky/epel/8/Everything/x86_64/'

- alias: 'rocky-baseos'
url: 'http://dl.rockylinux.org/pub/rocky/8/BaseOS/x86_64/os'
- alias: 'rock_appstream'
url: 'http://dl.rockylinux.org/pub/rocky/8/AppStream/x86_64/os'
- alias: 'rock_powertools'
url: 'http://dl.rockylinux.org/pub/rocky/8/PowerTools/x86_64/os'
- alias: 'epel'
url: 'http://dl.fedoraproject.org/pub/epel/8/Everything/x86_64/'

# Package groups to install, in this example YUM/DNF package groups.
package_groups:
- 'Minimal Install'
- 'Development Tools'

# List of packages to install after repos get added. These names get passed
# straight to the package manager.
packages:
- kernel
- wget

# List of commands to run after package management steps get run. Each
# command gets passed to the shell, so redirection can be used. Besides
# 'cmd', an optional 'loglevel` can be passed (e.g. 'INFO', 'DEBUG') to
# control command verbosity. By default, it is 'INFO'.
cmds:
- 'echo hello'
- cmd: 'echo hello'
```

Then you can use this config file to build an "base" layer:
Then you can use this config file to build a "base" layer (make sure the `S3_ACCESS` and `S3_SECRET` environment variables are set to the S3 credentials if being used):

```
image-build --name base-os \
--config base.yaml \
--pkg-manager dnf \
--parent scratch \
--publish-tags 8.8 \
--layer-type base
podman run \
--rm \
--device /dev/fuse \
-v /path/to/config.yaml:/home/builder/config.yaml:Z \
-e "S3_ACCESS=${S3_ACCESS}" \
-e "S3_SECRET=${S3_SECRET}" \
ghcr.io/openchami/image-build \
image-build --config config.yaml --log-level DEBUG
```

You can then build on top of this base os with a new config file, just point the `--parent` flag at the base os container image
See [Publishing Images](#publishing-images) below for more explanation on how `image-build` publishes images.

You can then build on top of this base os with a new config file, just point the `parent` key at the base os container image, in the above example, `registry.mysite.tld/openchami/rocky-base:8.10`.


## Ansible Type Layer

You can also run an ansible playbook against a buildah container. This type using the Buildah connection plugin in ansible to treat the container as a host.
```
image-build \
--name ansible-layer \
--parent base-os \
--groups compute \
--pb playbook.yaml \
--inventory my_inventory/ \
--publish-tags v1 \
--layer-type ansible
You can also run an Ansible playbook against a buildah container. This type of layer uses the Buildah connection plugin in Ansible to treat the container as a host.

Configuration for an Ansible-type layer is largely the same as a base-type layer configuration with a few differences.

```yaml
# An Ansible-type layer only needs the global options block.
options:
# Layer type us 'ansible' instead of 'base'
layer_type: 'ansible'

# Ansible-specific options.
#
# 'groups' defines the Ansible groups in the passed inventory to run the
# playbook(s) on.
groups:
- 'img_ochami_compute'
- 'img_ochami'
#
# The playbook(s) to run against the image.
playbooks: 'playbooks/images/compute.yaml'
#
# The Ansible inventory to pass corresponding with the playbook(s).
inventory: 'inventory/'

# Everything else is the same format as base layer.
name: 'ansible-layer'
publish_tags: '8.10'
parent: 'registry.mysite.tld/openchami/rocky-base:8.10'
publish_registry: 'registry.mysite.tld/openchami'
registry_opts_push:
- '--tls-verify=false'
publish_s3: 'http://s3.mysite.tld'
s3_prefix: 'compute/ansible/'
s3_bucket: 'boot-images'
```

This requires the parent to be setup to run ansible tasks
Build the image with:

```
podman run \
--rm \
--device /dev/fuse \
-v /path/to/config.yaml:/home/builder/config.yaml:Z \
-v /path/to/ansible/inventory/:/home/builder/inventory/:Z \
-v /path/to/ansible/playbooks/:/home/builder/playbooks/:Z \
-e "S3_ACCESS=${S3_ACCESS}" \
-e "S3_SECRET=${S3_SECRET}" \
ghcr.io/openchami/image-build \
image-build --config config.yaml --log-level DEBUG
```

> [!NOTE]
> In order to be able to use Ansible on the image, the parent must be set up to
> use Ansible (e.g. Ansible must be installed, etc.).

# Publishing Images

The `image-build` tool can publish the image layers to a few kinds of endpoints

## S3

using the `--publish-s3 <URL>` option will push to an s3 endpoint defined in an ENV variable: `S3_URL`.
You can also set the access and secret values with `S3_ACCESS` and `S3_SECRET` respectively
Using the `--publish-s3 <URL>` flag or `publish-s3` config key will push to an S3 endpoint.

Credentials for S3 can be set via environment variables. Use `S3_ACCESS` for the username and `S3_SECRET` for the password.

## Registry

Using the `--publish-registry <URL>` option will push to a docker registry defined in an ENV variable: `REGISTRY_EP`. You can point to a certs directory by setting `REGISTRY_CERTS_DIR`.
Using the `--publish-registry <URL>` flag or `publish-registry` config key will push to the passed registry base URL (not including image tag). Use `--registry-opts-push`/`registry-opts-push` to specify flags/args to pass to the `buildah push` command to push.

There is an equivalent flag/config option `--registry-opts-pull`/`registry-opts-pull` whose value is passed to the `buildah push` command to pull the parent OCI image.

## Local

Using the `--publish-local` option will squash the layer and copy it to a destination defined in `--publish-dest`.
Using the `--publish-local` flag or `publish-local` config key will push the resulting OCI image to the local podman registry using `buildah commit`.
9 changes: 3 additions & 6 deletions dockerfiles/dnf/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM almalinux:8.8
FROM docker.io/library/almalinux:8.8

RUN dnf clean all && \
dnf update --nogpgcheck -y && \
Expand All @@ -15,15 +15,12 @@ RUN dnf install -y \
squashfs-tools \
fuse-overlayfs

RUN pip3.11 install ansible ansible-base ansible-bender boto3 dnspython requests jinja2_ansible_filters
COPY requirements.txt /
RUN pip3.11 install -r /requirements.txt

COPY src/ /usr/local/bin/
RUN chmod -R 0755 /usr/local/bin/

RUN mkdir -p /tmp/dnf/log && \
mkdir /tmp/dnf/cache && \
mkdir /tmp/dnf/repos.d

# Allow non-root to run buildah commands
RUN setcap cap_setuid=ep "$(command -v newuidmap)" && \
setcap cap_setgid=ep "$(command -v newgidmap)" &&\
Expand Down
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
ansible
ansible-base
ansible-bender
boto3
dnspython
jinja2_ansible_filters
PyYAML
requests
Empty file modified src/arguments.py
100755 → 100644
Empty file.
Empty file modified src/image_config.py
100755 → 100644
Empty file.
Loading