Skip to content

Commit

Permalink
Merge branch 'myoung34:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Niek authored Sep 13, 2021
2 parents 450ab99 + b11f137 commit c9b0931
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 25 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
push:
paths:
- Dockerfile.base
- .github/workflows/base.yml
branches:
- master
- develop
Expand Down Expand Up @@ -88,4 +89,4 @@ jobs:
max_attempts: 3
retry_on: error
timeout_minutes: 120
command: docker buildx build -f Dockerfile.base.debian-${{ matrix.release }} -t ${ORG}/github-runner-base:debian-${{ matrix.release }} --output "type=image,push=true" --platform linux/amd64,linux/arm64,linux/arm/v7 .
command: docker buildx build -f Dockerfile.base.debian-${{ matrix.release }} -t ${ORG}/github-runner-base:debian-${{ matrix.release }} --output "type=image,push=true" --platform linux/amd64,linux/arm64,linux/arm/v7 .
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ LABEL maintainer="myoung34@my.apsu.edu"
ENV AGENT_TOOLSDIRECTORY=/opt/hostedtoolcache
RUN mkdir -p /opt/hostedtoolcache

ARG GH_RUNNER_VERSION="2.278.0"
ARG GH_RUNNER_VERSION="2.281.1"
ARG TARGETPLATFORM

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
Expand All @@ -17,8 +17,8 @@ RUN chmod +x /actions-runner/install_actions.sh \
&& /actions-runner/install_actions.sh ${GH_RUNNER_VERSION} ${TARGETPLATFORM} \
&& rm /actions-runner/install_actions.sh

COPY token.sh entrypoint.sh /
RUN chmod +x /token.sh /entrypoint.sh
COPY token.sh entrypoint.sh ephemeral-runner.sh /
RUN chmod +x /token.sh /entrypoint.sh /ephemeral-runner.sh

ENTRYPOINT ["/entrypoint.sh"]
CMD ["/actions-runner/bin/runsvc.sh"]
10 changes: 7 additions & 3 deletions Dockerfile.base
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
FROM ubuntu:focal
LABEL maintainer="myoung34@my.apsu.edu"

# TODO: Ubuntu focal (20.04) has git v2.25, but the minimum needed for GitHub
# Actions runners is v2.29. For now, we build git from source. When updating
# to hirsute (21.04) or later, we can remove the instructions to build from
# source in favor of the regular Ubuntu git package.
ARG GIT_VERSION="2.29.0"

ARG DUMB_INIT_VERSION="1.2.2"
ARG DOCKER_KEY="7EA0A9C3F273FCD8"

