19 Commits

Author SHA1 Message Date
Chris Evich
ae40196724 Pre-create podman storage root
Suspected cause of ownership problems when mounting a volume at this
location.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-19 07:33:57 -05:00
Chris Evich
9cda05620b Merge branch 'docs' into 'main'
Support externally maintained DNF cache

See merge request qontainers/pipglr!5
2022-11-19 10:52:12 +00:00
Chris Evich
cbddc54007 Resolve TODO, update volume names, update docs.
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-18 21:56:24 -05:00
Chris Evich
0e51bfdf8e Support externally maintained DNF cache
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-18 21:50:34 -05:00
Chris Evich
e5dfadbd4c Add wrapper error on podman start failure
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-18 09:04:05 -05:00
Chris Evich
b4dd3c667c Fix image tagging (again)
This CI environment is using a busybox `sh` so doesn't support all the
advanced features of bash.  Reimplement IMAGE_TAG processing so it
functions as intended.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 10:53:52 -05:00
Chris Evich
e214a0549c Merge branch 'bugfix' into 'main'
Fix CI/CD typo and other misc items

See merge request qontainers/pipglr!4
2022-11-16 15:07:01 +00:00
Chris Evich
04b61422a9 Be helpful to CI/CD job maintainers
It's often hard to debug/maintain/improve a job when you can't observe
any of the (many!) auto-generated CI env. vars.  Print them all out on
every job.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 10:01:51 -05:00
Chris Evich
341fbb8030 Fix kaniko command-line
For whatever reason, line-breaks must be used regardless of
string-block marker `|` or `>-`.  Fix this.

Also, support a fork/pull/MR model allowing contributors to run
pipelines on their fork w/ push to their registry.  In this case, images
should be tagged by MR number to be helpful.

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 09:46:19 -05:00
Chris Evich
64ce093a87 Ignore local pre-commit configuration files
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 09:23:17 -05:00
Chris Evich
5daaa407c8 Tweak some tag defaults
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 09:23:17 -05:00
Chris Evich
af48308a15 Merge branch 'set_name' into 'main'
Add runner-name build-arg

See merge request qontainers/pipglr!3
2022-11-16 14:05:06 +00:00
Chris Evich
98a0c4c9ab Merge branch 'upd_img_tag' into 'main'
Update image tagging w/ `mr` prefix for MRs

See merge request qontainers/pipglr!2
2022-11-16 14:04:45 +00:00
Chris Evich
9c95cf5074 Add runner-name build-arg
Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-16 08:53:48 -05:00
Chris Evich
952ff81c4f Update image tagging w/ mr prefix for MRs
Prevent any possiblilty of confusion for tags (prefixed with a `v`) vs MR tags
(previously no prefix).

Signed-off-by: Chris Evich <chris_gitlab@icuc.me>
2022-11-13 18:45:10 -05:00
Chris Evich
8066716b67 Add background cleanup process
When given the "run" argument, in addition to launching `podman system
service` in the background, also start a small periodic maintenance
script.  It's only job is to clean up stale images, containers, and
volumes from old jobs.  Currently hard-coded to trigger every 2 days,
this could be tweaked via build-args or env. var.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 14:19:09 -05:00
Chris Evich
ba2dc82ac8 Fix disused session-port var in run label
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 12:07:06 -05:00
Chris Evich
b9e5e066c8 Update documentation
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 11:53:56 -05:00
Chris Evich
125d7cb9ac Tag images based on build context
For tag-pipelines, tag image with the repo. tag

For MR's, tag the image with the MR number

For Branches, use the branch name except for `main` use `latest`.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-10 11:47:20 -05:00
6 changed files with 165 additions and 60 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/.pre-commit-config.yaml

View File

@@ -15,12 +15,26 @@ build:
BASE_TAG: latest
FLAVOR: stable
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- >-
- 'mkdir -p /kaniko/.docker'
- 'echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json'
- |
echo "Select CI env. vars.:";
printenv | egrep '^CI_' | sort
# N/B: There could be more than one merge-request open with this branch's HEAD
- |
IMAGE_TAG="${CI_COMMIT_BRANCH}";
if [[ -n "$CI_COMMIT_TAG" ]]; then
IMAGE_TAG="${CI_COMMIT_TAG}";
elif [[ -n "$CI_OPEN_MERGE_REQUESTS" ]]; then
IMAGE_TAG=mr$(echo "${CI_OPEN_MERGE_REQUESTS}" | cut -d, -f -1 | cut -d\! -f 2);
elif [[ "$CI_COMMIT_BRANCH" == "main" ]]; then
IMAGE_TAG="latest";
fi
echo "Building/Pushing to: ${CI_REGISTRY_IMAGE}:${IMAGE_TAG}";
- |
/kaniko/executor \
--context $CI_PROJECT_DIR \
--dockerfile $CI_PROJECT_DIR/Containerfile \
--destination "$CI_REGISTRY_IMAGE:${CI_COMMIT_TAG:-latest}" \
--destination "${CI_REGISTRY_IMAGE}:${IMAGE_TAG}" \
--build-arg "BASE_TAG=$BASE_TAG" \
--build-arg "FLAVOR=$FLAVOR"

