Skip to content

Commit 5d54ab0

Browse files
committed
Add Docker-based GitHub Actions self-hosted runner setup
- Introduced `Dockerfile` to build a lightweight custom image for the runner. - Added GitHub workflow (`.github/build.yml`) for building, pushing, and tagging multi-architecture images. - Included a startup script (`scripts/start.sh`) for registering and running the GitHub Actions runner.
0 parents  commit 5d54ab0

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

.github/build.yml

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
name: Build and Push gh-runner
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
env:
10+
IMAGE_NAME: ghcr.io/${{ github.repository }}
11+
RUNNER_VERSION: 2.325.0
12+
13+
jobs:
14+
prepare:
15+
name: "🧰 Build Preparation"
16+
runs-on: ubuntu-latest
17+
outputs:
18+
image_name: ${{ steps.normalize.outputs.name }}
19+
short_sha: ${{ steps.sha.outputs.short }}
20+
is_tag: ${{ steps.tag.outputs.is_tag }}
21+
version: ${{ steps.tag.outputs.version }}
22+
major: ${{ steps.tag.outputs.major }}
23+
minor: ${{ steps.tag.outputs.minor }}
24+
steps:
25+
- uses: actions/checkout@v4
26+
- name: Normalize image name
27+
id: normalize
28+
run: echo "name=${IMAGE_NAME,,}" >> "$GITHUB_OUTPUT"
29+
- name: Extract short SHA
30+
id: sha
31+
run: echo "short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
32+
- name: Extract version from tag
33+
id: tag
34+
run: |
35+
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
36+
version="${GITHUB_REF#refs/tags/}"
37+
major=$(cut -d. -f1 <<< "$version")
38+
minor=$(cut -d. -f2 <<< "$version")
39+
echo "is_tag=true" >> "$GITHUB_OUTPUT"
40+
echo "version=$version" >> "$GITHUB_OUTPUT"
41+
echo "major=$major" >> "$GITHUB_OUTPUT"
42+
echo "minor=$minor" >> "$GITHUB_OUTPUT"
43+
else
44+
echo "is_tag=false" >> "$GITHUB_OUTPUT"
45+
46+
build-amd64:
47+
name: "🐳 Build Docker (AMD64)"
48+
needs: prepare
49+
runs-on: ubuntu-latest
50+
steps:
51+
- uses: actions/checkout@v4
52+
- uses: docker/setup-buildx-action@v3
53+
- uses: docker/login-action@v3
54+
with:
55+
registry: ghcr.io
56+
username: ${{ github.actor }}
57+
password: ${{ secrets.GITHUB_TOKEN }}
58+
- name: Build and push AMD64 image
59+
uses: docker/build-push-action@v6
60+
with:
61+
context: .
62+
push: true
63+
platforms: linux/amd64
64+
tags: |
65+
${{ needs.prepare.outputs.image_name }}:amd64
66+
${{ needs.prepare.outputs.image_name }}:${{ needs.prepare.outputs.short_sha }}-amd64
67+
build-args: |
68+
RUNNER_VERSION=${{ env.RUNNER_VERSION }}
69+
cache-from: type=gha
70+
cache-to: type=gha,mode=max
71+
72+
build-arm64:
73+
name: "🐳 Build Docker (ARM64)"
74+
needs: prepare
75+
runs-on: self-hosted
76+
steps:
77+
- uses: actions/checkout@v4
78+
- uses: docker/setup-buildx-action@v3
79+
- uses: docker/login-action@v3
80+
with:
81+
registry: ghcr.io
82+
username: ${{ github.actor }}
83+
password: ${{ secrets.GITHUB_TOKEN }}
84+
- name: Build and push ARM64 image
85+
uses: docker/build-push-action@v6
86+
with:
87+
context: .
88+
push: true
89+
platforms: linux/arm64
90+
tags: |
91+
${{ needs.prepare.outputs.image_name }}:arm64
92+
${{ needs.prepare.outputs.image_name }}:${{ needs.prepare.outputs.short_sha }}-arm64
93+
build-args: |
94+
RUNNER_VERSION=${{ env.RUNNER_VERSION }}
95+
cache-from: type=gha
96+
cache-to: type=gha,mode=max
97+
98+
manifest:
99+
name: "📦 Docker Manifest"
100+
needs: [build-amd64, build-arm64]
101+
runs-on: ubuntu-latest
102+
env:
103+
IMAGE_NAME: ${{ needs.prepare.outputs.image_name }}
104+
SHORT_SHA: ${{ needs.prepare.outputs.short_sha }}
105+
IS_TAG: ${{ needs.prepare.outputs.is_tag }}
106+
VERSION: ${{ needs.prepare.outputs.version }}
107+
MAJOR: ${{ needs.prepare.outputs.major }}
108+
MINOR: ${{ needs.prepare.outputs.minor }}
109+
GITHUB_REF: ${{ github.ref }}
110+
steps:
111+
- uses: docker/login-action@v3
112+
with:
113+
registry: ghcr.io
114+
username: ${{ github.actor }}
115+
password: ${{ secrets.GITHUB_TOKEN }}
116+
- name: Create and push Docker manifest
117+
run: |
118+
docker buildx imagetools create \
119+
--tag $IMAGE_NAME:$SHORT_SHA \
120+
$IMAGE_NAME:amd64 \
121+
$IMAGE_NAME:arm64
122+
123+
if [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
124+
docker buildx imagetools create \
125+
--tag $IMAGE_NAME:latest \
126+
$IMAGE_NAME:amd64 \
127+
$IMAGE_NAME:arm64
128+
fi
129+
130+
if [[ "$IS_TAG" == "true" ]]; then
131+
docker buildx imagetools create \
132+
--tag $IMAGE_NAME:$VERSION \
133+
--tag $IMAGE_NAME:$MAJOR \
134+
--tag $IMAGE_NAME:$MAJOR.$MINOR \
135+
$IMAGE_NAME:amd64 \
136+
$IMAGE_NAME:arm64

Dockerfile

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# base image
2+
FROM ubuntu:24.04
3+
4+
ARG RUNNER_VERSION
5+
ENV DEBIAN_FRONTEND=noninteractive
6+
7+
LABEL Author="Florent Morselli"
8+
LABEL Email="florent.morselli@spomky-labs.com"
9+
LABEL GitHub="https://github.com/Spomky"
10+
LABEL BaseImage="ubuntu:24.04"
11+
LABEL RunnerVersion=${RUNNER_VERSION}
12+
13+
# 1. Update base system and create a non-root user
14+
RUN apt-get update -y && \
15+
apt-get upgrade -y && \
16+
useradd -m -s /bin/bash docker
17+
18+
# 2. Install core system packages (including gnupg for keyring management)
19+
RUN apt-get install -y --no-install-recommends \
20+
curl \
21+
gnupg \
22+
nodejs \
23+
wget \
24+
unzip \
25+
vim \
26+
git \
27+
jq \
28+
build-essential \
29+
libssl-dev \
30+
libffi-dev \
31+
python3 \
32+
python3-venv \
33+
python3-dev \
34+
python3-pip
35+
36+
# 3. Clean APT cache to reduce image size
37+
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
38+
39+
# 4. Add Docker’s official GPG key
40+
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
41+
gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
42+
43+
# 5. Add Docker’s APT repository (for CLI only)
44+
RUN echo "deb [arch=arm64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
45+
https://download.docker.com/linux/ubuntu \
46+
$(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" \
47+
> /etc/apt/sources.list.d/docker.list
48+
49+
# 6. Install Docker CLI only (no daemon)
50+
RUN apt-get update && \
51+
apt-get install -y --no-install-recommends docker-ce-cli
52+
53+
# 7. Download GitHub Actions runner
54+
RUN mkdir -p /actions-runner && \
55+
curl -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz \
56+
-o /actions-runner/runner.tar.gz && \
57+
tar -xzf /actions-runner/runner.tar.gz -C /actions-runner && \
58+
rm /actions-runner/runner.tar.gz
59+
60+
# 8. Install runner dependencies
61+
RUN /actions-runner/bin/installdependencies.sh
62+
63+
# 9. Copy script
64+
COPY scripts/start.sh /start.sh
65+
RUN chmod +x /start.sh

scripts/start.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
GH_OWNER=$GH_OWNER
6+
#GH_REPOSITORY=$GH_REPOSITORY
7+
GH_TOKEN=$GH_TOKEN
8+
9+
RUNNER_NAME="runner-$(head /dev/urandom | tr -dc a-z0-9 | head -c 6)"
10+
export RUNNER_NAME
11+
12+
# Ensure /actions-runner is accessible and owned
13+
chown -R docker:docker /actions-runner
14+
15+
# Get Docker socket GID and allow docker user to access it
16+
DOCKER_GID=$(stat -c '%g' /var/run/docker.sock)
17+
groupadd -g "$DOCKER_GID" docker-host || true
18+
usermod -aG docker-host docker
19+
20+
# Switch to docker user for config and execution
21+
su docker <<EOF
22+
cd /actions-runner
23+
24+
#if [ ! -f .runner ]; then
25+
# REG_TOKEN=\$(curl -sX POST \
26+
# -H "Accept: application/vnd.github.v3+json" \
27+
# -H "Authorization: token ${GH_TOKEN}" \
28+
# https://api.github.com/repos/${GH_OWNER}/${GH_REPOSITORY}/actions/runners/registration-token | jq -r .token)
29+
#
30+
# ./config.sh --unattended --url https://github.com/${GH_OWNER}/${GH_REPOSITORY} --token "\$REG_TOKEN" --name "\$RUNNER_NAME"
31+
#fi
32+
if [ ! -f .runner ]; then
33+
REG_TOKEN=\$(curl -sX POST \
34+
-H "Accept: application/vnd.github.v3+json" \
35+
-H "Authorization: token ${GH_TOKEN}" \
36+
https://api.github.com/orgs/${GH_OWNER}/actions/runners/registration-token | jq -r .token)
37+
38+
./config.sh --unattended --url https://github.com/${GH_OWNER} --token "\$REG_TOKEN" --name "\$RUNNER_NAME"
39+
fi
40+
41+
42+
cleanup() {
43+
echo "Removing runner..."
44+
./config.sh remove --unattended --token "\$REG_TOKEN"
45+
}
46+
trap 'cleanup; exit 130' INT
47+
trap 'cleanup; exit 143' TERM
48+
49+
./run.sh
50+
EOF

0 commit comments

Comments
 (0)