diff --git a/registry/sharkymark/.images/flask-app.png b/registry/sharkymark/.images/flask-app.png new file mode 100644 index 000000000..3ba241870 Binary files /dev/null and b/registry/sharkymark/.images/flask-app.png differ diff --git a/registry/sharkymark/templates/docker-devcontainer/README.md b/registry/sharkymark/templates/docker-devcontainer/README.md new file mode 100644 index 000000000..e0948c5fc --- /dev/null +++ b/registry/sharkymark/templates/docker-devcontainer/README.md @@ -0,0 +1,75 @@ +--- +display_name: Dev Containers in a Docker workspace +description: Provision Docker containers as Coder workspaces running Dev Containers via Docker-in-Docker and the devcontainer CLI. +icon: "../../../../.icons/docker.svg" +maintainer_github: sharkymark +verified: false +tags: [docker, container, devcontainer, code-server] +--- + +# Remote Development on Dev Containers + +Provision Docker containers as [Coder workspaces](https://coder.com/docs/workspaces) running [Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers) via Docker-in-Docker and the Dev Container CLI. + +![Flask App Screenshot](../../.images/flask-app.png) + +## Example devcontainer + +The default example devcontainer is one of Sharkymark's sales commissions calculators. It is a Python Flask app and will auto-start so should be accessible on port 5000 when the workspace is running. See the repo on GitHub [here](https://github.com/sharkymark/flask-commissions). + +The example repo `devcontainer.json` includes a feature to Coder' code-server web IDE which will be available in the Web Editors button in the workspace. It defaults to open in the `/workspaces` folder which is where the repo is cloned. + +Happy selling! 🦈 + +## Prerequisites + +### Infrastructure + +The VM you run Coder on must have a running Docker socket and the `coder` user must be added to the Docker group: + +```sh +# Add coder user to Docker group +sudo adduser coder docker + +# Restart Coder server +sudo systemctl restart coder + +# Test Docker +sudo -u coder docker ps +``` + +## Things to know + +### docker prune + +The template's `shutdown_script` runs `docker system prune -f` to clean up unused Docker resources when the workspace is stopped. This helps manage disk space on the host machine. Consider adjusting to make the workspace load faster if needed. + +### devcontainers-cli Coder module + +The template uses Coder's [devcontainers-cli module](https://registry.coder.com/modules/coder/devcontainers-cli) to install the `@devcontainers/cli` tool in the workspace. This is used to run Dev Containers inside the Docker-in-Docker container. + +### Docker-in-Docker script + +The template runs a custom script in the template called `docker-in-docker.sh` to do the following: + +- Detects if Coder workspace is accessed via host.docker.internal and applies networking fixes if needed. +- Enables IP forwarding and sets up NAT rules to allow proper traffic flow between devcontainers and host. +- Forwards relevant ports (from the agent URL, or 80/443 by default) to the host gateway. +- Starts Docker service and determines workspace bridge IP for DNS resolution. +- Installs and configures dnsmasq so devcontainers can resolve host.docker.internal to the workspace’s IP. +- Configures Docker to use the custom DNS server, ensuring hostname resolution works inside all containers. + +## Architecture + +This example uses the `codercom/enterprise-node:ubuntu` Docker image as a base image for the workspace. See image on [DockerHub](https://hub.docker.com/r/codercom/enterprise-node) and Dockerfile on [GitHub](https://github.com/coder/images/tree/main/images/node) It includes necessary tools like Docker and Node.js, which are required for running Dev Containers via the `@devcontainers/cli` tool. + +This template provisions the following resources: + +- Docker image (built by Docker socket and kept locally) +- Docker container (ephemeral) +- Docker volume (persistent on `/home/coder`) +- Docker volume (persistent on `/var/lib/docker`) + +This means, when the workspace restarts, any tools or files outside of the home directory or docker library are not persisted. + +For devcontainers running inside the workspace, data persistence is dependent on each projects `devcontainer.json` configuration. diff --git a/registry/sharkymark/templates/docker-devcontainer/main.tf b/registry/sharkymark/templates/docker-devcontainer/main.tf new file mode 100644 index 000000000..e684dc772 --- /dev/null +++ b/registry/sharkymark/templates/docker-devcontainer/main.tf @@ -0,0 +1,309 @@ +terraform { + required_providers { + coder = { + source = "coder/coder" + } + docker = { + source = "kreuzwerker/docker" + } + } +} + +locals { + username = data.coder_workspace_owner.me.name + + # Use a workspace image that supports rootless Docker + # (Docker-in-Docker) and Node.js. + workspace_image = "codercom/enterprise-node:ubuntu" +} + +variable "docker_socket" { + default = "" + description = "(Optional) Docker socket URI" + type = string +} + +data "coder_parameter" "repo_url" { + type = "string" + name = "repo_url" + display_name = "Git Repository" + description = "Enter the URL of the Git repository to clone into your workspace. This repository should contain a devcontainer.json file to configure your development environment. This default is a sample Flask web app to calculate sales commissions. 🦈" + default = "https://github.com/sharkymark/flask-commissions.git" + mutable = true +} + +provider "docker" { + # Defaulting to null if the variable is an empty string lets us have an optional variable without having to set our own default + host = var.docker_socket != "" ? var.docker_socket : null +} + +data "coder_provisioner" "me" {} +data "coder_workspace" "me" {} +data "coder_workspace_owner" "me" {} + +resource "coder_agent" "main" { + arch = data.coder_provisioner.me.arch + os = "linux" + startup_script = <<-EOT + set -e + + # Prepare user home with default files on first start. + if [ ! -f ~/.init_done ]; then + cp -rT /etc/skel ~ + touch ~/.init_done + fi + + # Add any commands that should be executed at workspace startup + # (e.g. install requirements, start a program, etc) here. + EOT + shutdown_script = <<-EOT + set -e + + # Clean up the docker volume from unused resources to keep storage + # usage low. + # + # WARNING! This will remove: + # - all stopped containers + # - all networks not used by at least one container + # - all images without at least one container associated to them + # - all build cache + docker system prune -a -f + + # Stop the Docker service. + sudo service docker stop + EOT + + # These environment variables allow you to make Git commits right away after creating a + # workspace. Note that they take precedence over configuration defined in ~/.gitconfig! + # You can remove this block if you'd prefer to configure Git manually or using + # dotfiles. (see docs/dotfiles.md) + env = { + GIT_AUTHOR_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name) + GIT_AUTHOR_EMAIL = "${data.coder_workspace_owner.me.email}" + GIT_COMMITTER_NAME = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name) + GIT_COMMITTER_EMAIL = "${data.coder_workspace_owner.me.email}" + } + + # The following metadata blocks are optional. They are used to display + # information about your workspace in the dashboard. You can remove them + # if you don't want to display any information. + # For basic resources, you can use the `coder stat` command. + # If you need more control, you can write your own script. + metadata { + display_name = "CPU Usage" + key = "0_cpu_usage" + script = "coder stat cpu" + interval = 10 + timeout = 1 + } + + metadata { + display_name = "RAM Usage" + key = "1_ram_usage" + script = "coder stat mem" + interval = 10 + timeout = 1 + } + + metadata { + display_name = "Home Disk" + key = "3_home_disk" + script = "coder stat disk --path $${HOME}" + interval = 60 + timeout = 1 + } + + metadata { + display_name = "CPU Usage (Host)" + key = "4_cpu_usage_host" + script = "coder stat cpu --host" + interval = 10 + timeout = 1 + } + + metadata { + display_name = "Memory Usage (Host)" + key = "5_mem_usage_host" + script = "coder stat mem --host" + interval = 10 + timeout = 1 + } + + metadata { + display_name = "Load Average (Host)" + key = "6_load_host" + # get load avg scaled by number of cores + script = <