|
| 1 | +--- |
| 2 | +title: "Shipwright build scheduler features" |
| 3 | +date: 2025-05-20T11:39:00-06:00 |
| 4 | +draft: false |
| 5 | +author: "Dylan Orzel ([@dorzel](https://github.com/dorzel))" |
| 6 | +--- |
| 7 | + |
| 8 | +## New Build Scheduler Features |
| 9 | + |
| 10 | +A new set of build scheduling features introduced in [v0.15](https://shipwright.io/docs/blog/posts/2025-02-28-release-v0.15) allows users to specify node selectors, custom schedulers, and tolerations for builds. |
| 11 | + |
| 12 | +These make it easier to schedule builds on clusters with nodes of multiple CPU architectures, use a scheduler that is tuned to a certain workflow, or just more general control of which nodes builds run on. |
| 13 | + |
| 14 | +### CLI flags to use build scheduler features |
| 15 | + |
| 16 | +With these features also comes new CLI flags (introduced in v0.16) that allow specifying nodeSelectors and custom schedulers on the command line when using `shp` with `Builds` or `BuildRuns`. |
| 17 | + |
| 18 | +In the following commands, `--node-selector` and `--scheduler-name` sets these fields on the `Build` or `BuildRun` objects: |
| 19 | + |
| 20 | +- `shp build create` |
| 21 | +- `shp build run` |
| 22 | +- `shp build upload` |
| 23 | +- `shp buildrun create` |
| 24 | + |
| 25 | +### Example with `shp build run` and `--node-selector` |
| 26 | + |
| 27 | +We can specify a build to be scheduled to a node with certain labels by using a node selector. Here, we'll schedule a build to a node with an ARM CPU architecture. Starting with an example build: |
| 28 | + |
| 29 | +```bash |
| 30 | +$ shp build create test-golang-build \ |
| 31 | + --output-image=kind.local/test/test-golang-build \ |
| 32 | + --source-git-url=https://github.com/shipwright-io/sample-go \ |
| 33 | + --source-context-dir=docker-build \ |
| 34 | + --strategy-name=buildah-shipwright-managed-push |
| 35 | +``` |
| 36 | + |
| 37 | +We'll run the build and specify the arm64 architecture in the node selector flag: |
| 38 | + |
| 39 | +```bash |
| 40 | +$ shp build run test-golang-build --node-selector=kubernetes.io/arch=arm64 |
| 41 | +``` |
| 42 | + |
| 43 | +and see that the created BuildRun now has a nodeSelector specified and has been scheduled on the arm64 node: |
| 44 | + |
| 45 | +```yaml |
| 46 | +apiVersion: shipwright.io/v1beta1 |
| 47 | +kind: BuildRun |
| 48 | +metadata: |
| 49 | + creationTimestamp: "2025-05-20T17:27:01Z" |
| 50 | + generateName: test-golang-build- |
| 51 | + generation: 1 |
| 52 | + labels: |
| 53 | + build.shipwright.io/generation: "1" |
| 54 | + build.shipwright.io/name: test-golang-build |
| 55 | + name: test-golang-build-glghq |
| 56 | + namespace: default |
| 57 | + resourceVersion: "31512392" |
| 58 | + uid: 19f02c99-1d24-44c0-a5f9-ab544fdf55ae |
| 59 | +spec: |
| 60 | + build: |
| 61 | + name: test-golang-build |
| 62 | + nodeSelector: |
| 63 | + kubernetes.io/arch: arm64 |
| 64 | +``` |
| 65 | +
|
| 66 | +```bash |
| 67 | +$ kubectl get pod test-golang-build-glghq-tfhnn-pod -o jsonpath='{.spec.nodeName} {.spec.nodeSelector}' |
| 68 | + |
| 69 | +arm-node.compute.internal {"kubernetes.io/arch":"arm64"} |
| 70 | + |
| 71 | +$ kubectl get node arm-node.compute.internal -o jsonpath='{.metadata.labels.kubernetes\.io/arch}' |
| 72 | + |
| 73 | +arm64 |
| 74 | +``` |
| 75 | + |
| 76 | +### Example with `shp build create` and `--scheduler-name` |
| 77 | + |
| 78 | +This time we'll specify a scheduler name `test-scheduler` that is assumed to be a custom scheduler that has been deployed to the cluster already. |
| 79 | + |
| 80 | +Instead of specifying these options when running the build, we can also specify them when creating the build: |
| 81 | + |
| 82 | +```bash |
| 83 | +$ shp build create test-golang-build \ |
| 84 | + --output-image=kind.local/test/test-golang-build \ |
| 85 | + --source-git-url=https://github.com/shipwright-io/sample-go \ |
| 86 | + --source-context-dir=docker-build \ |
| 87 | + --strategy-name=buildah-shipwright-managed-push \ |
| 88 | + --scheduler-name=test-scheduler |
| 89 | +``` |
| 90 | + |
| 91 | +and see that the scheduler name appears in the Build yaml: |
| 92 | + |
| 93 | +```yaml |
| 94 | +apiVersion: shipwright.io/v1beta1 |
| 95 | +kind: Build |
| 96 | +metadata: |
| 97 | + creationTimestamp: "2025-05-20T17:49:07Z" |
| 98 | + generation: 1 |
| 99 | + name: test-golang-build |
| 100 | + namespace: default |
| 101 | + resourceVersion: "31518385" |
| 102 | + uid: bd2333fb-71eb-4228-bc5f-5a466032fcc5 |
| 103 | +spec: |
| 104 | + output: |
| 105 | + image: kind.local/test/test-golang-build |
| 106 | + schedulerName: test-scheduler |
| 107 | + source: |
| 108 | + contextDir: docker-build |
| 109 | + git: |
| 110 | + url: https://github.com/shipwright-io/sample-go |
| 111 | + type: Git |
| 112 | + strategy: |
| 113 | + kind: ClusterBuildStrategy |
| 114 | + name: buildah-shipwright-managed-push |
| 115 | +status: |
| 116 | + message: all validations succeeded |
| 117 | + reason: Succeeded |
| 118 | + registered: "True" |
| 119 | +``` |
| 120 | +
|
| 121 | +After running the build with `shp build run test-golang-build`, we see that the schedulerName got picked up by the build pod: |
| 122 | + |
| 123 | +```bash |
| 124 | +$ kubectl get pods test-golang-build-j9vbd-qr9cr-pod -o jsonpath='{.spec.schedulerName}' |
| 125 | +
|
| 126 | +test-scheduler |
| 127 | +``` |
| 128 | + |
| 129 | +### Example with setting tolerations on a `BuildRun` |
| 130 | + |
| 131 | +We'll start with the same example build as above: |
| 132 | + |
| 133 | +```bash |
| 134 | +$ shp build create test-golang-build \ |
| 135 | + --output-image=kind.local/test/test-golang-build \ |
| 136 | + --source-git-url=https://github.com/shipwright-io/sample-go \ |
| 137 | + --source-context-dir=docker-build \ |
| 138 | + --strategy-name=buildah-shipwright-managed-push |
| 139 | +``` |
| 140 | + |
| 141 | +```yaml |
| 142 | +apiVersion: shipwright.io/v1beta1 |
| 143 | +kind: Build |
| 144 | +metadata: |
| 145 | + creationTimestamp: "2025-05-20T18:29:43Z" |
| 146 | + generation: 1 |
| 147 | + name: test-golang-build |
| 148 | + namespace: default |
| 149 | + resourceVersion: "31529978" |
| 150 | + uid: f393de53-e389-4e25-a29a-a434505bd82e |
| 151 | +spec: |
| 152 | + output: |
| 153 | + image: kind.local/test/test-golang-build |
| 154 | + source: |
| 155 | + contextDir: docker-build |
| 156 | + git: |
| 157 | + url: https://github.com/shipwright-io/sample-go |
| 158 | + type: Git |
| 159 | + strategy: |
| 160 | + kind: ClusterBuildStrategy |
| 161 | + name: buildah-shipwright-managed-push |
| 162 | +``` |
| 163 | + |
| 164 | +In this example we have a three node cluster, so let's taint all of the nodes with `test-key` and `test-value`. This will prevent any pod from scheduling on these nodes unless it tolerates this taint: |
| 165 | + |
| 166 | +```bash |
| 167 | +$ kubectl get nodes -o name |
| 168 | +node/test-node-1 |
| 169 | +node/test-node-2 |
| 170 | +node/test-node-3 |
| 171 | +
|
| 172 | +$ kubectl taint nodes test-node-1 test-key=test-value:NoSchedule |
| 173 | +node/test-node-1 tainted |
| 174 | +$ kubectl taint nodes test-node-2 test-key=test-value:NoSchedule |
| 175 | +node/test-node-2 tainted |
| 176 | +$ kubectl taint nodes test-node-3 test-key=test-value:NoSchedule |
| 177 | +node/test-node-3 tainted |
| 178 | +``` |
| 179 | + |
| 180 | +We see that if we create a BuildRun now, it will fail to schedule: |
| 181 | + |
| 182 | +```bash |
| 183 | +$ shp build run test-golang-build |
| 184 | +
|
| 185 | +$ kubectl get events |
| 186 | +LAST SEEN TYPE REASON OBJECT MESSAGE |
| 187 | +... |
| 188 | +2m54s Normal Pending taskrun/test-golang-build-bqw9s-hbkds pod status "PodScheduled":"False"; message: "0/3 nodes are available: 3 node(s) had untolerated taint {test-key: test-value}. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling." |
| 189 | +... |
| 190 | +``` |
| 191 | + |
| 192 | +Let's patch the Build with the following toleration in order to have it tolerate the node taint: |
| 193 | + |
| 194 | +```yaml |
| 195 | +tolerations: |
| 196 | + - key: "test-key" |
| 197 | + operator: "Equal" |
| 198 | + value: "test-value" |
| 199 | + effect: "NoSchedule" |
| 200 | +``` |
| 201 | + |
| 202 | +```bash |
| 203 | +$ kubectl patch Build test-golang-build --type=merge -p '{"spec":{"tolerations":[{"key":"test-key","operator":"Equal","value":"test-value"}]}}' |
| 204 | +
|
| 205 | +$ shp build run test-golang-build |
| 206 | +``` |
| 207 | + |
| 208 | +Now we see that the build is successfully scheduled: |
| 209 | + |
| 210 | +```bash |
| 211 | +$ kubectl get events |
| 212 | +LAST SEEN TYPE REASON OBJECT MESSAGE |
| 213 | +... |
| 214 | +109s Normal Scheduled pod/test-golang-build-k7zc4-492d2-pod Successfully assigned default/test-golang-build-k7zc4-492d2-pod to ip-10-0-2-69.us-east-2.compute.internal |
| 215 | +... |
| 216 | +``` |
| 217 | + |
| 218 | +### Conclusion |
| 219 | + |
| 220 | +Shipwright's new build scheduling options make it much easier to control where builds get placed in the cluster and give more options to those who are operating in multi-arch environments or with other constraints. |
0 commit comments