Skip to content

Commit 1117575

Browse files
bpmctkhorne3
authored andcommitted
example: add docker-image-builds + docker docs (#1526)
Co-authored-by: Katie Horne <katie@23spoons.com>
1 parent a8e7e88 commit 1117575

File tree

9 files changed

+557
-66
lines changed

9 files changed

+557
-66
lines changed
+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
---
2+
name: Develop in Docker with custom image builds
3+
description: Build images and run workspaces on the Docker host with no image registry required
4+
tags: [local, docker]
5+
---
6+
7+
# docker-image-builds
8+
9+
This example bundles Dockerfiles with the Coder template, allowing the Docker host to build images itself instead of relying on an external registry.
10+
11+
For large use cases, we recommend building images using CI/CD pipelines and registries instead of at workspace runtime. However, this example is practical for tinkering and iterating on Dockerfiles.
12+
13+
## Getting started
14+
15+
Run `coder templates init` and select this template. Follow the instructions that appear.
16+
17+
## Adding images
18+
19+
Create a Dockerfile (e.g `images/golang.Dockerfile`):
20+
21+
```sh
22+
vim images/golang.Dockerfile
23+
```
24+
25+
```Dockerfile
26+
# Start from base image (built on Docker host)
27+
FROM coder-base:latest
28+
29+
# Install everything as root
30+
USER root
31+
32+
# Install go
33+
RUN curl -L "https://dl.google.com/go/go1.18.1.linux-amd64.tar.gz" | tar -C /usr/local -xzvf -
34+
35+
# Setup go env vars
36+
ENV GOROOT /usr/local/go
37+
ENV PATH $PATH:$GOROOT/bin
38+
39+
ENV GOPATH /home/coder/go
40+
ENV GOBIN $GOPATH/bin
41+
ENV PATH $PATH:$GOBIN
42+
43+
# Set back to coder user
44+
USER coder
45+
```
46+
47+
Edit the Terraform template (`main.tf`):
48+
49+
```sh
50+
vim main.tf
51+
```
52+
53+
Edit the validation to include the new image:
54+
55+
```diff
56+
variable "docker_image" {
57+
description = "What Docker imagewould you like to use for your workspace?"
58+
default = "base"
59+
60+
# List of images available for the user to choose from.
61+
# Delete this condition to give users free text input.
62+
validation {
63+
- condition = contains(["base", "java", "node"], var.docker_image)
64+
+ condition = contains(["base", "java", "node", "golang], var.docker_image)
65+
error_message = "Invalid Docker image!"
66+
}
67+
}
68+
```
69+
70+
Bump the image tag to a new version:
71+
72+
```diff
73+
resource "docker_image" "coder_image" {
74+
name = "coder-base-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
75+
build {
76+
path = "./images/"
77+
dockerfile = "${var.docker_image}.Dockerfile"
78+
- tag = ["coder-${var.docker_image}:v0.1"]
79+
+ tag = ["coder-${var.docker_image}:v0.2"]
80+
}
81+
82+
# Keep alive for other workspaces to use upon deletion
83+
keep_locally = true
84+
}
85+
```
86+
87+
Update the template:
88+
89+
```sh
90+
coder template update docker-image-builds
91+
```
92+
93+
You can also remove images from the validation list. Workspaces using older template versions will continue using
94+
the removed image until you update the workspace to the latest version.
95+
96+
## Updating images
97+
98+
Edit the Dockerfile (or related assets):
99+
100+
```sh
101+
vim images/node.Dockerfile
102+
```
103+
104+
```diff
105+
# Install Node
106+
- RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
107+
+ RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
108+
RUN DEBIAN_FRONTEND="noninteractive" apt-get update -y && \
109+
apt-get install -y nodejs
110+
```
111+
112+
1. Edit the Terraform template (`main.tf`)
113+
114+
```sh
115+
vim main.tf
116+
```
117+
118+
Bump the image tag to a new version:
119+
120+
```diff
121+
resource "docker_image" "coder_image" {
122+
name = "coder-base-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
123+
build {
124+
path = "./images/"
125+
dockerfile = "${var.docker_image}.Dockerfile"
126+
- tag = ["coder-${var.docker_image}:v0.1"]
127+
+ tag = ["coder-${var.docker_image}:v0.2"]
128+
}
129+
130+
# Keep alive for other workspaces to use upon deletion
131+
keep_locally = true
132+
}
133+
```
134+
135+
Update the template:
136+
137+
```sh
138+
coder template update docker-image-builds
139+
```
140+
141+
Optional: Update workspaces to the latest template version
142+
143+
```sh
144+
coder ls
145+
coder update [workspace name]
146+
```
147+
148+
## Extending this template
149+
150+
See the [kreuzwerker/docker](https://registry.terraform.io/providers/kreuzwerker/docker) Terraform provider documentation to
151+
add the following features to your Coder template:
152+
153+
- SSH/TCP docker host
154+
- Build args
155+
- Volume mounts
156+
- Custom container spec
157+
- More
158+
159+
We also welcome all contributions!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
FROM ubuntu:20.04
2+
3+
RUN apt-get update && \
4+
DEBIAN_FRONTEND="noninteractive" apt-get install --yes \
5+
bash \
6+
build-essential \
7+
ca-certificates \
8+
curl \
9+
htop \
10+
locales \
11+
man \
12+
python3 \
13+
python3-pip \
14+
software-properties-common \
15+
sudo \
16+
systemd \
17+
systemd-sysv \
18+
unzip \
19+
vim \
20+
wget && \
21+
# Install latest Git using their official PPA
22+
add-apt-repository ppa:git-core/ppa && \
23+
DEBIAN_FRONTEND="noninteractive" apt-get install --yes git
24+
25+
# Add a user `coder` so that you're not developing as the `root` user
26+
RUN useradd coder \
27+
--create-home \
28+
--shell=/bin/bash \
29+
--uid=1000 \
30+
--user-group && \
31+
echo "coder ALL=(ALL) NOPASSWD:ALL" >>/etc/sudoers.d/nopasswd
32+
33+
USER coder
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# From the base image (built on Docker host)
2+
FROM coder-base:v0.1
3+
4+
# Install everything as root
5+
USER root
6+
7+
# Install JDK (OpenJDK 8)
8+
RUN DEBIAN_FRONTEND="noninteractive" apt-get update -y && \
9+
apt-get install -y openjdk-11-jdk
10+
ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64
11+
ENV PATH $PATH:$JAVA_HOME/bin
12+
13+
# Install Maven
14+
ARG MAVEN_VERSION=3.6.3
15+
ARG MAVEN_SHA512=c35a1803a6e70a126e80b2b3ae33eed961f83ed74d18fcd16909b2d44d7dada3203f1ffe726c17ef8dcca2dcaa9fca676987befeadc9b9f759967a8cb77181c0
16+
17+
ENV MAVEN_HOME /usr/share/maven
18+
ENV MAVEN_CONFIG "/home/coder/.m2"
19+
20+
RUN mkdir -p $MAVEN_HOME $MAVEN_HOME/ref \
21+
&& echo "Downloading Maven" \
22+
&& curl -fsSL -o /tmp/apache-maven.tar.gz https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \
23+
\
24+
&& echo "Checking downloaded file hash" \
25+
&& echo "${MAVEN_SHA512} /tmp/apache-maven.tar.gz" | sha512sum -c - \
26+
\
27+
&& echo "Unzipping Maven" \
28+
&& tar -xzf /tmp/apache-maven.tar.gz -C $MAVEN_HOME --strip-components=1 \
29+
\
30+
&& echo "Cleaning and setting links" \
31+
&& rm -f /tmp/apache-maven.tar.gz \
32+
&& ln -s $MAVEN_HOME/bin/mvn /usr/bin/mvn
33+
34+
# Install Gradle
35+
ENV GRADLE_VERSION=6.7
36+
ARG GRADLE_SHA512=d495bc65379d2a854d2cca843bd2eeb94f381e5a7dcae89e6ceb6ef4c5835524932313e7f30d7a875d5330add37a5fe23447dc3b55b4d95dffffa870c0b24493
37+
38+
ENV GRADLE_HOME /usr/bin/gradle
39+
40+
RUN mkdir -p /usr/share/gradle /usr/share/gradle/ref \
41+
&& echo "Downloading Gradle" \
42+
&& curl -fsSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip \
43+
\
44+
&& echo "Checking downloaded file hash" \
45+
&& echo "${GRADLE_SHA512} /tmp/gradle.zip" | sha512sum -c - \
46+
\
47+
&& echo "Unziping gradle" \
48+
&& unzip -d /usr/share/gradle /tmp/gradle.zip \
49+
\
50+
&& echo "Cleaning and setting links" \
51+
&& rm -f /tmp/gradle.zip \
52+
&& ln -s /usr/share/gradle/gradle-${GRADLE_VERSION} /usr/bin/gradle
53+
54+
ENV PATH $PATH:$GRADLE_HOME/bin
55+
56+
# Set back to coder user
57+
USER coder
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Start from base image (built on Docker host)
2+
FROM coder-base:v0.1
3+
4+
# Install everything as root
5+
USER root
6+
7+
# Install Node
8+
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
9+
RUN DEBIAN_FRONTEND="noninteractive" apt-get update -y && \
10+
apt-get install -y nodejs
11+
12+
# Install Yarn
13+
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
14+
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
15+
RUN DEBIAN_FRONTEND="noninteractive" apt-get update && apt-get install -y yarn
16+
17+
# Set back to coder user
18+
USER coder

examples/docker-image-builds/main.tf

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
2+
terraform {
3+
required_providers {
4+
coder = {
5+
source = "coder/coder"
6+
version = "0.4.1"
7+
}
8+
docker = {
9+
source = "kreuzwerker/docker"
10+
version = "~> 2.16.0"
11+
}
12+
}
13+
}
14+
15+
# Admin parameters
16+
variable "step1_docker_host_warning" {
17+
description = <<-EOF
18+
This template will use the Docker socket present on
19+
the Coder host, which is not necessarily your local machine.
20+
21+
You can specify a different host in the template file and
22+
surpress this warning.
23+
EOF
24+
validation {
25+
condition = contains(["Continue using /var/run/docker.sock on the Coder host"], var.step1_docker_host_warning)
26+
error_message = "Cancelling template create."
27+
}
28+
29+
sensitive = true
30+
}
31+
variable "step2_arch" {
32+
description = "arch: What archicture is your Docker host on?"
33+
validation {
34+
condition = contains(["amd64", "arm64", "armv7"], var.step2_arch)
35+
error_message = "Value must be amd64, arm64, or armv7."
36+
}
37+
sensitive = true
38+
}
39+
40+
provider "docker" {
41+
host = "unix:///var/run/docker.sock"
42+
}
43+
44+
provider "coder" {
45+
# The below assumes your Coder deployment is running in docker-compose.
46+
# If this is not the case, either comment or edit the below.
47+
url = "http://host.docker.internal:7080"
48+
}
49+
50+
data "coder_workspace" "me" {
51+
}
52+
53+
resource "coder_agent" "dev" {
54+
arch = var.step2_arch
55+
os = "linux"
56+
}
57+
58+
variable "docker_image" {
59+
description = "What Docker imagewould you like to use for your workspace?"
60+
default = "base"
61+
62+
# List of images available for the user to choose from.
63+
# Delete this condition to give users free text input.
64+
validation {
65+
condition = contains(["base", "java", "node"], var.docker_image)
66+
error_message = "Invalid Docker image!"
67+
}
68+
69+
# Prevents admin errors when the image is not found
70+
validation {
71+
condition = fileexists("images/${var.docker_image}.Dockerfile")
72+
error_message = "Invalid Docker image. The file does not exist in the images directory."
73+
}
74+
}
75+
76+
resource "docker_volume" "home_volume" {
77+
name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}-root"
78+
}
79+
80+
resource "docker_image" "coder_image" {
81+
name = "coder-base-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
82+
build {
83+
path = "./images/"
84+
dockerfile = "${var.docker_image}.Dockerfile"
85+
tag = ["coder-${var.docker_image}:v0.1"]
86+
}
87+
88+
# Keep alive for other workspaces to use upon deletion
89+
keep_locally = true
90+
}
91+
92+
resource "docker_container" "workspace" {
93+
count = data.coder_workspace.me.start_count
94+
image = docker_image.coder_image.latest
95+
# Uses lower() to avoid Docker restriction on container names.
96+
name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
97+
# Hostname makes the shell more user friendly: coder@my-workspace:~$
98+
hostname = lower(data.coder_workspace.me.name)
99+
dns = ["1.1.1.1"]
100+
command = ["sh", "-c", coder_agent.dev.init_script]
101+
env = ["CODER_AGENT_TOKEN=${coder_agent.dev.token}"]
102+
host {
103+
host = "host.docker.internal"
104+
ip = "host-gateway"
105+
}
106+
volumes {
107+
container_path = "/home/coder/"
108+
volume_name = docker_volume.home_volume.name
109+
read_only = false
110+
}
111+
}

examples/docker-local/README.md

-5
This file was deleted.

0 commit comments

Comments
 (0)