diff --git a/README.md b/README.md index 5163ff81..afc91ce1 100644 --- a/README.md +++ b/README.md @@ -961,13 +961,13 @@ spec: As it is based on `StatefulSet`, `selector` and `template.medatada.labels` needs to be defined and haev the exact same set of labels. `serviceName` must be set to some non-empty string as it is also required by `StatefulSet`. -Runner-related fields like `ephemeral`, `repository`, `organiƄtion`, `enterprise`, and so on should be written directly under `spec`. +Runner-related fields like `ephemeral`, `repository`, `organization`, `enterprise`, and so on should be written directly under `spec`. -Fields like `volumeClaimTemplates` that originates from `StatefulSet` shuold also be written directly under `spec`. +Fields like `volumeClaimTemplates` that originates from `StatefulSet` should also be written directly under `spec`. Pod-related fields like security contexts and volumes are written under `spec.template.spec` like `StatefulSet`. -Simillarly, container-related fields like resource requests and limits, container image names and tags, security context, and so on are written under `spec.template.spec.containers`. There are two reserved container `name`, `runner` and `docker`. The former is for the container that runs [actions runner](https://github.com/actions/runner) and the latter is for the container that runs a dockerd. +Similarly, container-related fields like resource requests and limits, container image names and tags, security context, and so on are written under `spec.template.spec.containers`. There are two reserved container `name`, `runner` and `docker`. The former is for the container that runs [actions runner](https://github.com/actions/runner) and the latter is for the container that runs a dockerd. For a more complex example, see the below: @@ -1018,7 +1018,7 @@ https://github.com/actions-runner-controller/actions-runner-controller/pull/629 Under the hood, `RunnerSet` relies on Kubernetes's `StatefulSet` and Mutating Webhook. A statefulset is used to create a number of pods that has stable names and dynamically provisioned persistent volumes, so that each statefulset-managed pod gets the same persisntet volume even after restarting. A mutating webhook is used to dynamically inject a runner's "registration token" which is used to call GitHub's "Create Runner" API. -We envision that `RunnerSet` will eventually replaces `RunnerDeployment`, as `RunnerSet` provides a more standard API that is easy to learn and use because it is based on `StatefulSet`, and it has a support for `volumeClaimTemplates` which is crucial to manage dynamically provisioned persistent volumes. +We envision that `RunnerSet` will eventually replace `RunnerDeployment`, as `RunnerSet` provides a more standard API that is easy to learn and use because it is based on `StatefulSet`, and it has a support for `volumeClaimTemplates` which is crucial to manage dynamically provisioned persistent volumes. ### Software Installed in the Runner Image diff --git a/api/v1alpha1/runner_types.go b/api/v1alpha1/runner_types.go index ca77c594..301cdb58 100644 --- a/api/v1alpha1/runner_types.go +++ b/api/v1alpha1/runner_types.go @@ -69,6 +69,8 @@ type RunnerConfig struct { DockerRegistryMirror *string `json:"dockerRegistryMirror,omitempty"` // +optional VolumeSizeLimit *resource.Quantity `json:"volumeSizeLimit,omitempty"` + // +optional + VolumeStorageMedium *string `json:"volumeStorageMedium,omitempty"` } // RunnerPodSpec defines the desired pod spec fields of the runner pod diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 073485e3..d8bb38ec 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -408,6 +408,11 @@ func (in *RunnerConfig) DeepCopyInto(out *RunnerConfig) { x := (*in).DeepCopy() *out = &x } + if in.VolumeStorageMedium != nil { + in, out := &in.VolumeStorageMedium, &out.VolumeStorageMedium + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerConfig. diff --git a/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerdeployments.yaml b/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerdeployments.yaml index 763818d7..1e6269de 100644 --- a/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerdeployments.yaml +++ b/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerdeployments.yaml @@ -984,6 +984,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string volumes: items: description: Volume represents a named volume in a pod that may be accessed by any container in the pod. diff --git a/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerreplicasets.yaml b/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerreplicasets.yaml index 53a36145..2fd14326 100644 --- a/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerreplicasets.yaml +++ b/charts/actions-runner-controller/crds/actions.summerwind.dev_runnerreplicasets.yaml @@ -981,6 +981,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string volumes: items: description: Volume represents a named volume in a pod that may be accessed by any container in the pod. diff --git a/charts/actions-runner-controller/crds/actions.summerwind.dev_runners.yaml b/charts/actions-runner-controller/crds/actions.summerwind.dev_runners.yaml index b23f9309..1ae62374 100644 --- a/charts/actions-runner-controller/crds/actions.summerwind.dev_runners.yaml +++ b/charts/actions-runner-controller/crds/actions.summerwind.dev_runners.yaml @@ -927,6 +927,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string volumes: items: description: Volume represents a named volume in a pod that may be accessed by any container in the pod. diff --git a/charts/actions-runner-controller/crds/actions.summerwind.dev_runnersets.yaml b/charts/actions-runner-controller/crds/actions.summerwind.dev_runnersets.yaml index 570387d6..aa8a23a2 100644 --- a/charts/actions-runner-controller/crds/actions.summerwind.dev_runnersets.yaml +++ b/charts/actions-runner-controller/crds/actions.summerwind.dev_runnersets.yaml @@ -7113,6 +7113,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string workDir: type: string required: diff --git a/config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml b/config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml index 763818d7..1e6269de 100644 --- a/config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml +++ b/config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml @@ -984,6 +984,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string volumes: items: description: Volume represents a named volume in a pod that may be accessed by any container in the pod. diff --git a/config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml b/config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml index 53a36145..2fd14326 100644 --- a/config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml +++ b/config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml @@ -981,6 +981,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string volumes: items: description: Volume represents a named volume in a pod that may be accessed by any container in the pod. diff --git a/config/crd/bases/actions.summerwind.dev_runners.yaml b/config/crd/bases/actions.summerwind.dev_runners.yaml index b23f9309..1ae62374 100644 --- a/config/crd/bases/actions.summerwind.dev_runners.yaml +++ b/config/crd/bases/actions.summerwind.dev_runners.yaml @@ -927,6 +927,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string volumes: items: description: Volume represents a named volume in a pod that may be accessed by any container in the pod. diff --git a/config/crd/bases/actions.summerwind.dev_runnersets.yaml b/config/crd/bases/actions.summerwind.dev_runnersets.yaml index 570387d6..aa8a23a2 100644 --- a/config/crd/bases/actions.summerwind.dev_runnersets.yaml +++ b/config/crd/bases/actions.summerwind.dev_runnersets.yaml @@ -7113,6 +7113,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true + volumeStorageMedium: + type: string workDir: type: string required: diff --git a/controllers/runner_controller.go b/controllers/runner_controller.go index 74e75a60..8713c521 100644 --- a/controllers/runner_controller.go +++ b/controllers/runner_controller.go @@ -886,32 +886,49 @@ func newRunnerPod(template corev1.Pod, runnerSpec v1alpha1.RunnerConfig, default // When you're NOT using dindWithinRunner=true, // it must also be shared with the dind container as it seems like required to run docker steps. // + // Setting VolumeSizeLimit to zero will disable /runner emptydir mount + // + // VolumeStorageMedium defines ways that storage can be allocated to a volume: "", "Memory", "HugePages", "HugePages-" + // runnerVolumeName := "runner" runnerVolumeMountPath := "/runner" runnerVolumeEmptyDir := &corev1.EmptyDirVolumeSource{} + if runnerSpec.VolumeStorageMedium != nil { + runnerVolumeEmptyDir.Medium = corev1.StorageMedium(*runnerSpec.VolumeStorageMedium) + } + if runnerSpec.VolumeSizeLimit != nil { runnerVolumeEmptyDir.SizeLimit = runnerSpec.VolumeSizeLimit } - pod.Spec.Volumes = append(pod.Spec.Volumes, - corev1.Volume{ - Name: runnerVolumeName, - VolumeSource: corev1.VolumeSource{ - EmptyDir: runnerVolumeEmptyDir, + if runnerSpec.VolumeSizeLimit == nil || !runnerSpec.VolumeSizeLimit.IsZero() { + pod.Spec.Volumes = append(pod.Spec.Volumes, + corev1.Volume{ + Name: runnerVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: runnerVolumeEmptyDir, + }, }, - }, - ) + ) - runnerContainer.VolumeMounts = append(runnerContainer.VolumeMounts, - corev1.VolumeMount{ - Name: runnerVolumeName, - MountPath: runnerVolumeMountPath, - }, - ) + runnerContainer.VolumeMounts = append(runnerContainer.VolumeMounts, + corev1.VolumeMount{ + Name: runnerVolumeName, + MountPath: runnerVolumeMountPath, + }, + ) + } if !dockerdInRunner && dockerEnabled { + if runnerSpec.VolumeSizeLimit != nil && runnerSpec.VolumeSizeLimit.IsZero() { + return *pod, fmt.Errorf( + "%s volume can't be disabled because it is required to share the working directory between the runner and the dockerd containers", + runnerVolumeName, + ) + } + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ Name: "work", diff --git a/main.go b/main.go index d11d324e..d8d3bc58 100644 --- a/main.go +++ b/main.go @@ -203,7 +203,7 @@ func main() { log.Info( "Initializing actions-runner-controller", - "github-api-cahce-duration", gitHubAPICacheDuration, + "github-api-cache-duration", gitHubAPICacheDuration, "sync-period", syncPeriod, "runner-image", runnerImage, "docker-image", dockerImage,