mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: improve the image management experience with template builder (#27018)
Makes it easier to pick the right workspace image, both in the template builder and in the docs. - Template builder: the Docker and Kubernetes bases now expose a `container_image` variable in the wizard (freeform text, defaults to `codercom/example-base:ubuntu`), and their prerequisites explain why image choice matters, with tradeoffs between `codercom/example-base:ubuntu` (minimal) and `codercom/example-universal:ubuntu` (catch-all), plus pointers to [coder/images](https://github.com/coder/images) and the image management docs. - Docs: reworked [image management](https://coder.com/docs/@ben%2Fdevrel-201-image-guidance-prereqs/admin/templates/managing-templates/image-management) into a clearer maturity ladder (minimal → golden → project-specific → developer customization), with pullable image references in every example, `codercom/oss-dogfood` as a project-specific example, and Dev Containers + [mise](https://mise.jdx.dev/) as ways to customize without new images. Companion PR for the starter templates: coder/registry#943 Part of DEVREL-201. 🤖 Generated with Coder Agents using Claude, on behalf of @bpmct (wizard variable by @jeremyruppel in #27024) --------- Co-authored-by: Jeremy Ruppel <jeremyruppel@users.noreply.github.com>
This commit is contained in:
co-authored by
Jeremy Ruppel
parent
bf58e8a402
commit
7b19ec3933
@@ -17,6 +17,19 @@ Provision Docker containers as [Coder workspaces](https://coder.com/docs/user-gu
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Workspace image
|
||||
|
||||
The container image determines what tools, languages, and runtimes are available in the workspace out of the box, so it has a major impact on the developer experience.
|
||||
|
||||
Some options to consider:
|
||||
|
||||
- [`codercom/example-base:ubuntu`](https://github.com/coder/images/tree/main/images/base) (default): minimal and lightweight, but may not include many tools developers expect by default
|
||||
- [`codercom/example-universal:ubuntu`](https://github.com/coder/images/tree/main/images/universal): catch-all image with many languages and tools available, but larger and slower to pull
|
||||
|
||||
More language-specific images (Go, Java, Node.js, and more) are available in [coder/images](https://github.com/coder/images), and the [devcontainers/images](https://github.com/devcontainers/images) collection is another good source of ready-made development images.
|
||||
You can also build your own image to pre-bake the exact tools your team needs.
|
||||
Visit [Coder's image management docs](https://coder.com/docs/admin/templates/managing-templates/image-management) for additional guidance.
|
||||
|
||||
### Infrastructure
|
||||
|
||||
The VM you run Coder on must have a running Docker socket and the `coder` user must be added to the Docker group:
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
"display_name": "Docker",
|
||||
"os": "linux",
|
||||
"default_context": {
|
||||
"container_image": "codercom/enterprise-base:ubuntu"
|
||||
}
|
||||
"container_image": "codercom/example-base:ubuntu"
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"name": "container_image",
|
||||
"type": "string",
|
||||
"description": "Container image for workspaces. The image determines which tools and languages are available in the workspace by default. See the template README for guidance on choosing an image.",
|
||||
"default": "codercom/example-base:ubuntu",
|
||||
"required": false,
|
||||
"sensitive": false,
|
||||
"computed": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ resource "docker_container" "workspace" {
|
||||
{{- if .ImageOptions }}
|
||||
image = data.coder_parameter.container_image.value
|
||||
{{- else }}
|
||||
image = "{{ .ContainerImage }}"
|
||||
image = {{ .Variables.container_image }}
|
||||
{{- end }}
|
||||
# Uses lower() to avoid Docker restriction on container names.
|
||||
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
|
||||
@@ -21,7 +21,18 @@ Provision Kubernetes Pods as [Coder workspaces](https://coder.com/docs/user-guid
|
||||
|
||||
**Cluster**: This template requires an existing Kubernetes cluster
|
||||
|
||||
**Container Image**: This template uses the [codercom/enterprise-base:ubuntu image](https://github.com/coder/enterprise-images/tree/main/images/base) with some dev tools preinstalled. To add additional tools, extend this image or build it yourself.
|
||||
### Workspace image
|
||||
|
||||
The container image determines what tools, languages, and runtimes are available in the workspace out of the box, so it has a major impact on the developer experience.
|
||||
|
||||
Some options to consider:
|
||||
|
||||
- [`codercom/example-base:ubuntu`](https://github.com/coder/images/tree/main/images/base) (default): minimal and lightweight, but may not include many tools developers expect by default
|
||||
- [`codercom/example-universal:ubuntu`](https://github.com/coder/images/tree/main/images/universal): catch-all image with many languages and tools available, but larger and slower to pull
|
||||
|
||||
More language-specific images (Go, Java, Node.js, and more) are available in [coder/images](https://github.com/coder/images), and the [devcontainers/images](https://github.com/devcontainers/images) collection is another good source of ready-made development images.
|
||||
You can also build your own image to pre-bake the exact tools your team needs.
|
||||
Visit [Coder's image management docs](https://coder.com/docs/admin/templates/managing-templates/image-management) for additional guidance.
|
||||
|
||||
### Authentication
|
||||
|
||||
|
||||
@@ -3,9 +3,18 @@
|
||||
"display_name": "Kubernetes",
|
||||
"os": "linux",
|
||||
"default_context": {
|
||||
"container_image": "codercom/enterprise-base:ubuntu"
|
||||
"container_image": "codercom/example-base:ubuntu"
|
||||
},
|
||||
"variables": [
|
||||
{
|
||||
"name": "container_image",
|
||||
"type": "string",
|
||||
"description": "Container image for workspaces. The image determines which tools and languages are available in the workspace by default. See the template README for guidance on choosing an image.",
|
||||
"default": "codercom/example-base:ubuntu",
|
||||
"required": false,
|
||||
"sensitive": false,
|
||||
"computed": false
|
||||
},
|
||||
{
|
||||
"name": "use_kubeconfig",
|
||||
"type": "bool",
|
||||
|
||||
@@ -260,7 +260,7 @@ resource "kubernetes_deployment_v1" "main" {
|
||||
{{- if .ImageOptions }}
|
||||
image = data.coder_parameter.container_image.value
|
||||
{{- else }}
|
||||
image = "{{ .ContainerImage }}"
|
||||
image = {{ .Variables.container_image }}
|
||||
{{- end }}
|
||||
image_pull_policy = "Always"
|
||||
command = ["sh", "-c", coder_agent.main.init_script]
|
||||
|
||||
@@ -65,14 +65,14 @@ func TestDefaultBaseRenderContext(t *testing.T) {
|
||||
t.Run("Docker", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
rc := templatebuilder.DefaultBaseRenderContext("docker")
|
||||
require.Equal(t, "codercom/enterprise-base:ubuntu", rc.ContainerImage)
|
||||
require.Equal(t, "codercom/example-base:ubuntu", rc.ContainerImage)
|
||||
require.Nil(t, rc.ImageOptions)
|
||||
})
|
||||
|
||||
t.Run("Kubernetes", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
rc := templatebuilder.DefaultBaseRenderContext("kubernetes")
|
||||
require.Equal(t, "codercom/enterprise-base:ubuntu", rc.ContainerImage)
|
||||
require.Equal(t, "codercom/example-base:ubuntu", rc.ContainerImage)
|
||||
require.Nil(t, rc.ImageOptions)
|
||||
})
|
||||
|
||||
|
||||
@@ -290,6 +290,60 @@ func TestCompose(t *testing.T) {
|
||||
require.Contains(t, err.Error(), "interpolation")
|
||||
})
|
||||
|
||||
t.Run("DockerDefaultContainerImage", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result, err := templatebuilder.Compose(templatebuilder.ComposeRequest{
|
||||
BaseTemplateID: "docker",
|
||||
RegistryURL: "https://registry.coder.com",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(result.MainTF), `"codercom/example-base:ubuntu"`)
|
||||
})
|
||||
|
||||
t.Run("DockerCustomContainerImage", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result, err := templatebuilder.Compose(templatebuilder.ComposeRequest{
|
||||
BaseTemplateID: "docker",
|
||||
RegistryURL: "https://registry.coder.com",
|
||||
BaseVariableValues: map[string]string{
|
||||
"container_image": "myregistry/myimage:v2",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
mainTF := string(result.MainTF)
|
||||
require.Contains(t, mainTF, `"myregistry/myimage:v2"`)
|
||||
require.NotContains(t, mainTF, `codercom/example-base:ubuntu`)
|
||||
})
|
||||
|
||||
t.Run("KubernetesDefaultContainerImage", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result, err := templatebuilder.Compose(templatebuilder.ComposeRequest{
|
||||
BaseTemplateID: "kubernetes",
|
||||
RegistryURL: "https://registry.coder.com",
|
||||
BaseVariableValues: map[string]string{
|
||||
"namespace": "default",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, string(result.MainTF), `"codercom/example-base:ubuntu"`)
|
||||
})
|
||||
|
||||
t.Run("KubernetesCustomContainerImage", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result, err := templatebuilder.Compose(templatebuilder.ComposeRequest{
|
||||
BaseTemplateID: "kubernetes",
|
||||
RegistryURL: "https://registry.coder.com",
|
||||
BaseVariableValues: map[string]string{
|
||||
"namespace": "default",
|
||||
"container_image": "custom/workspace:latest",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
mainTF := string(result.MainTF)
|
||||
require.Contains(t, mainTF, `"custom/workspace:latest"`)
|
||||
require.NotContains(t, mainTF, `codercom/example-base:ubuntu`)
|
||||
})
|
||||
|
||||
t.Run("MissingRequiredVariable", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// git-clone has a required "url" variable with no default.
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ resource "docker_volume" "home_volume" {
|
||||
|
||||
resource "docker_container" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
image = "codercom/example-base:ubuntu"
|
||||
# Uses lower() to avoid Docker restriction on container names.
|
||||
name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
|
||||
# Hostname makes the shell more user friendly: coder@my-workspace:~$
|
||||
|
||||
+1
-1
@@ -244,7 +244,7 @@ resource "kubernetes_deployment_v1" "main" {
|
||||
|
||||
container {
|
||||
name = "dev"
|
||||
image = "codercom/enterprise-base:ubuntu"
|
||||
image = "codercom/example-base:ubuntu"
|
||||
image_pull_policy = "Always"
|
||||
command = ["sh", "-c", coder_agent.main.init_script]
|
||||
security_context {
|
||||
|
||||
@@ -45,13 +45,14 @@ func TestTemplateBuilderBases(t *testing.T) {
|
||||
{
|
||||
id: "docker",
|
||||
expectedOS: "linux",
|
||||
hasVariables: false,
|
||||
hasVariables: true,
|
||||
expectedVars: []string{"container_image"},
|
||||
},
|
||||
{
|
||||
id: "kubernetes",
|
||||
expectedOS: "linux",
|
||||
hasVariables: true,
|
||||
expectedVars: []string{"namespace", "use_kubeconfig"},
|
||||
expectedVars: []string{"container_image", "namespace", "use_kubeconfig"},
|
||||
},
|
||||
{
|
||||
id: "aws-linux",
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
# Image Management
|
||||
|
||||
While Coder provides example
|
||||
[base container images](https://github.com/coder/enterprise-images) for
|
||||
workspaces, it's often best to create custom images that matches the needs of
|
||||
your users. This document serves a guide to operational maturity with some best
|
||||
practices around managing workspaces images for Coder.
|
||||
[container images](https://github.com/coder/images) for
|
||||
workspaces, it's often best to create custom images that match the needs of
|
||||
your users. This document serves as a guide to operational maturity with some
|
||||
best practices around managing workspace images for Coder.
|
||||
|
||||
1. Create a minimal base image
|
||||
2. Create golden image(s) with standard tooling
|
||||
3. Allow developers to bring their own images and customizations with Dev
|
||||
Containers
|
||||
After following this tutorial, you'll accomplish the following:
|
||||
|
||||
1. Create a minimal base image.
|
||||
2. Create golden images with standard tooling.
|
||||
3. Create project-specific images for common use cases.
|
||||
4. Let developers customize their own environment.
|
||||
|
||||
An image is just one of the many properties defined within the template.
|
||||
Templates can pull images from a public image registry (e.g. Docker Hub) or an
|
||||
internal one, thanks to Terraform.
|
||||
internal one, thanks to Terraform. Each image reference below can be dropped
|
||||
directly into a template's image variable or parameter.
|
||||
|
||||
## Create a minimal base image
|
||||
|
||||
While you may not use this directly in Coder templates, it's useful to have a
|
||||
minimal base image is a small image that contains only the necessary
|
||||
minimal base image as a small image that contains only the necessary
|
||||
dependencies to work in your network and work with Coder. Here are some things
|
||||
to consider:
|
||||
|
||||
@@ -31,17 +34,18 @@ to consider:
|
||||
`docker`, `bash`, `jq`, and/or internal tooling
|
||||
- Consider creating (and starting the container with) a non-root user
|
||||
|
||||
See Coder's
|
||||
[example base image](https://github.com/coder/enterprise-images/tree/main/images/minimal)
|
||||
for reference.
|
||||
Examples:
|
||||
|
||||
## Create general-purpose golden image(s) with standard tooling
|
||||
- [`codercom/example-minimal:ubuntu`](https://github.com/coder/images/tree/main/images/minimal): only the necessary dependencies for a Coder workspace to bootstrap
|
||||
- [`codercom/example-base:ubuntu`](https://github.com/coder/images/tree/main/images/base): a slightly more padded starting point with common utilities preinstalled
|
||||
|
||||
It's often practical to have a few golden images that contain standard tooling
|
||||
for developers. These images should contain a number of languages (e.g. Python,
|
||||
Java, TypeScript), IDEs (VS Code, JetBrains, PyCharm), and other tools (e.g.
|
||||
`docker`). Unlike project-specific images (which are also important), general
|
||||
purpose images are great for:
|
||||
## Create golden images with standard tooling
|
||||
|
||||
Building on the base image, it's often practical to have a few golden images
|
||||
that contain standard tooling for developers. These images should contain a
|
||||
number of languages (e.g. Python, Java, TypeScript), IDEs (VS Code, JetBrains,
|
||||
PyCharm), and other tools (e.g. `docker`). Unlike project-specific images
|
||||
(which are also important), general purpose images are great for:
|
||||
|
||||
- **Scripting:** Developers may just want to hop in a Coder workspace to run
|
||||
basic scripts or queries.
|
||||
@@ -61,14 +65,37 @@ most cases) with a well-defined scope.
|
||||
|
||||
Examples:
|
||||
|
||||
- [Universal Dev Containers Image](https://github.com/devcontainers/images/tree/main/src/universal)
|
||||
- [`codercom/example-universal:ubuntu`](https://github.com/coder/images/tree/main/images/universal): a catch-all image with many languages and tools preinstalled. Runs as the `coder` user, so it works with Coder templates out of the box.
|
||||
- [`mcr.microsoft.com/devcontainers/universal`](https://github.com/devcontainers/images/tree/main/src/universal): the Universal Dev Containers image
|
||||
|
||||
## Allow developers to bring their own images and customizations with Dev Containers
|
||||
## Create project-specific images for common use cases
|
||||
|
||||
While golden images are great for general use cases, developers will often need
|
||||
specific tooling for their projects. The [Dev Container](https://containers.dev)
|
||||
specification allows developers to define their projects dependencies within a
|
||||
`devcontainer.json` in their Git repository.
|
||||
Beyond golden images, create images scoped to a specific project, language, or
|
||||
use case (e.g., a Go backend, a Node.js frontend, or a data science stack).
|
||||
These images stay smaller and faster to pull than one larger image that tries to install all dependencies.
|
||||
|
||||
- [Configure a template for Dev Containers](../../integrations/devcontainers/integration.md) (recommended)
|
||||
- [Learn about Envbuilder](../../integrations/devcontainers/envbuilder/index.md) (alternative for environments without Docker)
|
||||
Examples:
|
||||
|
||||
- [`codercom/example-golang:ubuntu`](https://github.com/coder/images/tree/main/images/golang),
|
||||
[`codercom/example-java:ubuntu`](https://github.com/coder/images/tree/main/images/java),
|
||||
[`codercom/example-node:ubuntu`](https://github.com/coder/images/tree/main/images/node):
|
||||
Coder's example language-specific images, all running as the `coder` user.
|
||||
Refer to [coder/images](https://github.com/coder/images) for the full list.
|
||||
- [`codercom/oss-dogfood:latest`](https://github.com/coder/coder/tree/main/dogfood):
|
||||
the image Coder's own engineers use to develop Coder. A good reference for a
|
||||
project-specific image tailored to a specific team or monorepo setup.
|
||||
|
||||
## Let developers customize their own environment
|
||||
|
||||
Even with well-scoped images, developers will often need tooling that is
|
||||
specific to their project or personal workflow. Instead of maintaining an
|
||||
image for every combination, developers can layer their own customizations
|
||||
on top of a smaller image:
|
||||
|
||||
- [Dev Containers](https://containers.dev): developers define their project's
|
||||
dependencies in a `devcontainer.json` in their Git repository, and Coder
|
||||
builds the environment on top of your base image. Visit
|
||||
[configure a template for Dev Containers](../../integrations/devcontainers/integration.md).
|
||||
- [mise](https://mise.jdx.dev/): developers install and pin language runtimes
|
||||
and CLI tools at workspace startup without rebuilding the image. Visit the
|
||||
[install command-line tools guide](../../../get-started/customize-your-template/install-command-line-tools.md).
|
||||
|
||||
Reference in New Issue
Block a user