Ghost in the Shell

A coding competition

View on GitHub

Docker

Docker is a tool for building and delivering software in packages called containers. This challenge will require participants to build Docker images as part of their submission. If you’re not familiar with Docker, this will provide a basic overview as well guide you through the process of building your first container.

Getting Docker

To get started, you’ll want to download and install Docker Desktop. The recommended way of getting Docker is through Docker Hub and creating an account. However, if you’d like to skip that step, you can download the installer directly for Windows or Mac.

Downloading Images

Most public Docker images are hosted on Docker Hub and can easily be downloaded to run on your machine with the docker pull command, e.g.

docker pull node

Will download the latest stable version of the image node (the official Node.js image) to your machine.

Querying images

You may many images on your machine at any moment, probably very many if you’re actively building images (see below). You manage images in your Docker image cache using the docker image command, e.g.

docker image ls

Will show all containers in your image cache. To cleanup any unused images, simply run

docker image prune

Creating Containers

To execute the software contained within an image you must create a container for it. This can be done with the docker create command, e.g.

docker create --name my-mongo mongo

Will create a new container for executing MongoDB and assigns the locally unique name my-mongo, but crucially, this command will not start the container.

Running Containers

To start a previously created container like the one we created above, use the docker start command, e.g.

docker start --name my-mongo

Will start the container named my-mongo in a background process. The container will stop by itself once its main process exits.

Note that the create and start steps may usually be combined into a single call using the docker run command, e.g.

docker run --name my-mongo mongo

Will create a new container from the image mongo, assign it the name my-mongo, start that container, and will show all the logs of that container in the current console (unlike docker start, the docker run command will ‘attach’ to the created container, use --detach/-d to detach).

The above commands are useful for service-type images, but for many images, you’ll want to use something like this:

docker run --rm -it node node

This will create a new container from the image node, assign it a random name (since we didn’t specify one), attach to the container to current console, it will pass the command node to that container to be executed (in this case, it will launch the Node.Js REPL), and finally, it cause the container to be deleted once it’s main process exits.

Querying Containers

To get a list of containers, use the docker ps command, e.g.

docker ps

Will show a list of all currently running containers. To include stopped containers as well, use

docker ps -a

Stopping and Removing Containers

To stop a running container, simply use the docker stop command, e.g.

docker stop my-mongo

Will attempt to gracefully stop the running container, my-mongo. Unless created with the --rm option, the container will remain on your machine to be restarted later.

To remove a stopped container, use the docker rm command, e.g.

docker rm my-mongo

Will attempt to remove the stopped container, my-mongo. You can also remove all stopped containers using

docker container prune

Persisting Data with Volumes

TBD

Publishing Container Ports

Networking in Docker must be done explicitly. If you’ve been following along up until now, you won’t have been able to connect to any of the MongoDB containers we’ve created so because we haven’t enabled any network configuration.

To expose a containerized application to the outside world (i.e. to other applications on your machine or to other machines) you’ll need to publish a port, for example:

docker run --name my-mongo --publish 11220:27017 mongo

Will create a container from the image mongo, name it my-mongo and publish the container’s port 27017 (MongoDB’s default port) to the host machine’s port 11220, which will allow other non-Docker applications on the same machine to connect to the MongoDB database using localhost:11220, e.g. using the MongoDB shell,

mongo localhost:11220

Networking Containers

However, you frequently need other docker containers to be able to connect to each other. This is done by first exposing ports (usually specified in the Dockerfile but this may also be done when you create the container through docker create --expose or docker run --expose). Then you’ll need to create a virtual network to connect these containers using the docker network command, e.g.

docker network create mongo-network

Will create a new Docker network named mongo-network for connecting networks together. You’ll then connect the existing container to the network, e.g.

docker network connect --alias mongo mongo-network my-mongo

Will connect the container my-mongo to the container mongo-network using the alias mongo (this is the name that other containers in this network will be able to see this network as). You can also connect new containers at the time of creation via docker create and docker run, e.g.

docker run \
    --detach \
    --name my-mongo-express \
    --network mongo-network \
    --network-alias mongo-express \
    --env ME_CONFIG_MONGODB_SERVER=mongo \
    --publish 8081:8081 \
    mongo-express

Will create a new container from the image mongo-express, assign it the name my-mongo-express, attach it to the network mongo-network, assign it an network alias of mongo-express, set an environment variable ME_CONFIG_MONGODB_SERVER with a value of mongo (this is the network alias of the mongo server we connected earlier), and publish the container port 8081 using the host port 8081, and it will start that container detached from the current console. You can now view your mongo-express dashboard by navigating to http://localhost:8081/ in your browser.

You can also remove all unused networks by using

docker network prune

Orchestration with Docker Compose

You might be thinking this was a lot work just to get a couple containers running on your machine, and you’re right. Fortunately, Docker provides a convenient utility named Docker Compose that simplifies a lot of this by allowing you to declaratively define container and network configuration in one place. For example, the following docker-compose.yml file is roughly equivalent to what we did in the previous section to setup a MongoDB server and Mongo Express dashboard:

version: "3.7"
services:
    mongo:
        image: mongo
        ports:
            - "11220:27017"
    mongo-express:
        image: mongo-express
        ports:
            - "8081:8081"
        environment:
            - ME_CONFIG_MONGODB_SERVER=mongo

Save this file in a particular folder and then in this same folder simply execute

docker-compose up

To quickly spin up all these services at once as well as review any logs from the services. These services will typically continue to stick around even after you detach the console, so you can use

docker-compose down

To quickly tear down the containers and networks created by the up command. If you’ve modified the docker-compose.yml file in the mean time, there might be orphaned objects that won’t be deleted by down. You can usually clean this up very easily destroy all unused objects using

docker system prune

Building Containers

TBD