In Progress
Unit 1, Lesson 21
In Progress

Furnishing Your Devcontainer

A brand new containerized development environment can be pretty spartan. Over time, you’ll furnish it with all the developer conveniences of home. Let’s look at how that process unfolds!

Video transcript & code

Furnishing Your Devcontainer

$ docker-compose up -d
Recreating sixmilebridge_app_1 ... done
Attaching to sixmilebridge_app_1
$ docker-compose exec app /bin/bash -l
root@a973373a5475:/workspace#

OK so we've got this container for app development. Or as we've been calling it, a "devcontainer".

One day we want to temporarily modify the .profile within this container.

If this were one of the project source files that are mounted into the container, we could just edit it on the host machine our favorite IDE.

But since it's only found inside the container, we need to edit it inside the container.

So out of habit we try to edit the file in vim and... oh, there's no vim.

root@a973373a5475:~# vim .profile

Or vi.

root@a973373a5475:~# vi .profile 
bash: vi: command not found

Or nano.

root@a973373a5475:~# nano .profile
bash: nano: command not found

Does this container have any editor at all on it? There's a handy trick for finding an editor in Debian-based linux systems:

We can run sensible-editor, which goes through a number of possibilities for an editor.

root@a973373a5475:/workspace# sensible-editor 
update-alternatives: error: no alternatives for editor
/usr/bin/sensible-editor: 25: /usr/bin/sensible-editor: editor: not found
/usr/bin/sensible-editor: 28: /usr/bin/sensible-editor: nano: not found
/usr/bin/sensible-editor: 31: /usr/bin/sensible-editor: nano-tiny: not found
/usr/bin/sensible-editor: 34: /usr/bin/sensible-editor: vi: not found
Couldn't find an editor!
Set the $EDITOR environment variable to your desired editor.

But in this case, it doesn't find any editor at all!

Which is frustrating. But it's actually not that weird!

Remember, we're building our devcontainer on the Ruby base image. Public Docker images are typically stripped down to the bare essentials. That's both to keep down file size, and to limit the security cross-section. The more executables a container has, the more opportunities for possible exploits.

Both of those concerns matter for deployment containers! But they aren't priorities for a devcontainer. We're only using this container for local development. This container gets optimized for developer convenience, not for deployment. That's why we make a distinction between deployment containers and devcontainers.

And having an editor at the container command-line is convenient.

OK, so we need an editor. Let's install vim.

root@a973373a5475:~# apt install vim
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package vim

Oops, we have to do an apt update first to get the package lists.

root@a973373a5475:~# apt update
Get:1 http://deb.debian.org/debian buster InRelease [122 kB]
Get:2 http://security.debian.org/debian-security buster/updates InRelease [65.4 kB]
Get:3 http://deb.debian.org/debian buster-updates InRelease [51.9 kB]
Get:4 http://security.debian.org/debian-security buster/updates/main amd64 Packages [267 kB]
Get:5 http://deb.debian.org/debian buster/main amd64 Packages [7907 kB]
Get:6 http://deb.debian.org/debian buster-updates/main amd64 Packages [9504 B]
Fetched 8422 kB in 3s (2853 kB/s)                        
Reading package lists... Done
Building dependency tree       
Reading state information... Done
35 packages can be upgraded. Run 'apt list --upgradable' to see them.

OK now we install vim.

root@a973373a5475:~# apt install vim
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  libgpm2 vim-common vim-runtime xxd
Suggested packages:
  gpm ctags vim-doc vim-scripts
The following NEW packages will be installed:
  libgpm2 vim vim-common vim-runtime xxd
