Skip to content

example: add docker-image-builds + docker docs #1526

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 23 commits into from
May 18, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
159 changes: 159 additions & 0 deletions examples/docker-image-builds/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
name: Develop in Docker with custom image builds
description: Builds images and runs workspaces on the Docker host, no image registry required
tags: [local, docker]
---

# docker-image-builds

This example bundles Dockerfiles in the Coder template, allowing the Docker host to build images itself instead of relying on an external registry.

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.

## Getting started

Pick this template in `coder templates init` and follow instructions.

## Adding images

Create a Dockerfile (e.g `images/golang.Dockerfile`)

```sh
vim images/golang.Dockerfile
```

```Dockerfile
# Start from base image (built on Docker host)
FROM coder-base:latest

# Install everything as root
USER root

# Install go
RUN curl -L "https://dl.google.com/go/go1.18.1.linux-amd64.tar.gz" | tar -C /usr/local -xzvf -

# Setup go env vars
ENV GOROOT /usr/local/go
ENV PATH $PATH:$GOROOT/bin

ENV GOPATH /home/coder/go
ENV GOBIN $GOPATH/bin
ENV PATH $PATH:$GOBIN

# Set back to coder user
USER coder
```

Edit the template Terraform (`main.tf`)

```sh
vim main.tf
```

Edit the validation to include the new image:

```diff
variable "docker_image" {
description = "What docker image would you like to use for your workspace?"
default = "base"

# List of images available for the user to choose from.
# Delete this condition to give users free text input.
validation {
- condition = contains(["base", "java", "node"], var.docker_image)
+ condition = contains(["base", "java", "node", "golang], var.docker_image)
error_message = "Invalid Docker Image!"
}
}
```

Bump the image tag to a new version:

```diff
resource "docker_image" "coder_image" {
name = "coder-base-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
build {
path = "./images/"
dockerfile = "${var.docker_image}.Dockerfile"
- tag = ["coder-${var.docker_image}:v0.1"]
+ tag = ["coder-${var.docker_image}:v0.2"]
}

# Keep alive for other workspaces to use upon deletion
keep_locally = true
}
```

Update the template:

```sh
coder template update docker-image-builds
```

Images can also be removed from the validation list. Workspaces using older template versions will continue using
the removed image until the workspace is updated to the latest version.

## Updating images

Edit the Dockerfile (or related assets)

```sh
vim images/node.Dockerfile
```

```diff
# Install Node
- RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
+ RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
RUN DEBIAN_FRONTEND="noninteractive" apt-get update -y && \
apt-get install -y nodejs
```

1. Edit the template Terraform (`main.tf`)

```sh
vim main.tf
```

Bump the image tag to a new version:

```diff
resource "docker_image" "coder_image" {
name = "coder-base-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
build {
path = "./images/"
dockerfile = "${var.docker_image}.Dockerfile"
- tag = ["coder-${var.docker_image}:v0.1"]
+ tag = ["coder-${var.docker_image}:v0.2"]
}

# Keep alive for other workspaces to use upon deletion
keep_locally = true
}
```

Update the template:

```sh
coder template update docker-image-builds
```

Optional: Update workspaces to the latest template version

```sh
coder ls
coder update [workspace name]
```

## Extending this template

