Skip to content

Commit

Permalink
Add a guide on prebuilds (#277)
Browse files Browse the repository at this point in the history
* Scaffold post, enable multiple authors

* Add initial content

* Guide content updates

* Feedback

* Content and wording

* fix displaying author(s)

* Typo

* Link between guides

* Feedback

* Typo

---------

Co-authored-by: bamurtaugh <[email protected]>
Co-authored-by: Josh Spicer <[email protected]>
  • Loading branch information
3 people authored Sep 5, 2023
1 parent 44bb154 commit aa45280
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 12 deletions.
10 changes: 8 additions & 2 deletions _layouts/post.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
layout: singlePage
---
<div class="container">
<p>{{ page.date | date_to_string }} - <a href="{{ page.authorUrl }}">{{ page.author }}</a></p>
<p>{{ page.date | date_to_string }} -
{% assign i = 0 %}
{% for a in page.author %}
<a href="{{ page.authorUrl[i] }}">{{ page.author[i] }}</a>
{% assign i = i | plus:1 %}
{% endfor %}
</p>

{{ content }}
</div>
</div>
6 changes: 4 additions & 2 deletions _posts/2022-11-01-author-a-feature.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
layout: post
title: "Authoring a Dev Container Feature"
author: "@joshspicer"
authorUrl: https://github.com/joshspicer
author:
- "@joshspicer"
authorUrl:
- https://github.com/joshspicer
---

Development container ["Features"](/features) are self-contained, shareable units of installation code and development container configuration. We [define a pattern](/implementors/features-distribution) for authoring and self-publishing Features.
Expand Down
10 changes: 6 additions & 4 deletions _posts/2022-12-16-dockerfile.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
layout: post
title: "Using Images, Dockerfiles, and Docker Compose"
author: "@chuxel"
authorUrl: https://github.com/chuxel
author:
- "@chuxel"
authorUrl:
- https://github.com/chuxel
---

When creating a development container, you have a variety of different ways to customize your environment like ["Features"](/features) or [lifecycle scripts](/implementors/json_reference/#lifecycle-scripts). However, if you are familiar with containers, you may want to use a [Dockerfile](/guide/dockerfile#dockerfile) or [Docker Compose / Compose](/guide/dockerfile#docker-compose) to customize your environment. This article will walk through how to use these formats with the Dev Container spec.
Expand Down Expand Up @@ -44,7 +46,7 @@ That's it! When you start up your Dev Container, the Dockerfile will be automati

Better yet, you can can use a Dockerfile as a part of authoring an image you can share with others. You can even **add Dev Container settings and metadata right into the image itself**. This avoids having to duplicate config and settings in multiple devcontainer.json files and keeps them in sync with your images!

See the reference on **[pre-building](/implementors/reference/#prebuilding)** to learn more!
See the guide on **[pre-building](/_posts/2023-08-22-prebuild.md)** to learn more!

## <a href="#docker-compose" name="docker-compose" class="anchor"> Using Docker Compose </a>

Expand Down Expand Up @@ -140,4 +142,4 @@ volumes:

Finally, as in the Dockerfile example, you can use this same setup to create a Dev Container image that you can share with others. You can also add Dev Container settings and metadata right into the image itself.

See the reference on **[pre-building](/implementors/reference/#prebuilding)** to learn more!
See the guide on **[pre-building](/_posts/2023-08-22-prebuild.md)** to learn more!
6 changes: 4 additions & 2 deletions _posts/2023-02-15-gitlab-ci.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
layout: post
title: "Working with GitLab CI"
author: "@raginjason"
authorUrl: https://github.com/raginjason
author:
- "@raginjason"
authorUrl:
- https://github.com/raginjason
---

For simple use cases you can use your development container (dev container) for CI without much issue. Once you begin using more advanced dev container functionality such as [Features](/features), you will need dev container tooling in your CI pipeline. While GitHub CI has the [devcontainers-ci GitHub Action](https://github.com/marketplace/actions/devcontainers-ci), there is no such analog in GitLab CI. To achieve the goal of using your dev container in GitLab CI, the container must be pre-built.
Expand Down
6 changes: 4 additions & 2 deletions _posts/2023-06-14-feature-authoring-best-practices.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
layout: post
title: "Best Practices: Authoring a Dev Container Feature"
author: "@joshspicer"
authorUrl: https://github.com/joshspicer
author:
- "@joshspicer"
authorUrl:
- https://github.com/joshspicer
---

Last November I wrote about the basics around [authoring a Dev Container Feature](/guide/author-a-feature). Since then, [hundreds](https://containers.dev/features) of Features have been written by the community. The flexibility of Features has enabled a wide variety of use cases, from installing a single tool to setting up specific aspects of a project's development environment that can be shared across repositories. To that effect, many different patterns for Feature authorship have emerged, and the core team has learned a lot about what works well and what doesn't.
Expand Down
138 changes: 138 additions & 0 deletions _posts/2023-08-22-prebuild.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
layout: post
title: "Speed Up Your Workflow with Prebuilds"
author:
- "@bamurtaugh"
- "@craiglpeters"
authorUrl:
- "https://github.com/bamurtaugh"
- "https://github.com/craiglpeters"

---

Getting dev containers up and running for your projects is exciting - you've unlocked environments that include all the dependencies your projects need to run, and you can spend so much more time on coding rather than configuration.

Once your dev container has everything it needs, you might start thinking more about ways to optimize it. For instance, it might take a while to build. Maybe it takes 5 minutes. Maybe it takes an hour!

You can get back to working fast and productively after that initial container build, but what if you need to work on another machine and build the container again? Or what if some of your teammates want to use the container on their machines and will need to build it too? It'd be great to make the build time faster for everyone, every time.

After configuring your dev container, a great next step is to **prebuild your image**.

In this guide, we'll explore what it means to prebuild an image and the benefits of doing so, such as speeding up your workflow, simplifying your environment, and pinning to specific versions of tools.

We have a variety of tools designed to help you with prebuilds. In this guide, we'll explore two different repos as examples of how our team uses different combinations of these tools:
* The prebuilt image for the [Kubernetes repo](https://github.com/craiglpeters/kubernetes-devcontainer) developed by one of our spec maintainers [Craig](https://github.com/craiglpeters)
* The prebuilt images we host in the [devcontainers/images](https://github.com/devcontainers/images/tree/main/src) repo as part of the dev container spec


## <a href="#what-is-a-prebuild" name="what-is-a-prebuild" class="anchor"> What is prebuilding? </a>

We should first define: What is prebuilding?

If you're already using dev containers, you're likely already familiar with the idea of building a container, where you package everything your app needs to run into a single unit.

You need to build your container once it has all the dependencies it needs, and rebuild anytime you add new dependencies. Since you may not need to rebuild often, it might be alright if it takes a while for that initial build. But if you or your teammates need to use that container on another machine, you'll need to wait for it to build again in those new environments.

> **Note:** The [dev container CLI doc](/_implementors/reference.md#prebuilding) is another great resource on prebuilding.
### <a href="#prebuilt-codespaces" name="prebuilt-codespaces" class="anchor"> Prebuilt Codespaces </a>

You may have heard (or will hear about) [GitHub Codespaces prebuilds](https://docs.github.com/en/codespaces/prebuilding-your-codespaces/about-github-codespaces-prebuilds). Codespaces prebuilds are similar to prebuilt container images, with some additional focus on the other code in your repo.

GitHub Codespaces prebuilds help to speed up the creation of new codespaces for large or complex repositories. A prebuild assembles the main components of a codespace for a particular combination of repository, branch, and `devcontainer.json` file.

By default, whenever you push changes to your repository, GitHub Codespaces uses GitHub Actions to automatically update your prebuilds.

You can learn more about codespaces prebuilds and how to manage them in the [codespaces docs]((https://docs.github.com/en/codespaces/prebuilding-your-codespaces/about-github-codespaces-prebuilds)).

## <a href="#how-to" name="how-to" class="anchor"> How do I prebuild my image? </a>

We try to make prebuilding an image and using a prebuilt image as easy as possible. Let's walk through the couple of steps to get started.

**Prebuilding an image:**
* Install the [Dev Container CLI](/_implementors/reference.md):

```bash
npm install -g @devcontainers/cli
```

* Build your image and push it to a container registry (like the [Azure Container Registry](https://learn.microsoft.com/azure/container-registry/container-registry-get-started-docker-cli?tabs=azure-cli), [GitHub Container Registry](https://docs.github.com/packages/working-with-a-github-packages-registry/working-with-the-container-registry#pushing-container-images), or [Docker Hub](https://docs.docker.com/engine/reference/commandline/push)):

```bash
devcontainer build --workspace-folder . --push true --image-name <my_image_name>:<optional_image_version>
```

* You can automate pre-building your image by scheduling the build using a DevOps or continuous integration (CI) service like GitHub Actions. We've created a [GitHub Action](https://github.com/marketplace/actions/devcontainers-ci) and [Azure DevOps task](https://marketplace.visualstudio.com/items?itemName=devcontainers.ci) to help with this.
**Using a prebuilt image:**
* Determine the published URL of the prebuilt image you want to use
* Reference it in your `devcontainer.json`, Dockerfile, or Docker Compose file
* In our previous guide on ["Using Images, Dockerfiles, and Docker Compose,"](/_posts/2022-12-16-dockerfile.md) we also showed how you can use prebuilt images, Dockerfiles, or Docker Compose files for your configurations
### <a href="#prebuild-examples" name="prebuild-examples" class="anchor"> Prebuild Examples </a>
As mentioned above, let's walk through a couple examples of these steps, one using Craig's [Kubernetes repo](https://github.com/craiglpeters/kubernetes-devcontainer), and the other using our [devcontainers/images](https://github.com/devcontainers/images/tree/main/src) repo.
**Kubernetes**
* It's a fork of the main [Kubernetes repo](https://github.com/kubernetes/kubernetes) and contributes a prebuilt dev container for use in the main Kubernetes repo or any other forks
* The dev container it's prebuilding is defined in the [.github/.devcontainer folder](https://github.com/craiglpeters/kubernetes-devcontainer/tree/master/.github/.devcontainer)
* Any time a change is made to the dev container, the repo currently uses the dev container [GitHub Action](https://github.com/craiglpeters/kubernetes-devcontainer/actions/workflows/devcontainer-build-and-push.yml) to build the image and push it to GHCR
* You can check out its latest prebuilt image in the [`Packages` tab](https://github.com/users/craiglpeters/packages/container/package/kubernetes-devcontainer) of its GitHub Repo. In this tab, you can see its GHCR URL is `ghcr.io/craiglpeters/kubernetes-devcontainer:latest`
* The main Kubernetes repo and any fork of it can now define a `.devcontainer` folder and [reference this prebuilt image](https://github.com/craiglpeters/kubernetes-devcontainer/blob/master/.devcontainer/devcontainer.json#L7) through: `"image": "ghcr.io/craiglpeters/kubernetes-devcontainer:latest"`
**Dev container spec images**
* This repo prebuilds a variety of dev containers, each of which is defined in their individual folders in the [src folder](https://github.com/devcontainers/images/tree/main/src)
* As an example, the Python image is defined in the [src/python/.devcontainer](https://github.com/devcontainers/images/tree/main/src/python/.devcontainer) folder
* Any time a change is made to the dev container, the repo uses a [GitHub Action](https://github.com/devcontainers/images/actions/workflows/push.yml) to build the image and push it to MCR
* Using the Python image as an example again, its MCR URL is `mcr.microsoft.com/devcontainers/python`
* Any projects can now reference this prebuilt image through: `"image": "mcr.microsoft.com/devcontainers/python"`
## <a href="#where" name="where" class="anchor"> Where do the dependencies come from? </a>
If your `devcontainer.json` is as simple as just an `image` property referencing a prebuilt image, you may wonder: How can I tell what dependencies will be installed for my project? And how can I modify them?
Let's walk through the Kubernetes prebuild as an example of how you can determine which dependencies are installed and where:
* **Start at your end user dev container**
* We start at the [`.devcontainer/devcontainer.json`](https://github.com/craiglpeters/kubernetes-devcontainer/blob/master/.devcontainer/devcontainer.json) designed for end use in the Kubernetes repo and other forks of it
* It sets a few properties, such as `hostRequirements`, `onCreateCommand`, and `otherPortsAttributes`
* We see it references a prebuilt image, which will include dependencies that don't need to be explicitly mentioned in this end user dev container. Let's next go explore the dev container defining this prebuilt image
* **Explore the dev container defining your prebuilt image**
* We next open the config that defines the prebuilt image. This is contained in the [`.github/.devcontainer` folder](https://github.com/craiglpeters/kubernetes-devcontainer/tree/master/.github/.devcontainer)
* We see there's a [`devcontainer.json`](https://github.com/craiglpeters/kubernetes-devcontainer/blob/master/.github/.devcontainer/devcontainer.json). It's much more detailed than the end user dev container we explored above and includes a variety of [Features](/_implementors/features.md)
* **Explore content in the prebuilt dev container's config**
* Each Feature defines additional functionality
* We can explore what each of them installs in their associated repo. Most appear to be defined in the [devcontainers/features repo](https://github.com/devcontainers/features/tree/main/src) as part of the dev container spec
* **Modify and rebuild as desired**
* If I'd like to add more content to my dev container, I can either modify my end user dev container (i.e. the one designed for the main Kubernetes repo), or modify the config defining the prebuilt image (i.e. the content in Craig's dev container)
* For universal changes that anyone using the prebuilt image should get, update the prebuilt image
* For more project or user specific changes (i.e. a language I need in my project but other forks won't necessarily need, or user settings I prefer for my editor environment), update the end user dev container
* Features are a great way to add dependencies in a clear, easily packaged way

## <a href="#benefits" name="benefits" class="anchor"> Benefits </a>

There are a variety of benefits (some of which we've already explored) to creating and using prebuilt images:
* Faster container startup
* Pull an already built dev container config rather than having to build it freshly on any new machine
* Simpler configuration
* Your `devcontainer.json` can be as simple as just an `image` property
* Pin to a specific version of tools
* This can improve supply-chain security and avoid breaks
## <a href="#tips" name="tips" class="anchor"> Tips and Tricks </a>
* We explored the prebuilt images we host as part of the spec in [devcontainers/images](https://github.com/devcontainers/images/tree/main/src). These can form a great base for other dev containers you'd like to create for more complex scenarios
* The spec has a concept of Development container "Templates" which are source files packaged together that encode configuration for a complete development environment
* A Template may be as simple as a `devcontainer.json` referencing a prebuilt image, and a `devcontainer-template.json`
* You can learn more about Templates in our [Templates documentation](../_implementors/templates.md)
* You can adopt and iterate on [existing Templates](../templates.html) from the spec and community, or you can [create and share your own](../_implementors/templates-distribution.md)
* You can include Dev Container configuration and Feature metadata in prebuilt images via [image labels](https://docs.docker.com/config/labels-custom-metadata/). This makes the image self-contained since these settings are automatically picked up when the image is referenced - whether directly, in a `FROM` in a referenced Dockerfile, or in a Docker Compose file. You can learn more in our [reference docs](/_implementors/reference.md#metadata-in-image-labels)
* You can use multi-stage Dockerfiles to create a prod container from your dev container
* You'd typically stat with your prod image, then add to it
* Features provide a quick way to add development and CI specific layers that you wouldn't use in production
* For more information and an example, check out our [discussion on multi-stage builds](https://github.com/orgs/devcontainers/discussions/4#discussioncomment-4152158)

## <a href="#feedback" name="feedback" class="anchor"> Feedback and Closing </a>

We hope this guide will help you optimize your dev container workflows! We can't wait to hear your tips, tricks, and feedback. How are you prebuilding your images? Would anything in the spec or tooling make the process easier for you?
If you haven't already, we recommend joining our dev container [community Slack channel](https://aka.ms/dev-container-community) where you can connect with the dev container spec maintainers and community at large. If you have any feature requests or experience any issues as you use the above tools, please feel free to also open an issue in the corresponding repo in the [dev containers org](https://github.com/devcontainers) on GitHub.

0 comments on commit aa45280

Please sign in to comment.