Skip to content

Commit

Permalink
Merge pull request #137 from bitdeps/dennybaa/podman-5x
Browse files Browse the repository at this point in the history
feat: migrate to bitdeps/podman-static 5.x version
  • Loading branch information
dennybaa authored Jan 12, 2025
2 parents 79027c6 + bcda001 commit 44aab05
Show file tree
Hide file tree
Showing 12 changed files with 377 additions and 291 deletions.
11 changes: 8 additions & 3 deletions .github/workflows/publish-runner.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: Publish Runner Image

on:
pull_request:
branches:
- main

workflow_dispatch:
inputs:
version:
Expand Down Expand Up @@ -46,7 +50,7 @@ jobs:
id: runner
run: |
version="${{ inputs.version }}"; version="${version#v}"
from_file="$(cat runners/podman.containerfile |
from_file="$(cat runners/Containerfile |
sed -En '/ARG\s+RUNNER_VERSION/ { s/ARG\s+RUNNER_VERSION=//; p; }')"
echo "version=${version:-$from_file}" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -82,9 +86,10 @@ jobs:
id: build-and-push
uses: docker/build-push-action@v6
with:
file: runners/podman.containerfile
file: runners/Containerfile
context: ./runners
push: true
# Publish image when the workflow is explicitly invoked
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: |
Expand Down
179 changes: 178 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,179 @@
# actions-runner
GitHub actions runner and images

GitHub Actions runner and images based on Podman.

## Podman 5.x

The Bitdeps Actions Runner uses a statically built version of Podman, which differs from the official Microsoft runner and offers unique features. Unlike the official runner, this version provides:

- **Rootless execution** on Kubernetes with minimal capabilities (only SETUID and SETGID are used).
- **No need for Buildx** — efficient multi-architecture builds are supported out of the box.
- **Container-only job execution** — this runner is designed exclusively for container jobs.
- **Pre-configured tools**:
- A Docker compatibility wrapper for remote Podman execution inside any job container.
- GitHub CLI tool.
- Pre-configured Google Artifacts Registry credential helper.

### Known Limitations

