You can configure your own custom volume mounts. For example to have the work/docker data in memory or on NVME SSD, for i/o intensive builds. Other custom volume mounts should be possible as well, see kubernetes documentation
Example how to place the runner work dir, docker sidecar and /tmp within the runner onto a ramdisk.
kind: RunnerDeployment
spec:
template:
spec:
dockerVolumeMounts:
- mountPath: /var/lib/docker
name: docker
volumeMounts:
- mountPath: /tmp
name: tmp
volumes:
- name: docker
emptyDir:
medium: Memory
- name: work # this volume gets automatically used up for the workdir
emptyDir:
medium: Memory
- name: tmp
emptyDir:
medium: Memory
ephemeral: true # recommended to not leak data between builds.
In this example we provide NVME backed storage for the workdir, docker sidecar and /tmp within the runner. Here we use a working example on GKE, which will provide the NVME disk at /mnt/disks/ssd0. We will be placing the respective volumes in subdirs here and in order to be able to run multiple runners we will use the pod name as a prefix for subdirectories. Also the disk will fill up over time and disk space will not be freed until the node is removed.
Beware that running these persistent backend volumes leave data behind between 2 different jobs on the workdir and /tmp
with ephemeral: false
.
kind: RunnerDeployment
spec:
template:
spec:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
dockerVolumeMounts:
- mountPath: /var/lib/docker
name: docker
subPathExpr: $(POD_NAME)-docker
- mountPath: /runner/_work
name: work
subPathExpr: $(POD_NAME)-work
volumeMounts:
- mountPath: /runner/_work
name: work
subPathExpr: $(POD_NAME)-work
- mountPath: /tmp
name: tmp
subPathExpr: $(POD_NAME)-tmp
dockerEnv:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumes:
- hostPath:
path: /mnt/disks/ssd0
name: docker
- hostPath:
path: /mnt/disks/ssd0
name: work
- hostPath:
path: /mnt/disks/ssd0
name: tmp
ephemeral: true # VERY important. otherwise data inside the workdir and /tmp is not cleared between builds
Note: Ensure that the volume mount is added to the container that is running the Docker daemon.
docker
stores pulled and built image layers in the daemon's (not client) local storage area which is usually at /var/lib/docker
.
By leveraging RunnerSet's dynamic PV provisioning feature and your CSI driver, you can let ARC maintain a pool of PVs that are
reused across runner pods to retain /var/lib/docker
.
Be sure to add the volume mount to the container that is supposed to run the docker daemon.
Be sure to trigger several workflow runs before checking if the cache is effective. ARC requires an Available
PV to be reused for the new runner pod, and a PV becomes Available
only after some time after the previous runner pod that was using the PV terminated. See the related discussion.
By default, ARC creates a sidecar container named docker
within the runner pod for running the docker daemon. In that case,
it's where you need the volume mount so that the manifest looks like:
kind: RunnerSet
metadata:
name: example
spec:
template:
spec:
containers:
- name: docker
volumeMounts:
- name: var-lib-docker
mountPath: /var/lib/docker
volumeClaimTemplates:
- metadata:
name: var-lib-docker
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
storageClassName: var-lib-docker
With dockerdWithinRunnerContainer: true
, you need to add the volume mount to the runner
container.
Go
is known to cache builds under $HOME/.cache/go-build
and downloaded modules under $HOME/pkg/mod
.
The module cache dir can be customized by setting GOMOD_CACHE
so by setting it to somewhere under $HOME/.cache
,
we can have a single PV to host both build and module cache, which might improve Go module downloading and building time.
Be sure to trigger several workflow runs before checking if the cache is effective. ARC requires an Available
PV to be reused for the new runner pod, and a PV becomes Available
only after some time after the previous runner pod that was using the PV terminated. See the related discussion.
kind: RunnerSet
metadata:
name: example
spec:
template:
spec:
containers:
- name: runner
env:
- name: GOMODCACHE
value: "/home/runner/.cache/go-mod"
volumeMounts:
- name: cache
mountPath: "/home/runner/.cache"
volumeClaimTemplates:
- metadata:
name: cache
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
storageClassName: cache
ARC works by automatically creating runner pods for running actions/runner
and running config.sh
which you had to ran manually without ARC.
config.sh
is the script provided by actions/runner
to pre-configure the runner process before being started. One of the options provided by config.sh
is --work
,
which specifies the working directory where the runner runs your workflow jobs in.
The volume and the partition that hosts the work directory should have several or dozens of GBs free space that might be used by your workflow jobs.
By default, ARC uses /runner/_work
as work directory, which is powered by Kubernetes's emptyDir
. emptyDir
is usually backed by a directory created within a host's volume, somewhere under /var/lib/kuberntes/pods
. Therefore
your host's volume that is backing /var/lib/kubernetes/pods
must have enough free space to serve all the concurrent runner pods that might be deployed onto your host at the same time.
So, in case you see a job failure seemingly due to "disk full", it's very likely you need to reconfigure your host to have more free space.
In case you can't rely on host's volume, consider using RunnerSet
and backing the work directory with a ephemeral PV.
Kubernetes 1.23 or greater provides the support for generic ephemeral volumes, which is designed to support this exact use-case. It's defined in the Pod spec API so it isn't currently available for RunnerDeployment
. RunnerSet
is based on Kubernetes' StatefulSet
which mostly embeds the Pod spec under spec.template.spec
, so there you go.
kind: RunnerSet
metadata:
name: example
spec:
template:
spec:
containers:
- name: runner
volumeMounts:
- mountPath: /runner/_work
name: work
- name: docker
volumeMounts:
- mountPath: /runner/_work
name: work
volumes:
- name: work
ephemeral:
volumeClaimTemplate:
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "runner-work-dir"
resources:
requests:
storage: 10Gi