Skip to content

feat: add provisioner chart to release and docs #9050

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ jobs:
build/coder_"$version"_linux_{amd64,armv7,arm64}.{tar.gz,apk,deb,rpm} \
build/coder_"$version"_{darwin,windows}_{amd64,arm64}.zip \
build/coder_"$version"_windows_amd64_installer.exe \
build/coder_helm_"$version".tgz
build/coder_helm_"$version".tgz \
build/provisioner_helm_"$version".tgz
env:
CODER_SIGN_DARWIN: "1"
AC_CERTIFICATE_FILE: /tmp/apple_cert.p12
Expand Down Expand Up @@ -295,9 +296,11 @@ jobs:
version="$(./scripts/version.sh)"
mkdir -p build/helm
cp "build/coder_helm_${version}.tgz" build/helm
cp "build/provisioner_helm_${version}.tgz" build/helm
gsutil cp gs://helm.coder.com/v2/index.yaml build/helm/index.yaml
helm repo index build/helm --url https://helm.coder.com/v2 --merge build/helm/index.yaml
gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/coder_helm_${version}.tgz gs://helm.coder.com/v2
gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/provisioner_helm_${version}.tgz gs://helm.coder.com/v2
gsutil -h "Cache-Control:no-cache,max-age=0" cp build/helm/index.yaml gs://helm.coder.com/v2
gsutil -h "Cache-Control:no-cache,max-age=0" cp helm/artifacthub-repo.yml gs://helm.coder.com/v2

Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,19 @@ push/$(CODER_MAIN_IMAGE): $(CODER_MAIN_IMAGE)
docker manifest push "$$image_tag"
.PHONY: push/$(CODER_MAIN_IMAGE)

# Helm charts that are available
charts = coder provisioner

# Shortcut for Helm chart package.
build/coder_helm.tgz: build/coder_helm_$(VERSION).tgz
$(foreach chart,$(charts),build/$(chart)_helm.tgz): build/%_helm.tgz: build/%_helm_$(VERSION).tgz
rm -f "$@"
ln "$<" "$@"

# Helm chart package.
build/coder_helm_$(VERSION).tgz:
$(foreach chart,$(charts),build/$(chart)_helm_$(VERSION).tgz): build/%_helm_$(VERSION).tgz:
./scripts/helm.sh \
--version "$(VERSION)" \
--chart $* \
--output "$@"