Expand Down Expand Up @@ -37,7 +42,7 @@ RUN apt-get update && \
openssh-client \
locales \
python3-pip \
jq \
python \
dumb-init \
&& pip3 install --no-cache-dir awscliv2 \
&& locale-gen en_US.UTF-8 \
Expand All @@ -61,7 +66,6 @@ RUN apt-get update && \
&& ( add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/${distro} ${version} stable" ) \
&& apt-get update \
&& apt-get install -y docker-ce docker-ce-cli containerd.io --no-install-recommends --allow-unauthenticated \
&& [[ $(lscpu -J | jq -r '.lscpu[] | select(.field == "Vendor ID:") | .data') == "ARM" ]] && echo "Not installing docker-compose. See https://github.com/docker/compose/issues/6831" || ( curl -sL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose ) \
&& chmod +x /usr/local/bin/docker-compose \
&& [[ $(lscpu -J | jq -r '.lscpu[] | select(.field == "Vendor ID:") | .data') == "ARM" ]] && echo "Not installing docker-compose. See https://github.com/docker/compose/issues/6831" || ( curl -sL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose ) \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/*
73 changes: 72 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ These containers are built via Github actions that [copy the dockerfile](https:/
| `RUNNER_GROUP` | Name of the runner group to add this runner to (defaults to the default runner group) |
| `GITHUB_HOST` | Optional URL of the Github Enterprise server e.g github.mycompany.com. Defaults to `github.com`. |
| `DISABLE_AUTOMATIC_DEREGISTRATION` | Optional flag to disable signal catching for deregistration. Default is `false`. Any value other than exactly `false` is considered `true`. See [here](https://github.com/myoung34/docker-github-actions-runner/issues/94) |
| `CONFIGURED_ACTIONS_RUNNER_FILES_DIR` | Path to use for runner data. It allows avoiding reregistration each the start of the runner. No default value. |

## Examples ##

Expand Down Expand Up @@ -86,7 +87,24 @@ docker run -d --restart always --name github-runner \
myoung34/github-runner:latest
```

Or shell wrapper:
Adding the reusage of the registered runner (can be propogated to all other approaches):

```shell
# per repo
docker run -d --restart always --name github-runner \
-e REPO_URL="https://github.com/myoung34/repo" \
-e RUNNER_NAME="foo-runner" \
-e RUNNER_TOKEN="footoken" \
-e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \
-e RUNNER_GROUP="my-group" \
-e CONFIGURED_ACTIONS_RUNNER_FILES_DIR="/actions-runner-files" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
-v /tmp/foo:/actions-runner-files \
myoung34/github-runner:latest
```

Shell wrapper:

```shell
function github-runner {
Expand Down Expand Up @@ -297,3 +315,56 @@ docker run -d --restart always --name github-runner \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
myoung34/github-runner:latest
```

## Ephemeral mode

GitHub's hosted runners are completely ephemeral. You can remove all its data without breaking all future jobs.

To achieve the same resilience in a self-hosted runner:
1. override the command for your runner with `/ephemeral-runner.sh` (which will terminate after one job executes)
2. don't mount a local folder into `RUNNER_WORKDIR` (to ensure no filesystem persistence)
3. run the container with `--rm` (to delete it after termination)
4. wrap the container execution in a system service that restarts (to start a fresh container after each job)

Here's an example service definition for systemd:

```
# Install with:
# sudo install -m 644 ephemeral-github-actions-runner.service /etc/systemd/system/
# sudo systemctl daemon-reload
# sudo systemctl enable ephemeral-github-actions-runner
# Run with:
# sudo systemctl start ephemeral-github-actions-runner
# Stop with:
# sudo systemctl stop ephemeral-github-actions-runner
# See live logs with:
# journalctl -f -u ephemeral-github-actions-runner.service --no-hostname --no-tail
[Unit]
Description=Ephemeral GitHub Actions Runner Container
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
Restart=always
ExecStartPre=-/usr/bin/docker stop %n
ExecStartPre=-/usr/bin/docker rm %n
ExecStartPre=-/usr/bin/docker pull myoung34/github-runner:latest
ExecStart=/usr/bin/docker run --rm --env-file /etc/ephemeral-github-actions-runner.env --name %n myoung34/ephemeral-github-actions-runner:latest /ephemeral-runner.sh
[Install]
WantedBy=multi-user.target
```

And an example of the corresponding env file that the service reads from:

```
# Install with:
# sudo install -m 600 ephemeral-github-actions-runner.env /etc/
REPO_URL=https://github.com/your-org/your-repo
RUNNER_NAME=your-runner-name-here
ACCESS_TOKEN=foo-access-token
RUNNER_WORKDIR=/tmp/runner/work
LABELS=any-custom-labels-go-here
```
65 changes: 48 additions & 17 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,27 +51,58 @@ case ${RUNNER_SCOPE} in
;;
esac

if [[ -n "${ACCESS_TOKEN}" ]]; then
_TOKEN=$(bash /token.sh)
RUNNER_TOKEN=$(echo "${_TOKEN}" | jq -r .token)
fi
configure_runner() {
if [[ -n "${ACCESS_TOKEN}" ]]; then
echo "Obtaining the token of the runnet"
_TOKEN=$(bash /token.sh)
RUNNER_TOKEN=$(echo "${_TOKEN}" | jq -r .token)
fi

echo "Configuring"
./config.sh \
--url "${_SHORT_URL}" \
--token "${RUNNER_TOKEN}" \
--name "${_RUNNER_NAME}" \
--work "${_RUNNER_WORKDIR}" \
--labels "${_LABELS}" \
--runnergroup "${_RUNNER_GROUP}" \
--unattended \
--replace
echo "Configuring"
./config.sh \
--url "${_SHORT_URL}" \
--token "${RUNNER_TOKEN}" \
--name "${_RUNNER_NAME}" \
--work "${_RUNNER_WORKDIR}" \
--labels "${_LABELS}" \
--runnergroup "${_RUNNER_GROUP}" \
--unattended \
--replace
}

unset RUNNER_TOKEN

# Opt into runner reusage because a value was given
if [[ -n "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then
echo "Runner reusage is enabled"

# directory exists, copy the data
if [[ -d "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then
echo "Copying previous data"
cp -p -r "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}/." "/actions-runner"
fi

if [ -f "/actions-runner/.runner" ]; then
echo "The runner has already been configured"
else
configure_runner
fi
else
echo "Runner reusage is disabled"
configure_runner
fi

if [[ -n "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then
echo "Reusage is enabled. Storing data to ${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}"
# Quoting (even with double-quotes) the regexp brokes the copying
cp -p -r "/actions-runner/_diag" "/actions-runner/svc.sh" /actions-runner/.[^.]* "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}"
fi

if [[ ${_DISABLE_AUTOMATIC_DEREGISTRATION} == "false" ]]; then
trap deregister_runner SIGINT SIGQUIT SIGTERM
fi

# shellcheck disable=SC2068
$@
unset ACCESS_TOKEN
unset RUNNER_TOKEN

# Container's command (CMD) execution
"$@"
36 changes: 36 additions & 0 deletions ephemeral-runner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash

echo "*** Starting ephemeral runner. ***"
/actions-runner/run.sh --once
rv=$?

# See exit code constants in the runner source here:
# https://github.com/actions/runner/blob/be96323/src/Runner.Common/Constants.cs#L135
if [[ $rv == 4 ]]; then
# The runner software was updated.
echo "*** Software update detected. ***"

echo "*** Waiting for update to complete. ***"
# Hard-coded sleep. Without some delay, the update is still in progress in
# the background, leading to failures when we re-launch.
sleep 10

# Now add an adaptive delay, where we loop and check if the Runner is usable
# yet. As soon as it is, break.
for i in $(seq 10); do
if /actions-runner/bin/Runner.Listener --version &>/dev/null; then
break
fi

echo "*** Update still in progress... ***"
sleep 5
done

# Now re-launch the script.
echo "*** Re-launching runner. ***"
exec "$0"
fi

# For any other return value, let the script and the Docker container terminate.
echo "*** Exit code $rv ***"
exit $rv

0 comments on commit c9b0931

Please sign in to comment.