View File

@@ -37,16 +37,16 @@ ARG EXCLUDE_PACKAGES="\
# Base-image runs as user 'podman', temporarily switch to root
# for installation/setup.
USER root
# Not a real build-arg. Avoiding addition of an env. layer
# only to help prevent some extra typing.
ARG dnfcmd="dnf --setopt=tsflags=nodocs -y"
# Helper for comparison in future RUN operations (DO NOT USE)
ARG _DNFCMD="dnf --setopt=tsflags=nodocs -y"
# Set this instead, if (for example) you want to volume-mount in /var/cache/dnf
ARG DNFCMD="${_DNFCMD}"
# Avoid installing any documentation to keep image small
# During install, excluding packages is meaningless if already installed
RUN set -x && \
rm -f /etc/dnf/protected.d/sudo.conf && \
rm -f /etc/dnf/protected.d/yum.conf && \
$dnfcmd remove ${EXCLUDE_PACKAGES} && \
dnf clean all && \
rm -rf /var/cache/dnf
$DNFCMD remove ${EXCLUDE_PACKAGES}
# Enable callers to customize the runner version as needed, otherwise
# assume this image will be version-tagged, so it's fine to grab the latest.
@@ -56,11 +56,13 @@ ARG TARGETARCH="amd64"
ENV RUNNER_RPM_URL=https://gitlab-runner-downloads.s3.amazonaws.com/${RUNNER_VERSION}/rpm/gitlab-runner_${TARGETARCH}.rpm
RUN for rpm in ${EXCLUDE_PACKAGES}; do x+="--exclude=$rpm "; done && \
set -x && \
$dnfcmd update && \
$dnfcmd install $x $RUNNER_RPM_URL && \
$dnfcmd upgrade && \
$DNFCMD update && \
$DNFCMD install $x $RUNNER_RPM_URL && \
$DNFCMD upgrade && \
if [[ "${DNFCMD}" == "${_DNFCMD}" ]]; then \
dnf clean all && \
rm -rf /var/cache/dnf
rm -rf /var/cache/dnf; \
fi
# In case of a runner escape, prevent easy installation of packages.
RUN rm -f /etc/dnf/protected.d/* && \
@@ -86,16 +88,17 @@ RUN if [[ "$RUNNER_LISTEN_ADDRESS" == "disabled" ]]; then \
# A small wrapper is needed to launch a background podman system service
# process for the gitlab-runner to connect to.
ADD /gitlab-runner-wrapper /usr/local/bin/
ADD /gitlab-runner-wrapper /podman-in-podman-maintenance /usr/local/bin/
# Base image UTS NS configuration causes runner to break when launching
# nested rootless containers.
RUN sed -i -r \
-e 's/^utsns.+host.*/utsns="private"/' \
/etc/containers/containers.conf && \
chmod +x /usr/local/bin/gitlab-runner-wrapper && \
chmod +x /usr/local/bin/podman-in-podman-maintenance && \
chown -R podman.podman /home/podman && \
rm -f /home/podman/.bash* && \
echo DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" > /etc/profile.d/podman.sh
echo DOCKER_HOST="unix:///tmp/podman-run-1000/podman/podman.sock" > /etc/profile.d/podman.sh && \
# Runtime rootless-mode configuration
USER podman
@@ -104,12 +107,16 @@ VOLUME ["/home/podman/.local/share/containers/storage/",\
WORKDIR /home/podman
ENTRYPOINT ["/usr/local/bin/gitlab-runner-wrapper"]
# Ensure root storage directory exists with correct permissions
RUN mkdir -p .local/share/containers/storage
# Gitlab-runner configuration options. Default to unprivileged (nested)
# runner. Privileged is required to permit nested container image building.
ARG RUNNER_NAME="qontainers-pipglr"
ARG PRIVILEGED_RUNNER="false"
# Tags allow pinning jobs to specific runners, comma-separated list of
# tags to add to runner (no spaces!)
ARG RUNNER_TAGS="podman_in_podman"
ARG RUNNER_TAGS="podman-in-podman"
# Permit running jobs without any tag at all
ARG RUNNER_UNTAGGED="true"
ENV REGISTER_NON_INTERACTIVE="true" \
@@ -118,6 +125,7 @@ ENV REGISTER_NON_INTERACTIVE="true" \
REGISTER_ACCESS_LEVEL="ref_protected" \
REGISTER_MAXIMUM_TIMEOUT="3600" \
CI_SERVER_URL="https://gitlab.com/" \
RUNNER_NAME="${RUNNER_NAME}" \
RUNNER_EXECUTOR="docker" \
RUNNER_SHELL="bash" \
REGISTER_MAINTENANCE_NOTE="Podman-in-Podman containerized runner" \
@@ -129,16 +137,10 @@ ENV REGISTER_NON_INTERACTIVE="true" \
DOCKER_PRIVILEGED="$PRIVILEGED_RUNNER"
# Not a real build-arg. Simply here to save lots of typing.
ARG _pm="--systemd=true --device=/dev/fuse --security-opt label=disable --user podman -v gitlab-runner-storage:/home/podman/.local/share/containers/storage:Z,U -v gitlab-runner-cache:/home/podman/.cache/gitlab-runner:Z,U -v gitlab-runner-config:/home/podman/.gitlab-runner:Z,U -e PODMAN_RUNNER_DEBUG"
ARG _pm="--systemd=true --device=/dev/fuse --security-opt label=disable --user podman --volume pipglr-podman-root:/home/podman/.local/share/containers/storage:Z --volume pipglr-runner-config:/home/podman/.gitlab-runner:Z -e PODMAN_RUNNER_DEBUG -e LOG_LEVEL"
# These labels simply make it easier to register and execute the runner.
# Define them last so they are absent should a image-build failure occur.
LABEL register="podman run -it --rm $_pm --secret REGISTRATION_TOKEN,type=env \$IMAGE register"
# TODO: Figure out what's needed to run w/o --privileged. When unspecified,
# conmon fails with this error (from podman debug output):
#
# DEBU[0019] running conmon: /usr/bin/conmon args="[--api-version 1 -c 289...c08 -u 289...c08 -r /usr/bin/crun -b /home/podman/.local/share/containers/storage/overlay-containers/289...c08/userdata -p /tmp/podman-run-1000/containers/overlay-containers/289...c08/userdata/pidfile -n runner-8pxm3xb-project-19009784-concurrent-0-a71b53d132a29e56-predefined-0 --exit-dir /tmp/podman-run-1000/libpod/tmp/exits --full-attach -l k8s-file:/home/podman/.local/share/containers/storage/overlay-containers/289...c08/userdata/ctr.log --log-level debug --syslog --runtime-arg --cgroup-manager --runtime-arg disabled -i --conmon-pidfile /tmp/podman-run-1000/containers/overlay-containers/289...c08/userdata/conmon.pid --exit-command /usr/bin/podman --exit-command-arg --root --exit-command-arg /home/podman/.local/share/containers/storage --exit-command-arg --runroot --exit-command-arg /tmp/podman-run-1000/containers --exit-command-arg --log-level --exit-command-arg debug --exit-command-arg --cgroup-manager --exit-command-arg cgroupfs --exit-command-arg --tmpdir --exit-command-arg /tmp/podman-run-1000/libpod/tmp --exit-command-arg --network-config-dir --exit-command-arg --exit-command-arg --network-backend --exit-command-arg netavark --exit-command-arg --volumepath --exit-command-arg /home/podman/.local/share/containers/storage/volumes --exit-command-arg --runtime --exit-command-arg crun --exit-command-arg --storage-driver --exit-command-arg overlay --exit-command-arg --events-backend --exit-command-arg file --exit-command-arg --syslog --exit-command-arg container --exit-command-arg cleanup --exit-command-arg 289...c08]"
# [conmon:d]: failed to write to /proc/self/oom_score_adj: Permission denied
LABEL run="podman run -d --privileged --name gitlab-runner $_pm -p $RUNNER_SESSION_PORT:$RUNNER_SESSION_PORT \$IMAGE run"
# Note: Privileged mode is required to permit building container images with inner-podman
LABEL run="podman run -d --rm --privileged --name gitlab-runner $_pm \$IMAGE run"

119
README.md
View File

@@ -3,71 +3,110 @@
This container image is built daily from this `Containerfile`, and
made available as:
* FIXME
* `registry.gitlab.com/qontainers/pipglr:latest`
-or-
* `registry.gitlab.com/qontainers/pipglr:<version>`
It's purpose is to provide an easy method to execute a GitLab runner,
to service CI/CD jobs for groups and/or repositories on
[gitlab.com](https://gitlab.com). It comes pre-configured to utilize
the gitlab-runner app to execute with rootless podman containers,
the gitlab-runner app to execute within a rootless podman container,
nested inside a rootless podman container.
This is intended to provide multiple additional layers of security
for the host, when running potentially arbitrary CI/CD code. Though,
the ultimate responsibility still rests with the end-user to review
the setup and configuration relative to their own situation/environment.
This is intended to provide additional layers of security for the host,
when running potentially arbitrary CI/CD code. Though, the ultimate
responsibility still rests with the end-user to review the setup and
configuration relative to their own security situation/environment.
### Quickstart
### Operation
Several labels are set on the built image or manifest list to support
easy registration and execution of a runner container. They require
defining several environment variables for use.
This image supports `podman container runlabel`, or if your version
lacks this feature, Several labels are set on the image to support
easy registration and execution of a runner container using a special
bash command. See the examples below for more information.
#### Volume Ownership Bug
Some versions of podman contain a bug where named volumes aren't owned
by the namespaced user within a rootless container (i.e. in conjunction
with the --user option). Since the `podman` user/group inside the `pipglr`
container is known, it's possible to manually set/reset ownership:
```bash
VOLUME=pipglr-podman-root
podman volume create $VOLUME
cd $(podman unshare podman volume mount $VOLUME)
podman unshare chown 1000:1000
podman volume unmount $VOLUME
```
#### Runner registration
Each time the registration command is run, a new runner is added into
the configuration. If your intent is to simply update or modify the
configuration, please edit the config.toml file within the
`gitlab-runner-config` volume.
Note: These commands assume you have both `podman` and `jq` available.
the configuration. If however, you simply need to update/modify the
configuration, please edit the `config.toml` file directly after mounting
(default) `pipglr-runner-config` (`/home/podman/.gitlab-runner/`) volume.
For modern versions of podman, registration can be performed with the
following commands:
```bash
$ echo '<registration token>' | podman secret create REGISTRATION_TOKEN -
$ export IMAGE=<image FQIN:TAG>
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.register)
IMAGE="=registry.gitlab.com/qontainers/pipglr:latest"
echo '<actual registration token>' | podman secret create REGISTRATION_TOKEN -
podman container runlabel $IMAGE register --secret REGISTRATION_TOKEN,type=env
```
Where `<actual registration token>` is the value obtained from the "runners"
settings page of a gitlab group or project.
Note: Some versions of podman don't support the `container runlabel` sub-command.
If this is the case, you may simulate it with the following command (in addition
to the other example commands above):
```bash
eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.register)
```
#### Runner Startup
With one or more runners registered and configured, and `$IMAGE` set,
the GitLab runner container may be launched with the following commands.
With one or more runners successfully registered and configured, the GitLab
runner container may be launched with the following commands:
Note: The first time this is run, startup will take an extended amount
of time as the runner downloads and runs several (inner) support containers.
```bash
podman container runlabel $IMAGE run
```
Debugging: You may `export PODMAN_RUNNER_DEBUG=debug` to enable inner-podman
debugging (or any other supported log level) to stdout.
As above, if you're missing the `container runlabel` sub-command, the following
may be used instead (assuming `$IMAGE` remains set):
```bash
$ eval $(podman inspect --format=json $IMAGE | jq -r .[].Labels.run)
```
#### Debugging
Before starting the runner, you may `export PODMAN_RUNNER_DEBUG=debug` to enable
debugging on the inner-podman. Whereas `export LOG_LEVEL=debug` can be used to
debug the gitlab-runner itself.
## Building
This image may be built simply with:
`podman build -t runner .`
`podman build -t registry.gitlab.com/qontainers/pipglr:latest .`
This will utilize the latest stable version of podman and the latest
stable version of the gitlab runner.
### Multi-arch
### Notes
Assuming the host supports foreign-architecture emulation. The
`Containerfile` may be used to produce a multi-arch manifest-list.
For example:
* If you wish to use the `testing` or `upstream` flavors of the podman base image,
simply build with `--build-arg FLAVOR=testing` (or `upstream`).
`podman build --jobs 4 --platform linux/s390x,linux/ppc64le,linux/amd64 --manifest runner .`
* Additionally or alternatively, you may specify a specific podman base image tag
with `--build-arg BASE_TAG=<value>`. Where `<value>` is either `latest`, the
podman image version (e.g. `v4`, `v4.2`, `v4.2.0`, etc.)
### Build-args
@@ -82,13 +121,18 @@ Several build arguments are available to control the output image:
and `vX.Y.Z` (where, `X`, `Y`, and `Z` represent the podman semantic
version numbers). It's also possible to specify an image SHA.
* `EXCLUDE_PACKAGES` - A space-separated list of RPM packages to prevent
their existance in the final image. This is intended as a security measure
their existence in the final image. This is intended as a security measure
to limit the attack-surface should a gitlab-runner process escape it's
inner-container.
* `RUNNER_VERSION` - Allows specifying an exact gitlab runner version.
By default the `latest` is used, assuming the user is building a tagged
image anyway. Valid versions may be found on the [runner
release page](https://gitlab.com/gitlab-org/gitlab-runner/-/releases).
* `DNFCMD` - By default this is set to `dnf --setopt=tsflags=nodocs -y`.
However, if you'd like to volume-mount in `/var/cache/dnf` then you'll
need to use
`--build-arg DNFCMD="dnf --setopt=tsflags=nodocs -y --setopt keepcache=true`"
Note: Changing `DNFCMD` will cause build-time cache cleanup to be disabled.
* `TARGETARCH` - Supports inclusion of non-x86_64 gitlab runners. This
value is assumed to match the image's architecture. If using the
`--platform` build argument, it will be set automatically.
@@ -105,3 +149,16 @@ Several build arguments are available to control the output image:
configuration), and determines where jobs are run.
* `RUNNER_UNTAGED` - Defaults to `true`, may be set to `false`. Allows
the runner to service jobs without any tags on them at all.
### Environment variables
Nearly every option to every gitlab-runner sub-command may be specified via
environment variable. Many important/required options are set in the
`Containerfile`. However it's entirely possible to pass them in via
either of the `podman container runlabel...` container commands. To
discover them, simply append `--help` to the end of the command.
For example:
```bash
podman container runlabel $IMAGE register --help
```

View File

@@ -1,5 +1,9 @@
#!/bin/bash
# This script is intended to be called as the entrypoint for
# a podman-in-podman gitlab runner container. Any usage
# outside that context is not supported and may cause harm.
set -e
unset _debug_args
@@ -7,10 +11,18 @@ if [[ -n "$PODMAN_RUNNER_DEBUG" ]]; then
_debug_args="--log-level=$PODMAN_RUNNER_DEBUG"
fi
if [[ "$1" == "run" ]] && [[ ! -S "/tmp/podman-run-1000/podman/podman.sock" ]]; then
SOCKET="/tmp/podman-run-1000/podman/podman.sock"
if [[ "$1" == "run" ]] && [[ ! -S "$SOCKET" ]]; then
podman $_debug_args system service -t 0 &
# Prevent SIGHUP propigation to podman process
/usr/local/bin/podman-in-podman-maintenance &
# Prevent SIGHUP propagation to podman process
disown -ar
sleep 1s # Give podman a chance to get going
# Verify podman is listening on it's socket
if [[ ! -S "$SOCKET" ]]; then
echo "ERROR: Inner-podman system service failed to start, expecting to find socket '$SOCKET'. Are all volume's owned & writeable by $(id -u podman):$(id -g podman)?" > /dev/stderr
exit 1
fi
fi
exec gitlab-runner "$@"

View File

@@ -0,0 +1,19 @@
#!/bin/bash
# This script is intended to be called by the entrypoint for
# a podman-in-podman gitlab runner container. Any usage
# outside that context is not supported and may cause harm.
set -e
maintain_podman() {
# Two days seems to be a good happy-medium beween filling up
# about 40gig of storage space from moderate CI activity,
# and maintaining a useful level of caching.
while sleep 2d; do
if [[ -n "$PODMAN_RUNNER_DEBUG" ]]; then
echo "$(date --iso-8601=second) ${BASH_SOURCE[0] performing podman maintenance}"
fi
podman system prune --all --force
done
}