site/out/index.html: site/package.json $(shell find ./site $(FIND_EXCLUSIONS) -type f \( -name '*.ts' -o -name '*.tsx' \))
Expand Down
84 changes: 73 additions & 11 deletions docs/admin/provisioners.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ By default, the Coder server runs [built-in provisioner daemons](../cli/server.m

- **Reduce server load**: External provisioners reduce load and build queue times from the Coder server. See [Scaling Coder](./scale.md#concurrent-workspace-builds) for more details.

> External provisioners are in an [alpha state](../contributing/feature-stages.md#alpha-features) and the behavior is subject to change. Use [GitHub issues](https://github.com/coder/coder) to leave feedback.

## Running external provisioners

Each provisioner can run a single [concurrent workspace build](./scale.md#concurrent-workspace-builds). For example, running 30 provisioner containers will allow 30 users to start workspaces at the same time.

Provisioners are started with the [coder provisionerd start](../cli/provisionerd_start.md) command.

### Authentication
## Authentication

The provisioner daemon must authenticate with your Coder deployment.

The provisioner server must authenticate with your Coder deployment. There are two authentication methods:
Set a [provisioner daemon pre-shared key (PSK)](../cli/server.md#--provisioner-daemon-psk) on the Coder server and start the provisioner with
`coder provisionerd start --psk <your-psk>`. If you are [installing with Helm](../install/kubernetes#install-coder-with-helm),
see the [Helm example](#example-running-an-external-provisioner-with-helm) below.

- PSK: Set a [provisioner daemon PSK](../cli/server#--provisioner-daemon-psk) on the Coder server and start the provisioner with `coder provisionerd start --psk <your-psk>`
- User token: [Authenticate](../cli.md#--token) the Coder CLI as a user with the Template Admin or Owner role.
> Coder still supports authenticating the provisioner daemon with a [token](../cli.md#--token) from a user with the
> Template Admin or Owner role. This method is deprecated in favor of the PSK, which only has permission to access
> provisioner daemon APIs. We recommend migrating to the PSK as soon as practical.
Comment on lines +26 to +27
Copy link
Member

@bpmct bpmct Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we supported API token scopes, would you be open to us supporting both, or are there other reasons for deprecating user token auth?

Something I like about relying on user authentication is that this token could be rotated without restarting the Coder server and #7992 could be used down the road to limit specific user tokens to specific provisioners versus full API access.

It does feel a little odd passing CODER_SESSION_TOKEN to the provisioner, especially if it is a long-lived token, but that could be said about any automation somebody is trying to do with Coder.

I know it also requires that you create a token prior to starting a provisioner and you can only create a token on behalf of another user (machine account) via the REST API which is clunky.

Perhaps, for now, we mention the drawbacks of the token admin auth and avoid "deprecation" language and then we can support it again as a first-class citizen when we add scopes to API tokens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to keep supporting user token auth anyway because of local provisioners.

I agree that we'll want to add support for tightly-scoped credentials that can be rotated without restarting Coderd in the future. At present I'm agnostic about whether they are API tokens or some other kind of credential (as a specific example: a JWT signed by an external authority). We should speak with our large customers to get their take on how they'd like to do this auth.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should speak with our large customers to get their take on how they'd like to do this auth.

Bet you a 🌮 someone is going to ask for Azure Workload Identity


### Types of provisioners
## Types of provisioners

- **Generic provisioners** can pick up any build job from templates without provisioner tags.

Expand Down Expand Up @@ -65,7 +66,68 @@ The provisioner server must authenticate with your Coder deployment. There are t
--provisioner-tag scope=user
```

### Example: Running an external provisioner on a VM
## Example: Running an external provisioner with Helm

Coder provides a Helm chart for running external provisioner daemons, which you will use in concert with the Helm chart
for deploying the Coder server.

1. Create a long, random pre-shared key (PSK) and store it in a Kubernetes secret

```shell
kubectl create secret generic coder-provisioner-psk --from-literal=psk=`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 26`
```

1. Modify your Coder `values.yaml` to include

```yaml
provisionerDaemon:
pskSecretName: "coder-provisioner-psk"
```

1. Redeploy Coder with the new `values.yaml` to roll out the PSK. You can omit `--version <your version>` to also upgrade
Coder to the latest version.

```shell
helm upgrade coder coder-v2/coder \
--namespace coder \
--version <your version> \
--values values.yaml
```

1. Create a `provisioner-values.yaml` file for the provisioner daemons Helm chart. For example

```yaml
coder:
env:
- name: CODER_URL
value: "https://coder.example.com"
replicaCount: 10
provisionerDaemon:
pskSecretName: "coder-provisioner-psk"
tags:
location: auh
kind: k8s
Comment on lines +105 to +109
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit confusing to me how the provisioner chart and the Coder chart seemingly accept the same Helm values format despite being different charts. If I am understanding correctly, when:

provisionerDaemon:
 pskSecretName: "coder-provisioner-psk"

is set for the server values, it will set CODER_PROVISIONER_DAEMON_PSK for the coder server. I see that code here.

However, when the same values file is used set for the provisioner server, it passes the PSK to the provisioner.

Is this a normal practice for Helm charts? If so, I'm on board. I also wonder, in a follow-up PR, if we should rewrite our workspace proxies Helm to do something similar.

Our workspace proxies Helm is currently on the Coder chart and when enabled, Helm changes the entrypoint/command to start a wsproxy server instead of a full server. Our current wsproxy approach is easier for me to wrap my head around, but I can see how it may not be a best practice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The values.yaml between the charts is very similar, but there are a number of differences. For example, the provisioner chart doesn't accept TLS certificates (because it doesn't serve traffic), doesn't include the service block, etc.

I kept the pskSecretName value the same, since from a user's perspective, it's the same thing: the name of the K8s secret with the PSK. It's true that the two charts are doing slightly different things with the same piece of information.

I think having different charts for coderd and provisionerd is conceptually easier to understand, rather than a single massive chart with lots of values that don't make sense when running provisioners. Workspace proxies are conceptually much more similar to Coderd in that they serve traffic, so maybe it's ok to keep those two use cases in a single chart. We should keep an eye on how much they diverge and split it into two charts if we're finding there is a lot of either-or config knobs.

```

This example creates a deployment of 10 provisioner daemons (for 10 concurrent builds) with the listed tags. For
generic provisioners, remove the tags.

> Refer to the [values.yaml](https://github.com/coder/coder/blob/main/helm/provisioner/values.yaml) file for the
> coder-provisioner chart for information on what values can be specified.

1. Install the provisioner daemon chart

```shell
helm install coder-provisioner coder-v2/coder-provisioner \
--namespace coder \
--version <your version> \
--values provisioner-values.yaml
```

You can verify that your provisioner daemons have successfully connected to Coderd by looking for a log with message
`provisionerd successfully connected to coderd` from each Pod.

## Example: Running an external provisioner on a VM

```sh
curl -L https://coder.com/install.sh | sh
Expand All @@ -74,7 +136,7 @@ export CODER_SESSION_TOKEN=your_token
coder provisionerd start
```

### Example: Running an external provisioner via Docker
## Example: Running an external provisioner via Docker

```sh
docker run --rm -it \
Expand Down
Binary file modified helm/coder/charts/libcoder-0.1.0.tgz
Binary file not shown.
36 changes: 36 additions & 0 deletions helm/provisioner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Coder Helm Chart

This directory contains the Helm chart used to deploy Coder provisioner daemons onto a Kubernetes
cluster.

External provisioner daemons are an Enterprise feature. Contact sales@coder.com.

## Getting Started

> **Warning**: The main branch in this repository does not represent the
> latest release of Coder. Please reference our installation docs for
> instructions on a tagged release.

View
[our docs](https://coder.com/docs/v2/latest/admin/provisioners)
for detailed installation instructions.

## Values

Please refer to [values.yaml](values.yaml) for available Helm values and their
defaults.

A good starting point for your values file is:

```yaml
coder:
env:
- name: CODER_URL
value: "https://coder.example.com"
# This env enables the Prometheus metrics endpoint.
- name: CODER_PROMETHEUS_ADDRESS
value: "0.0.0.0:2112"
replicaCount: 10
provisionerDaemon:
pskSecretName: "coder-provisioner-psk"
```
Binary file modified helm/provisioner/charts/libcoder-0.1.0.tgz
Binary file not shown.
39 changes: 20 additions & 19 deletions scripts/helm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,39 @@
# .tgz file at the specified path, and may optionally push it to the Coder OSS
# repo.
#
# ./helm.sh [--version 1.2.3] [--output path/to/coder.tgz] [--push]
# ./helm.sh [--version 1.2.3] [--chart coder|provisioner] [--output path/to/coder.tgz]
#
# If no version is specified, defaults to the version from ./version.sh.
#
# If no output path is specified, defaults to
# "$repo_root/build/coder_helm_$version.tgz".
# If no chart is specified, defaults to 'coder'
#
# If the --push parameter is specified, the resulting artifact will be published
# to the Coder OSS repo. This requires `gsutil` to be installed and configured.
# If no output path is specified, defaults to
# "$repo_root/build/$chart_helm_$version.tgz".

set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"

version=""
output_path=""
push=0
chart=""

args="$(getopt -o "" -l version:,output:,push -- "$@")"
args="$(getopt -o "" -l version:,chart:,output:,push -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--version)
version="$2"
shift 2
;;
--chart)
chart="$2"
shift 2
;;
--output)
output_path="$(realpath "$2")"
shift 2
;;
--push)
push="1"
shift
;;
--)
shift
break
Expand All @@ -54,10 +53,17 @@ if [[ "$version" == "" ]]; then
version="$(execrelative ./version.sh)"
fi

if [[ "$chart" == "" ]]; then
chart="coder"
fi
if ! [[ "$chart" =~ ^(coder|provisioner)$ ]]; then
error "--chart value must be one of (coder, provisioner)"
fi

if [[ "$output_path" == "" ]]; then
cdroot
mkdir -p build
output_path="$(realpath "build/coder_helm_$version.tgz")"
output_path="$(realpath "build/${chart}_helm_${version}.tgz")"
fi

# Check dependencies
Expand All @@ -69,10 +75,10 @@ cdroot
temp_dir="$(mktemp -d)"

cdroot
cd ./helm/coder
cd ./helm/$chart
log "--- Updating dependencies"
helm dependency update .
log "--- Packaging helm chart for version $version ($output_path)"
log "--- Packaging helm chart $chart for version $version ($output_path)"
helm package \
--version "$version" \
--app-version "$version" \
Expand All @@ -82,8 +88,3 @@ helm package \
log "Moving helm chart to $output_path"
cp "$temp_dir"/*.tgz "$output_path"
rm -rf "$temp_dir"

if [[ "$push" == 1 ]]; then
log "--- Publishing helm chart..."
# TODO: figure out how/where we want to publish the helm chart
fi