A collection of Dockerfiles specifically curated for development purposes.
The base images in these Dockerfiles are parameterized, allowing them to be built on top of any compatible distro images such as Debian, Alpine, and others.
The overarching concept is to facilitate the creation of images that can be stacked atop each other, depending on specific requirements. For instance, one could build an image premised on the latest Ubuntu version, customized for developing in Golang and Python. This process allows for greater flexibility and customization in the development environment.
docker build golang/debian/ -t my_dev_container --build-arg BASE_IMAGE=ubuntu:latest
# Then use the newly built image as the base image of the next one.
docker build python/debian/ -t my_dev_container --build-arg BASE_IMAGE=my_dev_containerThese images are designed for local development and testing. They incorporate a user within the container that utilizes a high-range id of 61000. This is done to avoid any potential overlap with host user IDs.
The following command builds a shell image on top of the Ubuntu image. It also set the username of the container user as dev.
docker build shell/debian -t your_registry/ubuntu-shell \
--build-arg BASE_IMAGE=ubuntu:latest \
--build-arg USER=devOr it could be built on top of any other compatible(same distro, e.g. debian) image.
docker build shell/debian -t your_registry/shell \
--build-arg BASE_IMAGE=your_registry/dev:latest \
--build-arg USER=devIt's crucial to note that the shell and the base image should be compatible. For instance, the Dockerfile located at
shell/debian/Dockerfileis compatible with any debian-like distributions. Therefore, it can be readily used on top of other systems such as Ubuntu, Debian, and so forth. This compatibility allows for seamless integration and efficient usage of resources, enhancing your overall development experience.
Using Linux docker engine where development within a container is preferred and the host's source code directory needs to be mounted as a volume, write permission must be granted to the container's user id. This permission management is usually handled by Docker Desktop in MacOS and Windows. However, when working with a Docker engine in an operating system such as Linux, this task falls upon the user.
A solution to this is to create a container user that shares the same USER_ID and GROUP_ID as the host's current user. This can effectively manage permissions and ensure seamless operation.
Building a shell using the host's current user and group IDs:
docker build shell/debian -t pv/ubuntu-shell \
--build-arg BASE_IMAGE=ubuntu:latest \
--build-arg USER_ID=$(id -u ${USER}) \
--build-arg GROUP_ID=$(id -g ${USER})The build.sh script is a versatile tool that you can use to craft images either on your local machine or within a cicd pipeline. Here are some examples to get you started, along with a few predefined images and their layers which can be quickly implemented.
To clean up the environment, the easiest way is to prune the system which deletes all the images, containers, and networks that are not being used.
docker system prune -aThen, pull the ubuntu image to start fresh.
docker pull ubuntu:latestBeyond its basic functionalities, build.sh offers the capability to create two additional shell images on top of the pv/dev image. By using the -s option, you can add a shell layer to pv/dev that incorporates a container's user. Meanwhile, the -c option allows you to construct a shell layer with a container's user that shares the same USER_ID and GROUP_ID as the host's current user.
Note: The order of the layers is crucial. Layers that depend on others should be arranged accordingly. For instance, the sequence could be ...golang,hugo,... .
To illustrate, let's create an image named my-image based on ubuntu:latest. This image will then have a series of layers built on top of it. Finally, a shell image with a user named dev will be constructed. Here's how you can achieve this:
./build.sh -d debian -b ubuntu:latest \
-i python,nodejs,golang,awscli,aws_cdk,shell \
-t my-registry/my-image:latest \
-u devOn a Linux docker engine, use the
-c | --current-useroption to generate a container user with the same USER ID as the host's user. The-coption adopts the current user's USER_ID and GROUP_ID, while-sapplies 61000 (a high range ID) for both USER_ID and GROUP_ID. MacOS and Windows users need not worry about this as Docker Desktop manages access to the host's file system.
Recipes are defined in build.sh. They are a collection of layers that can be built on top of each other. For all recipes by default, the user is set to dev and the base image is ubuntu:latest.
If a recipe is dependent on another recipe (as the base image), then the base recipe should be built first. For example, the anaconda recipe is dependent on the anaconda-base recipe. Therefore, the anaconda-base recipe should be built first.
docker pull ubuntu:latest \
&& ./build.sh -r ubuntu \
&& ./build.sh -r anaconda-base \
&& ./build.sh -r anaconda \
&& ./build.sh -r development-base \
&& ./build.sh -r development \
&& ./build.sh -r hugo
Note: In above, pulling the ubuntu:latest image at first is optional, as .build.sh script will pull it if it's not already available.
If you're targeting multiple platforms for your image, you'll need to use the --platform option along with a list of the desired platforms. Here's how you can do this:
./build.sh -r development \
--platform linux/amd64,linux/arm64 \
--registry $DOCKERHUB_USERNAME \
--pushThe command builds the development recipe for both amd64 and arm64 architectures. Once the image is built, it will be pushed to the Docker Hub repository.
Building anaconda recipe using --no-cache to force a rebuild of the image without using the docker's build cache.
./build.sh -r anaconda --no-cache