0 upgraded, 5 newly installed, 0 to remove and 35 not upgraded.
Need to get 7425 kB of archives.
After this operation, 33.8 MB of additional disk space will be used.
Do you want to continue? [Y/n] 
Get:1 http://deb.debian.org/debian buster/main amd64 xxd amd64 2:8.1.0875-5 [140 kB]
Get:2 http://deb.debian.org/debian buster/main amd64 vim-common all 2:8.1.0875-5 [195 kB]
Get:3 http://deb.debian.org/debian buster/main amd64 libgpm2 amd64 1.20.7-5 [35.1 kB]
Get:4 http://deb.debian.org/debian buster/main amd64 vim-runtime all 2:8.1.0875-5 [5775 kB]
Get:5 http://deb.debian.org/debian buster/main amd64 vim amd64 2:8.1.0875-5 [1280 kB]
Fetched 7425 kB in 1s (5194 kB/s)
debconf: delaying package configuration, since apt-utils is not installed
Selecting previously unselected package xxd.
(Reading database ... 30676 files and directories currently installed.)
Preparing to unpack .../xxd_2%3a8.1.0875-5_amd64.deb ...
Unpacking xxd (2:8.1.0875-5) ...
Selecting previously unselected package vim-common.
Preparing to unpack .../vim-common_2%3a8.1.0875-5_all.deb ...
Unpacking vim-common (2:8.1.0875-5) ...
Selecting previously unselected package libgpm2:amd64.
Preparing to unpack .../libgpm2_1.20.7-5_amd64.deb ...
Unpacking libgpm2:amd64 (1.20.7-5) ...
Selecting previously unselected package vim-runtime.
Preparing to unpack .../vim-runtime_2%3a8.1.0875-5_all.deb ...
Adding 'diversion of /usr/share/vim/vim81/doc/help.txt to /usr/share/vim/vim81/doc/help.txt.vim-tiny by vim-runtime'
Adding 'diversion of /usr/share/vim/vim81/doc/tags to /usr/share/vim/vim81/doc/tags.vim-tiny by vim-runtime'
Unpacking vim-runtime (2:8.1.0875-5) ...
Selecting previously unselected package vim.
Preparing to unpack .../vim_2%3a8.1.0875-5_amd64.deb ...
Unpacking vim (2:8.1.0875-5) ...
Setting up libgpm2:amd64 (1.20.7-5) ...
Setting up xxd (2:8.1.0875-5) ...
Setting up vim-common (2:8.1.0875-5) ...
Setting up vim-runtime (2:8.1.0875-5) ...
Setting up vim (2:8.1.0875-5) ...
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vim (vim) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vimdiff (vimdiff) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/rvim (rvim) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/rview (rview) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/vi (vi) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/view (view) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/ex (ex) in auto mode
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/editor (editor) in auto mode
Processing triggers for hicolor-icon-theme (0.17-2) ...
Processing triggers for libc-bin (2.28-10) ...
Processing triggers for mime-support (3.62) ...
root@a973373a5475:~# 

And we can finally edit the file.

root@a973373a5475:~# vim .profile

Are we done now?

Well... not quite. Because the next time we modify something in our devcontainer config that triggers a container rebuild, we discover that all that work has been thrown away.

$ docker-compose down
Stopping sixmilebridge_app_1 ... done
Removing sixmilebridge_app_1 ... done
Removing network sixmilebridge_default
$ docker-compose up -d
Creating network "sixmilebridge_default" with the default driver
Creating sixmilebridge_app_1 ... done
$ docker-compose exec app /bin/bash -l
root@0e8a8e7f84b3:/workspace# vim ~/.profile
bash: vim: command not found

This is both the joy and the frustration of developing inside containers in a nutshell: on the one hand, we can freely mess around with our development environment, even with operating system files, knowing that we can always get back to a pristine state. But on the other hand... sometimes we make changes and forget to make them "sticky".

Let's make this change "sticky".

We'll open up the Dockerfile.

And we'll add vim to the list of packages installed as part of container creation.

FROM ruby:2.7.2
RUN apt-get update \
  && apt-get install -y vim yarnpkg \
  && ln -s /usr/bin/yarnpkg /usr/local/bin/yarn \
  && rm -rf /var/lib/apt/lists/*

And finally, something that has been implied in this series of videos, but this time we'll do it explicitly: We commit these changes to git, and push them to the upstream repository.

The presence of the vim editor is now a recorded, versioned fact about our project's containerized development environment. It will be true for any other host we develop on, as well as for any teammate who also uses the devcontainer. It's not intended to be the primary editor for working on the project! But from now on the absence of console editor won't be a missing stair when we want to make temporary changes to container-only files.

This isn't just about a missing editor. This is a technique we'll use every time we run into a missing tool in our devcontainer.

We'll notice the absence.

We'll test how to install it from the command line.

And then we'll capture the dependency for posterity in our project's devcontainer configuration.

Happy hacking!

Responses