The code we write is rarely run solely on our own laptops. We usually write software that will ultimately run on other machines. Docker gives us a way to package our app, and all its dependencies, into a 'shippable' container that will run on another machine.
docker run hello-world
Once docker is installed in your shell you will have the docker
command that you can use to build and run containers.
With a Dockerfile
you can define your own container. You will usually start with a 'base-image' like the Alpine container we have looked at already. You can then declare a series of scripts or commands that will build out your own docker image. We are going to dockerise an app. Use the example below or one of your own projects.
cd
into the project folder and create a plain text file called Dockerfile
with no extension.
FROM ruby:2.6.0
RUN apt-get update && apt-get install -y net-tools
RUN mkdir /app
WORKDIR /app
COPY Gemfile* .
RUN bundle install
COPY ./lib/ .
EXPOSE 4567
CMD ["ruby", "server.rb"]
In this file we use a base image called ruby:2.6.0
. We RUN
a command that installs a Unix package called 'net-tools'. We then create a new folder called /app
at the root of the container. We then 'move' ourselves into that /app
folder. From here we COPY
from the local file system the lib
folder into the working directory inside the container which will become /app/lib
. We then make expose the containers' PORT
4567
to the 'host' system. Finally the container has a command that it will run when the container is started, which for us is ruby server.rb
this will start the ruby web program noughts and crosses.
docker build -t noughts-and-crosses .
This command is invoking docker
to build
a container and -t
tag it with the label noughts-and-crosses
from the Dockerfile that can be located in the current folder .
the period or dot means 'here' in the current operating system, your laptop, the 'host'.
docker run noughts-and-crosses
Easy. Now if you visit http://localhost:4567
you'll...
... not see anything 🙁 yet in your shell you should see the ruby process is running. Noughts and crosses is running, however it is sealed within that container that you just built. We did expose the PORT
4567
, what we need to do is map that port to the same port on our 'host' computer. Stop the container with control+c.
docker run -p 4567:4567 noughts-and-crosses
That should enable us to use that webapp at http://localhost:4567
. Some other things to experiment with. At the moment when we start a container it takes over our shell. We can run containers in 'detached' mode with the -d
flag, which means we can still use our shell.
docker run -d -p 4567:4567 noughts-and-crosses
If we wanted to see what containers are running we can list them all with the command
docker ps
Can you see the CONTAINER ID? You can use that ID to address it. We can also interact and enter the running containers like we did with the Alpine image. You will have a unique ID so replace 1c784864b06e
with your container ID.
docker exec -it 1c784864b06e sh
Here we invoke docker
to exec
execute a command in an -it
interactive session for container 1c784864b06e
and the command we want to execute is sh
which will start a shell, and we'll be 'in' the container. Can you find out which flavour of Unix this base-image is built on (hint uname -all
)?
You can run a container! Awesome. Now how about running 2, or 3 or 4 containers together. As this gets a bit unwieldy when you're just typing in shell commands, there is another tool called docker-compose
that lets you define all the config for a set of containers in a file, then you can docker-compose up
and start all the containers together.
This is great for example when you want to run a database, an API server and maybe a frontend project. We should start with just a pair of containers working together. You should have docker-compose
installed along with docker for desktop.
Create a docker-compose.yaml
file and lets define 2 containers.
version: "3.9"
services:
db:
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:latest
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
In the folder where you created the docker-compose.yaml
run docker-compose up
. Oh wow a locally running wordpress http://localhost:8000
with zero effort, thank you very much that will do nicely. But before you go setting up your wordpress, there is one problem. As soon as the containers stop, the database, and all your wordpress config will disappear. To solve this problem docker will let us create 'volumes' that will persist data between the life cycle of containers coming up and going down. That way we can write data into our SQL database stop the containers, start them again, and retrieve our data. To create a couple of volumes we are going to add the following config to the 'services' object in our docker-compose.yaml
file (volumes:
should be at the root of the yaml, same level as version and services, so no spaces).
volumes:
db_data: {}
wordpress_data: {}
Then update the db:
service to mount this external volume.
services:
db:
# other properties like image, environment etc
volumes:
- db_data:/var/lib/mysql
wordpress:
# other properties like image, ports, depends_on etc
volumes:
- wordpress_data:/var/www/html
Now when we start data will be persisted between container restarts. You can see the volumes docker has created by running the command:
docker volume ls
You can remove one of those volumes with docker volume rm CONTAINER ID
. Whats really interesting is we can use this same method to 'mount' areas of our local file system into a container. So for example you can create a local folder named themes
and download a wordpress theme. You can then mount that local folder into the container and use that theme in your wordpress.
wordpress:
# other properties like image, ports, depends_on etc
volumes:
- wordpress_data:/var/www/html
- ./themes:/var/www/html/wp-content/themes
If you tag your docker images with your docker-username/container-label
and then push them you'll see your images online.
docker push your_docker_username/some_image_label