Kubernetes PodSpec
Using the kubernetes plugin allows you to specify a PodSpec Kubernetes API resource that will be used in a Kubernetes Job.
Kubernetes PodSpec generation
The Agent Stack for Kubernetes controller allows you to define some or all of the Kubernetes PodSpec from the following locations:
- Controller configuration:
pod-spec-patch. - Buildkite job, using the
kubernetesplugin:podSpec,podSpecPatch.
With multiple PodSpec inputs provided, here is how the Agent Stack for Kubernetes controller generates a Kubernetes PodSpec:
-
Create a simple
PodSpeccontaining a single container with theImagedefined in the controller's configuration and the value of the Buildkite job's command (BUILDKITE_COMMAND).If the
kubernetesplugin is present in the Buildkite job's plugins and contains apodSpec, use this as the startingPodSpecinstead. Apply the
/workspaceVolume.Apply any
extra-volume-mountsdefined by thekubernetesplugin.Modify any
containersdefined by thekubernetesplugin, overriding thecommandandargs.Add the
agentcontainer to thePodSpec.Add the
checkoutcontainer to thePodSpec(ifskip.checkoutis set tofalse).Add
initcontainers for theimagecheck-#containers, based on the number of unique images defined in thePodSpec.Apply
pod-spec-patchfrom the controller's configuration, using a strategic merge patch in the controller.Apply
podSpecPatchfrom thekubernetesplugin, using a strategic merge patch in the controller.Ensure the a
checkoutcontainer in not present after applying patching viapod-spec-patch,podSpecPatch(ifskip.checkoutis set totrue).Remove any duplicate
VolumeMountspresent inPodSpecafter patching.Create a Kubernetes Job with the final
PodSpec.
PodSpec command and interpretation of arguments
In a podSpec, command must be a list of strings, since it is defined by Kubernetes. However, the Buildkite Agent Stack for Kubernetes controller runs the Buildkite Agent instead of the container's default entrypoint.
To run a command, the controller must re-interpret command into input for the Buildkite Agent. By default, the controller treats command as a sequence of multiple commands, similar to steps and commands in a pipeline.yaml file which is different to the interpretation of command (as an entrypoint vector run without a shell as a single command) in Kubernetes.
This interposer behavior can be changed using commandParams/interposer, which can have one of the following values:
-
buildkiteis the default, in which the Agent Stack for Kubernetes controller treatscommandas a sequence of multiple commands, andargsas extra arguments added to the end of the last command, which are then typically interpreted by the shell. -
vectoremulates the Kubernetes' interpretation in whichcommandandargsspecify components of a single command intended to be run directly. -
legacyis the behavior of the Agent Stack for Kubernetes controller version 0.14.0 and earlier, wherecommandandargsare joined directly into a single command with spaces.
An example using buildkite interposer behavior:
steps:
- label: Hello World!
agents:
queue: kubernetes
plugins:
- kubernetes:
commandParams:
interposer: buildkite # This is the default, and can be omitted
podSpec:
containers:
- image: alpine:latest
command:
- set -euo pipefail
- |- # <-- YAML block scalars work too
echo Hello World! > hello.txt
cat hello.txt | buildkite-agent annotate
If you have a multi-line command, specifying the args could lead to confusion. Therefore, it is recommended to just use command.
An example using vector interposer behavior:
steps:
- label: Hello World!
agents:
queue: kubernetes
plugins:
- kubernetes:
commandParams:
interposer: vector
podSpec:
containers:
- image: alpine:latest
command: ['sh']
args:
- '-c'
- |-
set -eu
echo Hello World! > hello.txt
cat hello.txt | buildkite-agent annotate
Custom images
In version 0.30.0 and later of the Agent Stack for Kubernetes controller, you can use the image attribute in a command step to specify a container image for the step's job.
Almost any container image may be used, but the image must have a POSIX shell available to be executed at /bin/sh.
# pipeline.yaml
steps:
- name: Hello World!
image: "alpine:latest" # <- New in v0.30.0
commands:
- echo -n Hello!
For versions of the prior to 0.30.0, you can specify a different image to use for a step in a step level podSpecPatch. Previously this could be done with a step level podSpec.
# pipeline.yaml
agents:
queue: kubernetes
steps:
- name: Hello World!
commands:
- echo -n Hello!
- echo " World!"
plugins:
- kubernetes:
podSpecPatch:
containers:
- name: container-0
image: alpine:latest
- name: Hello World from alpine!
commands:
- echo -n Hello
- echo " from alpine!"
plugins:
- kubernetes:
podSpecPatch:
containers:
- name: container-0 # <-- For the time being, specify this exactly as `container-0`.
image: alpine:latest # Currently under experimentation to make this more ergonomic.
Environment variables precedence
During its bootstrap phase, the Buildkite Agent receives some of its environment variables from the Buildkite platform. These environment variables are normally set using the env keyword in pipeline.yaml file.
During the generation of the Kubernetes podSpec, the podSpec receives some of its environment variables from the Agent Stack for Kubernetes controller itself, some controller-specific environment variables defined in the values.yaml file, as well as environment variables that can be set in various podSpec configuration steps of the pipeline.yaml file.
Be aware that currently, environment variables defined as part of a podSpec take higher precedence over environment variables set using the env keyword in the pipeline.yaml file.
If you have a need for a more flexible environment variable setup, use Agent hooks to implement a precedence rule suite to your organization.