See the [kreuzwerker/docker](https://registry.terraform.io/providers/kreuzwerker/docker) Terraform provider documentation to
add the following features to your Coder template:

- SSH/TCP docker host
- Build args
- Volume mounts
- Custom container spec
- More

Contributions are also welcome!
33 changes: 33 additions & 0 deletions examples/docker-image-builds/images/base.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM ubuntu:20.04

RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" apt-get install --yes \
bash \
build-essential \
ca-certificates \
curl \
htop \
locales \
man \
python3 \
python3-pip \
software-properties-common \
sudo \
systemd \
systemd-sysv \
unzip \
vim \
wget && \
# Install latest Git using their official PPA
add-apt-repository ppa:git-core/ppa && \
DEBIAN_FRONTEND="noninteractive" apt-get install --yes git

# Add a user `coder` so that you're not developing as the `root` user
RUN useradd coder \
--create-home \
--shell=/bin/bash \
--uid=1000 \
--user-group && \
echo "coder ALL=(ALL) NOPASSWD:ALL" >>/etc/sudoers.d/nopasswd

USER coder
57 changes: 57 additions & 0 deletions examples/docker-image-builds/images/java.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# From the base image (built on Docker host)
FROM coder-base:v0.1

# Install everything as root
USER root

# Install JDK (OpenJDK 8)
RUN DEBIAN_FRONTEND="noninteractive" apt-get update -y && \
apt-get install -y openjdk-11-jdk
ENV JAVA_HOME /usr/lib/jvm/java-11-openjdk-amd64
ENV PATH $PATH:$JAVA_HOME/bin

# Install Maven
ARG MAVEN_VERSION=3.6.3
ARG MAVEN_SHA512=c35a1803a6e70a126e80b2b3ae33eed961f83ed74d18fcd16909b2d44d7dada3203f1ffe726c17ef8dcca2dcaa9fca676987befeadc9b9f759967a8cb77181c0

ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "/home/coder/.m2"

RUN mkdir -p $MAVEN_HOME $MAVEN_HOME/ref \
&& echo "Downloading maven" \
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
&& echo "Downloading maven" \
&& echo "Downloading Maven" \

&& 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 \
\
&& echo "Checking downloaded file hash" \
&& echo "${MAVEN_SHA512} /tmp/apache-maven.tar.gz" | sha512sum -c - \
\
&& echo "Unzipping maven" \
&& tar -xzf /tmp/apache-maven.tar.gz -C $MAVEN_HOME --strip-components=1 \
\
&& echo "Cleaning and setting links" \
&& rm -f /tmp/apache-maven.tar.gz \
&& ln -s $MAVEN_HOME/bin/mvn /usr/bin/mvn

# Install Gradle
ENV GRADLE_VERSION=6.7
ARG GRADLE_SHA512=d495bc65379d2a854d2cca843bd2eeb94f381e5a7dcae89e6ceb6ef4c5835524932313e7f30d7a875d5330add37a5fe23447dc3b55b4d95dffffa870c0b24493

ENV GRADLE_HOME /usr/bin/gradle

RUN mkdir -p /usr/share/gradle /usr/share/gradle/ref \
&& echo "Downloading gradle" \
&& curl -fsSL -o /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip \
\
&& echo "Checking downloaded file hash" \
&& echo "${GRADLE_SHA512} /tmp/gradle.zip" | sha512sum -c - \
\
&& echo "Unziping gradle" \
&& unzip -d /usr/share/gradle /tmp/gradle.zip \
\
&& echo "Cleaning and setting links" \
&& rm -f /tmp/gradle.zip \
&& ln -s /usr/share/gradle/gradle-${GRADLE_VERSION} /usr/bin/gradle

ENV PATH $PATH:$GRADLE_HOME/bin

# Set back to coder user
USER coder
18 changes: 18 additions & 0 deletions examples/docker-image-builds/images/node.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Start from base image (built on Docker host)
FROM coder-base:v0.1

# Install everything as root
USER root

# Install Node
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
RUN DEBIAN_FRONTEND="noninteractive" apt-get update -y && \
apt-get install -y nodejs

# Install Yarn
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN DEBIAN_FRONTEND="noninteractive" apt-get update && apt-get install -y yarn

# Set back to coder user
USER coder
109 changes: 109 additions & 0 deletions examples/docker-image-builds/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@

terraform {
required_providers {
coder = {
source = "coder/coder"
version = "0.4.1"
}
docker = {
source = "kreuzwerker/docker"
version = "~> 2.16.0"
}
}
}

# Admin parameters
variable "step1_docker_host_warning" {
description = <<-EOF
This template will use the Docker socket present on
the Coder host, which is not necessarily your local machine.

You can specify a different host in the template file and
surpress this warning.
EOF
validation {
condition = contains(["Continue using /var/run/docker.sock on the Coder host"], var.step1_docker_host_warning)
error_message = "Cancelling template create."
}

sensitive = true
}
variable "step2_arch" {
description = "arch: What archicture is your Docker host on?"
validation {
condition = contains(["amd64", "arm64", "armv7"], var.step2_arch)
error_message = "Value must be amd64, arm64 or armv7."
}
sensitive = true
}

provider "docker" {
host = "unix:///var/run/docker.sock"
}

provider "coder" {
url = "http://host.docker.internal:7080"
}

data "coder_workspace" "me" {
}

resource "coder_agent" "dev" {
arch = var.step2_arch
os = "linux"
}

variable "docker_image" {
description = "What docker image would you like to use for your workspace?"
default = "base"

# List of images available for the user to choose from.
# Delete this condition to give users free text input.
validation {
condition = contains(["base", "java", "node"], var.docker_image)
error_message = "Invalid Docker Image!"
}

# Prevents admin errors when the image is not found
validation {
condition = fileexists("images/${var.docker_image}.Dockerfile")
error_message = "Invalid docker image. The file does not exist in the images directory."
}
}

resource "docker_volume" "home_volume" {
name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}-root"
}

resource "docker_image" "coder_image" {
name = "coder-base-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
build {
path = "./images/"
dockerfile = "${var.docker_image}.Dockerfile"
tag = ["coder-${var.docker_image}:v0.1"]
}

# Keep alive for other workspaces to use upon deletion
keep_locally = true
}

resource "docker_container" "workspace" {
count = data.coder_workspace.me.start_count
image = docker_image.coder_image.latest
# Uses lower() to avoid Docker restriction on container names.
name = "coder-${data.coder_workspace.me.owner}-${lower(data.coder_workspace.me.name)}"
# Hostname makes the shell more user friendly: coder@my-workspace:~$
hostname = lower(data.coder_workspace.me.name)
dns = ["1.1.1.1"]
command = ["sh", "-c", coder_agent.dev.init_script]
env = ["CODER_AGENT_TOKEN=${coder_agent.dev.token}"]
host {
host = "host.docker.internal"
ip = "host-gateway"
}
volumes {
container_path = "/home/coder/"
volume_name = docker_volume.home_volume.name
read_only = false
}
}
5 changes: 0 additions & 5 deletions examples/docker-local/README.md

This file was deleted.

Loading