mirror of
https://github.com/actions/actions-runner-controller.git
synced 2025-12-10 19:50:30 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d78fb07b3 | ||
|
|
faaca10fba | ||
|
|
d16dfac0f8 | ||
|
|
af483d83da | ||
|
|
92920926fe | ||
|
|
7d0bfb77e3 | ||
|
|
c4074130e8 | ||
|
|
be2e61f209 | ||
|
|
da818a898a | ||
|
|
2d250d5e06 |
14
.github/workflows/build-runner.yml
vendored
14
.github/workflows/build-runner.yml
vendored
@@ -43,6 +43,13 @@ jobs:
|
|||||||
--tag ${DOCKERHUB_USERNAME}/actions-runner:v${RUNNER_VERSION} \
|
--tag ${DOCKERHUB_USERNAME}/actions-runner:v${RUNNER_VERSION} \
|
||||||
--tag ${DOCKERHUB_USERNAME}/actions-runner:latest \
|
--tag ${DOCKERHUB_USERNAME}/actions-runner:latest \
|
||||||
-f Dockerfile .
|
-f Dockerfile .
|
||||||
|
docker buildx build \
|
||||||
|
--build-arg RUNNER_VERSION=${RUNNER_VERSION} \
|
||||||
|
--build-arg DOCKER_VERSION=${DOCKER_VERSION} \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--tag ${DOCKERHUB_USERNAME}/actions-runner-dind:v${RUNNER_VERSION} \
|
||||||
|
--tag ${DOCKERHUB_USERNAME}/actions-runner-dind:latest \
|
||||||
|
-f Dockerfile.dindrunner .
|
||||||
|
|
||||||
- name: Login to GitHub Docker Registry
|
- name: Login to GitHub Docker Registry
|
||||||
run: echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
|
run: echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
|
||||||
@@ -62,3 +69,10 @@ jobs:
|
|||||||
--tag ${DOCKERHUB_USERNAME}/actions-runner:v${RUNNER_VERSION} \
|
--tag ${DOCKERHUB_USERNAME}/actions-runner:v${RUNNER_VERSION} \
|
||||||
--tag ${DOCKERHUB_USERNAME}/actions-runner:latest \
|
--tag ${DOCKERHUB_USERNAME}/actions-runner:latest \
|
||||||
-f Dockerfile . --push
|
-f Dockerfile . --push
|
||||||
|
docker buildx build \
|
||||||
|
--build-arg RUNNER_VERSION=${RUNNER_VERSION} \
|
||||||
|
--build-arg DOCKER_VERSION=${DOCKER_VERSION} \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--tag ${DOCKERHUB_USERNAME}/actions-runner-dind:v${RUNNER_VERSION} \
|
||||||
|
--tag ${DOCKERHUB_USERNAME}/actions-runner-dind:latest \
|
||||||
|
-f Dockerfile.dindrunner . --push
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -42,6 +42,8 @@ jobs:
|
|||||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Build Container Image
|
- name: Build Container Image
|
||||||
|
env:
|
||||||
|
DOCKERHUB_USERNAME: ${{ github.repository_owner }}
|
||||||
run: |
|
run: |
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform linux/amd64,linux/arm64 \
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
|||||||
42
Makefile
42
Makefile
@@ -1,5 +1,8 @@
|
|||||||
NAME ?= summerwind/actions-runner-controller
|
NAME ?= summerwind/actions-runner-controller
|
||||||
VERSION ?= latest
|
VERSION ?= latest
|
||||||
|
# From https://github.com/VictoriaMetrics/operator/pull/44
|
||||||
|
YAML_DROP=$(YQ) delete --inplace
|
||||||
|
YAML_DROP_PREFIX=spec.validation.openAPIV3Schema.properties.spec.properties
|
||||||
|
|
||||||
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
|
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
|
||||||
CRD_OPTIONS ?= "crd:trivialVersions=true"
|
CRD_OPTIONS ?= "crd:trivialVersions=true"
|
||||||
@@ -56,7 +59,9 @@ deploy: manifests
|
|||||||
kustomize build config/default | kubectl apply -f -
|
kustomize build config/default | kubectl apply -f -
|
||||||
|
|
||||||
# Generate manifests e.g. CRD, RBAC etc.
|
# Generate manifests e.g. CRD, RBAC etc.
|
||||||
manifests: controller-gen
|
manifests: manifests-118 fix118
|
||||||
|
|
||||||
|
manifests-118: controller-gen
|
||||||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||||
|
|
||||||
# Run go fmt against code
|
# Run go fmt against code
|
||||||
@@ -67,6 +72,22 @@ fmt:
|
|||||||
vet:
|
vet:
|
||||||
go vet ./...
|
go vet ./...
|
||||||
|
|
||||||
|
# workaround for CRD issue with k8s 1.18 & controller-gen
|
||||||
|
# ref: https://github.com/kubernetes/kubernetes/issues/91395
|
||||||
|
fix118: yq
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.containers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.initContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.sidecarContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerreplicasets.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.ephemeralContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.containers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.initContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.sidecarContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runnerdeployments.yaml $(YAML_DROP_PREFIX).template.properties.spec.properties.ephemeralContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).containers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).initContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).sidecarContainers.items.properties
|
||||||
|
$(YAML_DROP) config/crd/bases/actions.summerwind.dev_runners.yaml $(YAML_DROP_PREFIX).ephemeralContainers.items.properties
|
||||||
|
|
||||||
# Generate code
|
# Generate code
|
||||||
generate: controller-gen
|
generate: controller-gen
|
||||||
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..."
|
$(CONTROLLER_GEN) object:headerFile=./hack/boilerplate.go.txt paths="./..."
|
||||||
@@ -105,6 +126,7 @@ github-release: release
|
|||||||
# download controller-gen if necessary
|
# download controller-gen if necessary
|
||||||
controller-gen:
|
controller-gen:
|
||||||
ifeq (, $(shell which controller-gen))
|
ifeq (, $(shell which controller-gen))
|
||||||
|
ifeq (, $(wildcard $(GOBIN)/controller-gen))
|
||||||
@{ \
|
@{ \
|
||||||
set -e ;\
|
set -e ;\
|
||||||
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
|
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
|
||||||
@@ -113,7 +135,25 @@ ifeq (, $(shell which controller-gen))
|
|||||||
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.3.0 ;\
|
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.3.0 ;\
|
||||||
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
|
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
|
||||||
}
|
}
|
||||||
|
endif
|
||||||
CONTROLLER_GEN=$(GOBIN)/controller-gen
|
CONTROLLER_GEN=$(GOBIN)/controller-gen
|
||||||
else
|
else
|
||||||
CONTROLLER_GEN=$(shell which controller-gen)
|
CONTROLLER_GEN=$(shell which controller-gen)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# find or download yq
|
||||||
|
# download yq if necessary
|
||||||
|
# Use always go-version to get consistent line wraps etc.
|
||||||
|
yq:
|
||||||
|
ifeq (, $(wildcard $(GOBIN)/yq))
|
||||||
|
echo "Downloading yq"
|
||||||
|
@{ \
|
||||||
|
set -e ;\
|
||||||
|
YQ_TMP_DIR=$$(mktemp -d) ;\
|
||||||
|
cd $$YQ_TMP_DIR ;\
|
||||||
|
go mod init tmp ;\
|
||||||
|
go get github.com/mikefarah/yq/v3@3.4.0 ;\
|
||||||
|
rm -rf $$YQ_TMP_DIR ;\
|
||||||
|
}
|
||||||
|
endif
|
||||||
|
YQ=$(GOBIN)/yq
|
||||||
|
|||||||
44
README.md
44
README.md
@@ -250,6 +250,27 @@ spec:
|
|||||||
repositoryNames:
|
repositoryNames:
|
||||||
- summerwind/actions-runner-controller
|
- summerwind/actions-runner-controller
|
||||||
```
|
```
|
||||||
|
## Runner with DinD
|
||||||
|
|
||||||
|
When using default runner, runner pod starts up 2 containers: runner and DinD (Docker-in-Docker). This might create issues if there's `LimitRange` set to namespace.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# dindrunnerdeployment.yaml
|
||||||
|
apiVersion: actions.summerwind.dev/v1alpha1
|
||||||
|
kind: RunnerDeployment
|
||||||
|
metadata:
|
||||||
|
name: example-dindrunnerdeploy
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
image: summerwind/actions-runner-dind
|
||||||
|
dockerdWithinRunnerContainer: true
|
||||||
|
repository: mumoshu/actions-runner-controller-ci
|
||||||
|
env: []
|
||||||
|
```
|
||||||
|
|
||||||
|
This also helps with resources, as you don't need to give resources separately to docker and runner.
|
||||||
|
|
||||||
## Additional tweaks
|
## Additional tweaks
|
||||||
|
|
||||||
@@ -283,6 +304,17 @@ spec:
|
|||||||
requests:
|
requests:
|
||||||
cpu: "2.0"
|
cpu: "2.0"
|
||||||
memory: "4Gi"
|
memory: "4Gi"
|
||||||
|
# If set to true, runner pod container only 1 container that's expected to be able to run docker, too.
|
||||||
|
# image summerwind/actions-runner-dind or custom one should be used with true -value
|
||||||
|
dockerdWithinRunnerContainer: false
|
||||||
|
# Valid if dockerdWithinRunnerContainer is not true
|
||||||
|
dockerdContainerResources:
|
||||||
|
limits:
|
||||||
|
cpu: "4.0"
|
||||||
|
memory: "8Gi"
|
||||||
|
requests:
|
||||||
|
cpu: "2.0"
|
||||||
|
memory: "4Gi"
|
||||||
sidecarContainers:
|
sidecarContainers:
|
||||||
- name: mysql
|
- name: mysql
|
||||||
image: mysql:5.7
|
image: mysql:5.7
|
||||||
@@ -364,3 +396,15 @@ spec:
|
|||||||
repository: summerwind/actions-runner-controller
|
repository: summerwind/actions-runner-controller
|
||||||
image: YOUR_CUSTOM_DOCKER_IMAGE
|
image: YOUR_CUSTOM_DOCKER_IMAGE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Alternatives
|
||||||
|
|
||||||
|
The following is a list of alternative solutions that may better fit you depending on your use-case:
|
||||||
|
|
||||||
|
- https://github.com/evryfs/github-actions-runner-operator/
|
||||||
|
|
||||||
|
Although the situation can change over time, as of writing this sentence, the benefits of using `actions-runner-controller` over the alternatives are:
|
||||||
|
|
||||||
|
- `actions-runner-controller` has the ability to autoscale runners based on number of pending/progressing jobs (#99)
|
||||||
|
- `actions-runner-controller` is able to gracefully stop runners (#103)
|
||||||
|
- `actions-runner-controller` has ARM support
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ type RunnerSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Containers []corev1.Container `json:"containers,omitempty"`
|
Containers []corev1.Container `json:"containers,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
|
DockerdContainerResources corev1.ResourceRequirements `json:"dockerdContainerResources,omitempty"`
|
||||||
|
// +optional
|
||||||
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
|
VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty"`
|
||||||
@@ -77,6 +79,8 @@ type RunnerSpec struct {
|
|||||||
EphemeralContainers []corev1.EphemeralContainer `json:"ephemeralContainers,omitempty"`
|
EphemeralContainers []corev1.EphemeralContainer `json:"ephemeralContainers,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
|
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
|
||||||
|
// +optional
|
||||||
|
DockerdWithinRunnerContainer *bool `json:"dockerdWithinRunnerContainer,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRepository validates repository field.
|
// ValidateRepository validates repository field.
|
||||||
|
|||||||
@@ -435,6 +435,7 @@ func (in *RunnerSpec) DeepCopyInto(out *RunnerSpec) {
|
|||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
in.DockerdContainerResources.DeepCopyInto(&out.DockerdContainerResources)
|
||||||
in.Resources.DeepCopyInto(&out.Resources)
|
in.Resources.DeepCopyInto(&out.Resources)
|
||||||
if in.VolumeMounts != nil {
|
if in.VolumeMounts != nil {
|
||||||
in, out := &in.VolumeMounts, &out.VolumeMounts
|
in, out := &in.VolumeMounts, &out.VolumeMounts
|
||||||
@@ -524,6 +525,11 @@ func (in *RunnerSpec) DeepCopyInto(out *RunnerSpec) {
|
|||||||
*out = new(int64)
|
*out = new(int64)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.DockerdWithinRunnerContainer != nil {
|
||||||
|
in, out := &in.DockerdWithinRunnerContainer, &out.DockerdWithinRunnerContainer
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunnerSpec.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -167,7 +167,11 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
|||||||
r.Recorder.Event(&runner, corev1.EventTypeNormal, "PodCreated", fmt.Sprintf("Created pod '%s'", newPod.Name))
|
r.Recorder.Event(&runner, corev1.EventTypeNormal, "PodCreated", fmt.Sprintf("Created pod '%s'", newPod.Name))
|
||||||
log.Info("Created runner pod", "repository", runner.Spec.Repository)
|
log.Info("Created runner pod", "repository", runner.Spec.Repository)
|
||||||
} else {
|
} else {
|
||||||
if runner.Status.Phase != string(pod.Status.Phase) {
|
// If pod has ended up succeeded we need to restart it
|
||||||
|
// Happens e.g. when dind is in runner and run completes
|
||||||
|
restart := pod.Status.Phase == corev1.PodSucceeded
|
||||||
|
|
||||||
|
if !restart && runner.Status.Phase != string(pod.Status.Phase) {
|
||||||
updated := runner.DeepCopy()
|
updated := runner.DeepCopy()
|
||||||
updated.Status.Phase = string(pod.Status.Phase)
|
updated.Status.Phase = string(pod.Status.Phase)
|
||||||
updated.Status.Reason = pod.Status.Reason
|
updated.Status.Reason = pod.Status.Reason
|
||||||
@@ -185,8 +189,6 @@ func (r *RunnerReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
|||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
restart := false
|
|
||||||
|
|
||||||
if pod.Status.Phase == corev1.PodRunning {
|
if pod.Status.Phase == corev1.PodRunning {
|
||||||
for _, status := range pod.Status.ContainerStatuses {
|
for _, status := range pod.Status.ContainerStatuses {
|
||||||
if status.Name != containerName {
|
if status.Name != containerName {
|
||||||
@@ -276,8 +278,8 @@ func (r *RunnerReconciler) unregisterRunner(ctx context.Context, org, repo, name
|
|||||||
|
|
||||||
func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) {
|
func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) {
|
||||||
var (
|
var (
|
||||||
privileged bool = true
|
privileged bool = true
|
||||||
group int64 = 0
|
dockerdInRunner bool = runner.Spec.DockerdWithinRunnerContainer != nil && *runner.Spec.DockerdWithinRunnerContainer
|
||||||
)
|
)
|
||||||
|
|
||||||
runnerImage := runner.Spec.Image
|
runnerImage := runner.Spec.Image
|
||||||
@@ -311,6 +313,10 @@ func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) {
|
|||||||
Name: "RUNNER_TOKEN",
|
Name: "RUNNER_TOKEN",
|
||||||
Value: runner.Status.Registration.Token,
|
Value: runner.Status.Registration.Token,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "DOCKERD_IN_RUNNER",
|
||||||
|
Value: fmt.Sprintf("%v", dockerdInRunner),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
env = append(env, runner.Spec.Env...)
|
env = append(env, runner.Spec.Env...)
|
||||||
@@ -330,58 +336,68 @@ func (r *RunnerReconciler) newPod(runner v1alpha1.Runner) (corev1.Pod, error) {
|
|||||||
ImagePullPolicy: runnerImagePullPolicy,
|
ImagePullPolicy: runnerImagePullPolicy,
|
||||||
Env: env,
|
Env: env,
|
||||||
EnvFrom: runner.Spec.EnvFrom,
|
EnvFrom: runner.Spec.EnvFrom,
|
||||||
VolumeMounts: []corev1.VolumeMount{
|
|
||||||
{
|
|
||||||
Name: "work",
|
|
||||||
MountPath: "/runner/_work",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "docker",
|
|
||||||
MountPath: "/var/run",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityContext: &corev1.SecurityContext{
|
SecurityContext: &corev1.SecurityContext{
|
||||||
RunAsGroup: &group,
|
// Runner need to run privileged if it contains DinD
|
||||||
|
Privileged: runner.Spec.DockerdWithinRunnerContainer,
|
||||||
},
|
},
|
||||||
Resources: runner.Spec.Resources,
|
Resources: runner.Spec.Resources,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "docker",
|
|
||||||
Image: r.DockerImage,
|
|
||||||
VolumeMounts: []corev1.VolumeMount{
|
|
||||||
{
|
|
||||||
Name: "work",
|
|
||||||
MountPath: "/runner/_work",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "docker",
|
|
||||||
MountPath: "/var/run",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SecurityContext: &corev1.SecurityContext{
|
|
||||||
Privileged: &privileged,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Volumes: []corev1.Volume{
|
|
||||||
{
|
|
||||||
Name: "work",
|
|
||||||
VolumeSource: corev1.VolumeSource{
|
|
||||||
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "docker",
|
|
||||||
VolumeSource: corev1.VolumeSource{
|
|
||||||
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !dockerdInRunner {
|
||||||
|
pod.Spec.Volumes = []corev1.Volume{
|
||||||
|
{
|
||||||
|
Name: "work",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "docker",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pod.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "work",
|
||||||
|
MountPath: "/runner/_work",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "docker",
|
||||||
|
MountPath: "/var/run",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pod.Spec.Containers = append(pod.Spec.Containers, corev1.Container{
|
||||||
|
Name: "docker",
|
||||||
|
Image: r.DockerImage,
|
||||||
|
VolumeMounts: []corev1.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "work",
|
||||||
|
MountPath: "/runner/_work",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "docker",
|
||||||
|
MountPath: "/var/run",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityContext: &corev1.SecurityContext{
|
||||||
|
Privileged: &privileged,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if len(runner.Spec.Containers) != 0 {
|
if len(runner.Spec.Containers) != 0 {
|
||||||
pod.Spec.Containers = runner.Spec.Containers
|
pod.Spec.Containers = runner.Spec.Containers
|
||||||
|
for i := 0; i < len(pod.Spec.Containers); i++ {
|
||||||
|
if pod.Spec.Containers[i].Name == containerName {
|
||||||
|
pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, env...)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(runner.Spec.VolumeMounts) != 0 {
|
if len(runner.Spec.VolumeMounts) != 0 {
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
|
|||||||
&& install -o root -g root -m 755 docker/docker /usr/local/bin/docker \
|
&& install -o root -g root -m 755 docker/docker /usr/local/bin/docker \
|
||||||
&& rm -rf docker docker.tgz \
|
&& rm -rf docker docker.tgz \
|
||||||
&& adduser --disabled-password --gecos "" --uid 1000 runner \
|
&& adduser --disabled-password --gecos "" --uid 1000 runner \
|
||||||
|
&& groupadd docker \
|
||||||
&& usermod -aG sudo runner \
|
&& usermod -aG sudo runner \
|
||||||
|
&& usermod -aG docker runner \
|
||||||
&& echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers
|
&& echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers
|
||||||
|
|
||||||
# Runner download supports amd64 as x64
|
# Runner download supports amd64 as x64
|
||||||
@@ -67,6 +69,6 @@ RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
|
|||||||
COPY entrypoint.sh /runner
|
COPY entrypoint.sh /runner
|
||||||
COPY patched /runner/patched
|
COPY patched /runner/patched
|
||||||
|
|
||||||
USER runner:runner
|
USER runner
|
||||||
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
|
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
|
||||||
CMD ["/runner/entrypoint.sh"]
|
CMD ["/runner/entrypoint.sh"]
|
||||||
|
|||||||
99
runner/Dockerfile.dindrunner
Normal file
99
runner/Dockerfile.dindrunner
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
FROM ubuntu:20.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
# Dev + DinD dependencies
|
||||||
|
RUN apt update \
|
||||||
|
&& apt install -y software-properties-common \
|
||||||
|
&& add-apt-repository -y ppa:git-core/ppa \
|
||||||
|
&& apt install -y \
|
||||||
|
build-essential \
|
||||||
|
curl \
|
||||||
|
ca-certificates \
|
||||||
|
dnsutils \
|
||||||
|
ftp \
|
||||||
|
git \
|
||||||
|
iproute2 \
|
||||||
|
iptables \
|
||||||
|
iputils-ping \
|
||||||
|
jq \
|
||||||
|
libunwind8 \
|
||||||
|
locales \
|
||||||
|
netcat \
|
||||||
|
openssh-client \
|
||||||
|
parallel \
|
||||||
|
rsync \
|
||||||
|
shellcheck \
|
||||||
|
sudo \
|
||||||
|
supervisor \
|
||||||
|
telnet \
|
||||||
|
time \
|
||||||
|
tzdata \
|
||||||
|
unzip \
|
||||||
|
upx \
|
||||||
|
wget \
|
||||||
|
zip \
|
||||||
|
zstd \
|
||||||
|
&& rm -rf /var/lib/apt/list/*
|
||||||
|
|
||||||
|
# Runner user
|
||||||
|
RUN adduser --disabled-password --gecos "" --uid 1000 runner \
|
||||||
|
&& groupadd docker \
|
||||||
|
&& usermod -aG sudo runner \
|
||||||
|
&& usermod -aG docker runner \
|
||||||
|
&& echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
ARG RUNNER_VERSION=2.272.0
|
||||||
|
ARG DOCKER_CHANNEL=stable
|
||||||
|
ARG DOCKER_VERSION=19.03.13
|
||||||
|
ARG DEBUG=false
|
||||||
|
|
||||||
|
# Docker installation
|
||||||
|
RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
|
||||||
|
&& if [ "$ARCH" = "arm64" ]; then export ARCH=aarch64 ; fi \
|
||||||
|
&& if [ "$ARCH" = "amd64" ]; then export ARCH=x86_64 ; fi \
|
||||||
|
&& if ! curl -L -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${ARCH}/docker-${DOCKER_VERSION}.tgz"; then \
|
||||||
|
echo >&2 "error: failed to download 'docker-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${ARCH}'"; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
tar --extract \
|
||||||
|
--file docker.tgz \
|
||||||
|
--strip-components 1 \
|
||||||
|
--directory /usr/local/bin/ \
|
||||||
|
; \
|
||||||
|
rm docker.tgz; \
|
||||||
|
dockerd --version; \
|
||||||
|
docker --version
|
||||||
|
|
||||||
|
# Runner download supports amd64 as x64
|
||||||
|
RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
|
||||||
|
&& if [ "$ARCH" = "amd64" ]; then export ARCH=x64 ; fi \
|
||||||
|
&& mkdir -p /runner \
|
||||||
|
&& cd /runner \
|
||||||
|
&& curl -L -o runner.tar.gz https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${ARCH}-${RUNNER_VERSION}.tar.gz \
|
||||||
|
&& tar xzf ./runner.tar.gz \
|
||||||
|
&& rm runner.tar.gz \
|
||||||
|
&& ./bin/installdependencies.sh \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
|
COPY modprobe startup.sh /usr/local/bin/
|
||||||
|
COPY supervisor/ /etc/supervisor/conf.d/
|
||||||
|
COPY logger.sh /opt/bash-utils/logger.sh
|
||||||
|
COPY entrypoint.sh /usr/local/bin/
|
||||||
|
|
||||||
|
RUN chmod +x /usr/local/bin/startup.sh /usr/local/bin/entrypoint.sh /usr/local/bin/modprobe
|
||||||
|
|
||||||
|
RUN export ARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
|
||||||
|
&& curl -L -o /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_${ARCH} \
|
||||||
|
&& chmod +x /usr/local/bin/dumb-init
|
||||||
|
|
||||||
|
VOLUME /var/lib/docker
|
||||||
|
|
||||||
|
COPY patched /runner/patched
|
||||||
|
|
||||||
|
# No group definition, as that makes it harder to run docker.
|
||||||
|
USER runner
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/dumb-init", "--"]
|
||||||
|
CMD ["startup.sh"]
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
NAME ?= summerwind/actions-runner
|
NAME ?= summerwind/actions-runner
|
||||||
|
DIND_RUNNER_NAME ?= ${NAME}-dind
|
||||||
TAG ?= latest
|
TAG ?= latest
|
||||||
|
|
||||||
RUNNER_VERSION ?= 2.273.5
|
RUNNER_VERSION ?= 2.273.5
|
||||||
@@ -23,10 +24,14 @@ endif
|
|||||||
|
|
||||||
docker-build:
|
docker-build:
|
||||||
docker build --build-arg RUNNER_VERSION=${RUNNER_VERSION} --build-arg DOCKER_VERSION=${DOCKER_VERSION} -t ${NAME}:${TAG} -t ${NAME}:v${RUNNER_VERSION} .
|
docker build --build-arg RUNNER_VERSION=${RUNNER_VERSION} --build-arg DOCKER_VERSION=${DOCKER_VERSION} -t ${NAME}:${TAG} -t ${NAME}:v${RUNNER_VERSION} .
|
||||||
|
docker build --build-arg RUNNER_VERSION=${RUNNER_VERSION} --build-arg DOCKER_VERSION=${DOCKER_VERSION} -t ${DIND_RUNNER_NAME}:${TAG} -t ${DIND_RUNNER_NAME}:v${RUNNER_VERSION} -f Dockerfile.dindrunner .
|
||||||
|
|
||||||
|
|
||||||
docker-push:
|
docker-push:
|
||||||
docker push ${NAME}:${TAG}
|
docker push ${NAME}:${TAG}
|
||||||
docker push ${NAME}:v${RUNNER_VERSION}
|
docker push ${NAME}:v${RUNNER_VERSION}
|
||||||
|
docker push ${DIND_RUNNER_NAME}:${TAG}
|
||||||
|
docker push ${DIND_RUNNER_NAME}:v${RUNNER_VERSION}
|
||||||
|
|
||||||
docker-buildx:
|
docker-buildx:
|
||||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||||
@@ -39,3 +44,9 @@ docker-buildx:
|
|||||||
-t "${NAME}:latest" \
|
-t "${NAME}:latest" \
|
||||||
-f Dockerfile \
|
-f Dockerfile \
|
||||||
. ${PUSH_ARG}
|
. ${PUSH_ARG}
|
||||||
|
docker buildx build --platform ${PLATFORMS} \
|
||||||
|
--build-arg RUNNER_VERSION=${RUNNER_VERSION} \
|
||||||
|
--build-arg DOCKER_VERSION=${DOCKER_VERSION} \
|
||||||
|
-t "${DIND_RUNNER_NAME}:latest" \
|
||||||
|
-f Dockerfile.dindrunner \
|
||||||
|
. ${PUSH_ARG}
|
||||||
|
|||||||
24
runner/logger.sh
Normal file
24
runner/logger.sh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Logger from this post http://www.cubicrace.com/2016/03/log-tracing-mechnism-for-shell-scripts.html
|
||||||
|
|
||||||
|
function INFO(){
|
||||||
|
local function_name="${FUNCNAME[1]}"
|
||||||
|
local msg="$1"
|
||||||
|
timeAndDate=`date`
|
||||||
|
echo "[$timeAndDate] [INFO] [${0}] $msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function DEBUG(){
|
||||||
|
local function_name="${FUNCNAME[1]}"
|
||||||
|
local msg="$1"
|
||||||
|
timeAndDate=`date`
|
||||||
|
echo "[$timeAndDate] [DEBUG] [${0}] $msg"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ERROR(){
|
||||||
|
local function_name="${FUNCNAME[1]}"
|
||||||
|
local msg="$1"
|
||||||
|
timeAndDate=`date`
|
||||||
|
echo "[$timeAndDate] [ERROR] $msg"
|
||||||
|
}
|
||||||
20
runner/modprobe
Normal file
20
runner/modprobe
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# "modprobe" without modprobe
|
||||||
|
# https://twitter.com/lucabruno/status/902934379835662336
|
||||||
|
|
||||||
|
# this isn't 100% fool-proof, but it'll have a much higher success rate than simply using the "real" modprobe
|
||||||
|
|
||||||
|
# Docker often uses "modprobe -va foo bar baz"
|
||||||
|
# so we ignore modules that start with "-"
|
||||||
|
for module; do
|
||||||
|
if [ "${module#-}" = "$module" ]; then
|
||||||
|
ip link show "$module" || true
|
||||||
|
lsmod | grep "$module" || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# remove /usr/local/... from PATH so we can exec the real modprobe as a last resort
|
||||||
|
export PATH='/usr/sbin:/usr/bin:/sbin:/bin'
|
||||||
|
exec modprobe "$@"
|
||||||
37
runner/startup.sh
Normal file
37
runner/startup.sh
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
source /opt/bash-utils/logger.sh
|
||||||
|
|
||||||
|
function wait_for_process () {
|
||||||
|
local max_time_wait=30
|
||||||
|
local process_name="$1"
|
||||||
|
local waited_sec=0
|
||||||
|
while ! pgrep "$process_name" >/dev/null && ((waited_sec < max_time_wait)); do
|
||||||
|
INFO "Process $process_name is not running yet. Retrying in 1 seconds"
|
||||||
|
INFO "Waited $waited_sec seconds of $max_time_wait seconds"
|
||||||
|
sleep 1
|
||||||
|
((waited_sec=waited_sec+1))
|
||||||
|
if ((waited_sec >= max_time_wait)); then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO "Starting supervisor"
|
||||||
|
sudo /usr/bin/supervisord -n >> /dev/null 2>&1 &
|
||||||
|
|
||||||
|
INFO "Waiting for processes to be running"
|
||||||
|
processes=(dockerd)
|
||||||
|
|
||||||
|
for process in "${processes[@]}"; do
|
||||||
|
wait_for_process "$process"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
ERROR "$process is not running after max time"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
INFO "$process is running"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Wait processes to be running
|
||||||
|
entrypoint.sh
|
||||||
6
runner/supervisor/dockerd.conf
Normal file
6
runner/supervisor/dockerd.conf
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[program:dockerd]
|
||||||
|
command=/usr/local/bin/dockerd
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stderr_logfile=/var/log/dockerd.err.log
|
||||||
|
stdout_logfile=/var/log/dockerd.out.log
|
||||||
Reference in New Issue
Block a user