Rootless execution in cloud environments like GKE has some limitations. For instance, port mapping and DNS for service containers do not work as expected. Podman relies on the CNI plugin, and [Netavark](https://www.redhat.com/en/blog/podman-new-network-stack) does not function well in cloud environments. Despite this, the overall user experience remains smooth.

## Using Podman Actions

Since this runner does not use Buildx or Docker, Podman-native actions are used instead. Here are some examples:

```yaml
jobs:
build:
runs-on: myrunner

permissions:
contents: read
packages: write

## Container jobs are only allowed!
container:
image: images/alpine:latest

steps:
- name: Build Image
id: build-image
uses: redhat-actions/buildah-build@v2
with:
image: myorg/repo
context: .
tags: latest
build-args: ""
containerfiles: Containerfile

- name: Push to Artifact Registry
uses: redhat-actions/push-to-registry@v2
with:
image: myorg/repo
tags: latest
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
```
For more information:
* [buildah-build GitHub Action](https://github.com/redhat-actions/buildah-build)
* [push-to-registry GitHub Action](https://github.com/redhat-actions/push-to-registry)
## Deploying on Kubernetes
The standard method for deploying self-hosted runners in Kubernetes is using [actions-runner-controller](https://github.com/actions/actions-runner-controller). For detailed setup, refer to the [official documentation](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller#runner-scale-set).
Further configuration details are provided below.
### Fuse Device Plugin
With the Fuse device plugin, pods no longer need to run in privileged mode to access `/dev/fuse`. The DaemonSet below injects the fuse device into pods running on matched nodes. [Learn more](https://github.com/kubernetes-learning-group/fuse-device-plugin/blob/master/README_EN.md).

```yaml
apiVersion: apps/v1
kind: DaemonSet
spec:
template:
spec:
hostNetwork: true
nodeSelector:
myorg/gh-runner: "true"
tolerations:
- key: myorg/gh-runner
operator: Exists
effect: NoSchedule
containers:
- image: soolaugust/fuse-device-plugin:v1.0
name: fuse-device-plugin-ctr
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: device-plugin
mountPath: /var/lib/kubelet/device-plugins
volumes:
- name: device-plugin
hostPath:
path: /var/lib/kubelet/device-plugins
```

### Actions Runner Controller Values

Refer to the [values.yaml](https://github.com/actions/actions-runner-controller/blob/master/charts/gha-runner-scale-set/values.yaml) for configuration reference. The configuration below highlights important settings, such as the Fuse device plugin, virtual network interface, and AppArmor security profile.

```yaml
template:
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/runner: unconfined
cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
spec:
serviceAccountName: scale-set-wi
nodeSelector:
myorg/gh-runner: "true"
tolerations:
- key: myorg/gh-runner
operator: Exists
effect: NoSchedule
containers:
- name: runner
command:
- entrypoint.sh
- /home/runner/run.sh
imagePullPolicy: Always
image: ghcr.io/bitdeps/actions-runner:5.3.1
securityContext:
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
capabilities:
allowPrivilegeEscalation: false
drop: ["ALL"]
add:
- SETUID
- SETGID
resources:
requests:
cpu: 500m
memory: 3350Mi
limits:
github.com/fuse: 1
cpu: 2
memory: 3350Mi
volumeMounts:
- mountPath: /home/runner/.local/share/containers
name: podman-local
# Virtual network interfaces mount
- mountPath: /dev/net/tun
name: dev-tun
# Additional registry shortnames for Podman
- name: shortnames
mountPath: "/etc/containers/registries.conf.d/001-shortnames.conf"
subPath: 001-shortnames.conf
readOnly: true
volumes:
- name: podman-local
emptyDir: {}
- name: dev-tun
hostPath:
path: /dev/net/tun
type: CharDevice
- name: shortnames
configMap:
name: scale-set-configs-shortnames
```
143 changes: 143 additions & 0 deletions runners/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
## --- Actions Runner Dist
FROM alpine as runner-dist

Check warning on line 2 in runners/Containerfile

View workflow job for this annotation

GitHub Actions / publish / image

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
# renovate: datasource=github-releases depName=actions/runner
ARG RUNNER_VERSION=2.321.0
# renovate: datasource=github-releases depName=actions/runner-container-hooks
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.2
#
ARG TARGETARCH=""
ARG TARGETOS="linux"

# Fetch runner
# ref: https://github.com/actions/runner/blob/main/images/Dockerfile
#
RUN apk add --update curl gzip unzip
WORKDIR /dist

RUN export RUNNER_ARCH=${TARGETARCH:-$(arch | sed 's/x86_64/amd64/')} \
&& if [ "$RUNNER_ARCH" = "amd64" ]; then export RUNNER_ARCH=x64 ; fi \
&& curl -f -L -o runner.tar.gz https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-${TARGETOS}-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz \
&& tar xzf ./runner.tar.gz \
&& rm runner.tar.gz

RUN curl -f -L -o runner-container-hooks.zip https://github.com/actions/runner-container-hooks/releases/download/v${RUNNER_CONTAINER_HOOKS_VERSION}/actions-runner-hooks-k8s-${RUNNER_CONTAINER_HOOKS_VERSION}.zip \
&& unzip ./runner-container-hooks.zip -d ./k8s \
&& rm runner-container-hooks.zip

COPY gcloud-artifacts-locations /tmp/
RUN mkdir -p /runner/tools/helpers \
# create comma separated google docker registries list \
&& cat /tmp/gcloud-artifacts-locations | sed -ne '/^#/!{ s/$/-docker.pkg.dev/; p}' | sed ':a;N;$!ba;s/\n/,/g' > /runner/tools/helpers/gcr-registries.list



## --- Actions Runner Image
FROM ubuntu:24.04@sha256:80dd3c3b9c6cecb9f1667e9290b3bc61b78c2678c02cbdae5f0fea92cc6734ab
ARG TARGETARCH="${TARGETARCH}"

Check warning on line 36 in runners/Containerfile

View workflow job for this annotation

GitHub Actions / publish / image

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TARGETARCH' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/
ARG TARGETOS="${TARGETOS:-linux}"

Check warning on line 37 in runners/Containerfile

View workflow job for this annotation

GitHub Actions / publish / image

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$TARGETOS' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

# renovate: datasource=github-releases depName=bitdeps/podman-static
ARG PODMAN_VERSION=5.3.1
# renovate: datasource=github-releases depName=cli/cli
ARG GH_CLI_VERSION=2.65.0
# renovate: datasource=github-releases depName=GoogleCloudPlatform/docker-credential-gcr
ARG GCR_CREDSHELPER_VERSION=2.1.25

# Runner specific environment. RUNNER_TOOLS_BIN is mounted into job containers
# which provides static tools and scripts
ENV RUNNER_TOOL_CACHE=/opt/hostedtoolcache
ENV RUNNER_TOOLS_BIN=/runner/tools/bin

COPY --from=runner-dist /runner/tools /runner/tools
COPY --chmod=755 container-tools/* $RUNNER_TOOLS_BIN/
# Copy docker wrapper for podman providing compatibility for actions-runner
COPY --chmod=755 docker-compat.sh /usr/bin/docker


# Update and install essentials
RUN apt update -y \
&& apt install -y --no-install-recommends wget curl ca-certificates git lsb-release unzip uidmap libcap2-bin iptables tini \
apt-transport-https gnupg \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt

# Install static podman
RUN export ARCH=${TARGETARCH:-$(arch | sed 's/x86_64/amd64/')}; \
curl -sL https://github.com/bitdeps/podman-static/releases/download/v${PODMAN_VERSION}/podman-linux-${ARCH}.tar.gz | tar xzC /tmp \
&& dist=/tmp/podman-linux-${ARCH} \
# clean up dist and copy \
&& rm ${dist}/usr/local/bin/runc && cp -r ${dist}/etc ${dist}/usr / \
# Link podman as podman-static to runner tools, since jobs podman wrapper uses it \
&& ln /usr/local/bin/podman $RUNNER_TOOLS_BIN/podman-static \
# clean up \
&& rm -rf /tmp/*

# Create runner user (1001:1001) with subordinate id mappings
RUN useradd -u 1001 -Um -d /home/runner -s /bin/bash runner \
&& printf "runner:1:1000\nrunner:1002:64535\n" | tee /etc/subuid > /etc/subgid \
&& mkdir -p /run/user/1001 \
&& chown runner:runner /run/user/1001 \
&& chmod a+x /run/user/1001 \
## Volumes might be passed with nosuid flag, this doesn't work well on debian/ubuntu! \
## So suid/sgid bits are removed from uid mapping binaries and capabilities are set directly. \
&& chmod 0755 /usr/bin/newuidmap /usr/bin/newgidmap \
&& setcap cap_setuid+ep /usr/bin/newuidmap \
&& setcap cap_setgid+ep /usr/bin/newgidmap

# Copy actions runner dist
COPY --chown=runner:runner --from=runner-dist /dist /home/runner

# Setup and links directories
RUN mkdir /opt/hostedtoolcache && chgrp runner /opt/hostedtoolcache \
&& chmod g+rwx /opt/hostedtoolcache \
&& mkdir -p /home/runner/.local/bin /home/runner/.local/share/containers \
&& chown -R runner:runner /home/runner/.local \
# switch to legacy iptables for better compatibility \
&& update-alternatives --set iptables /usr/sbin/iptables-legacy

# Copy runner configuration and env
COPY containers /etc/containers
COPY runner/hooks /runner/hooks
COPY --chown=runner:runner runner/containers.conf /home/runner/.config/containers/
COPY --chown=runner:runner runner/env /home/runner/.env
COPY --chmod=755 entrypoint.sh /usr/bin

# Setup environment
ENV PATH="${PATH}:/home/runner/.local/bin:/home/runner/bin:${RUNNER_TOOLS_BIN}"
ENV ImageOS=ubuntu24
ENV RUNNER_MANUALLY_TRAP_SIG=1
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
ENV XDG_RUNTIME_DIR=/run/user/1001
ENV DOCKER_HOST=unix:///run/user/1001/podman/podman.sock
ENV _CONTAINERS_USERNS_CONFIGURED=""
# Force container jobs only mode (i.e. runner does not support jobs without job.<id>.container set)!
ENV ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER=true
# Use the fix to mitigate startup on ubuntu 24.04
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1

RUN echo "PATH=${PATH}" > /etc/environment \
&& echo "ImageOS=${ImageOS}" >> /etc/environment \
&& echo "DOCKER_HOST=${DOCKER_HOST}" >> /etc/environment \
&& echo "XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}" >> /etc/environment


# Install static tools
RUN cd /tmp; export ARCH=${TARGETARCH:-$(arch | sed 's/x86_64/amd64/')}; \
# install static gh cli \
curl -L https://github.com/cli/cli/releases/download/v${GH_CLI_VERSION}/gh_${GH_CLI_VERSION}_${TARGETOS}_${ARCH}.tar.gz | \
tar -xz && mv gh_${GH_CLI_VERSION}_${TARGETOS}_${ARCH}/bin/gh $RUNNER_TOOLS_BIN \
&& rm -rf /tmp/*

# Install docker credential helper for Google Artifact Registry
RUN export ARCH=${TARGETARCH:-$(arch | sed 's/x86_64/amd64/')}; \
curl -fsSL "https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v${GCR_CREDSHELPER_VERSION}/docker-credential-gcr_${TARGETOS}_${ARCH}-${GCR_CREDSHELPER_VERSION}.tar.gz" \
| tar xz docker-credential-gcr \
&& chmod +x docker-credential-gcr && mv docker-credential-gcr /usr/local/bin/

WORKDIR /home/runner
VOLUME ["/home/runner/.local/share/containers"]
USER runner

# User specific setup
RUN docker-credential-gcr configure-docker --registries=$(cat /runner/tools/helpers/gcr-registries.list)

ENTRYPOINT ["tini", "--", "/usr/bin/entrypoint.sh"]
File renamed without changes.
File renamed without changes.
12 changes: 3 additions & 9 deletions runners/containers/containers.conf
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
# See https://github.com/containers/common/blob/master/pkg/config/containers.conf
# and https://github.com/containers/podman/blob/master/contrib/podmanimage/stable/containers.conf
[containers]
netns="host"
userns="host"
ipcns="host"
utsns="host"
cgroupns="host"
cgroups="disabled"
unmask = "/proc/*"
[engine]
cgroup_manager = "cgroupfs"
events_logger="file"
runtime="crun"

[network]
network_backend = "cni"
Loading

0 comments on commit 44aab05

Please sign in to comment.