Up to this point we have created containers using images pulled down from the internet. Then made changes to those containers. As long we didn’t delete the containers the changes persisted. We can also save the container permanently to images.
To demonstrate this let’s create a container using docker run --name myWeb1 -p 8080:80 -it ubuntu
which will launch a shell in our container.
docker run --name myWeb1 -p 8080:80 -it ubuntu
Once in the shell install the following.
apt-get update apt-get install curl -y apt-get install vim -y apt-get install apache2 -y service apache2 start // you can chain items together by putting && between them as follows apt-get update && apt-get install curl -y && apt-get install vim -y && apt-get install apache2 -y && service apache2 start
Modify the default index.html file by entering vim var/www/html/index.html
.
Once in vim press “1000dd” to delete all the content.
Then press “i” to insert your own html. Press “esc” and “:wq” to save.
Finally press ctrl+p and ctrl+q to exit to the command line while still leaving the container running.
vim var/www/html/index.html
Navigate to localhost:8080
and you will see your changes.
Next save the changes to an image using docker commit [CONTAINER] [REPOSITORY[:TAG]]
docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 476ddd6b0f5f ubuntu "/bin/bash" 5 minutes ago Up 5 minutes 0.0.0.0:8080->80/tcp myWeb1 docker commit myWeb1 my_web1_image sha256:e585e109dc42fa56cdd5408bd1d0332f06ebfb368df3901557aa525a012bb72b
Now that we have created our own image we can use it instead of ubunto. Create another container using docker run --name myWeb2 -p 8081:80 -t -i my_web1_image
This will launch your containers shell. Once again start the apache server by entering service apache2 start
.
Press ctrl+p and ctrl+q to exit to the command line while still leaving the container running.
Navigate to localhost:8081 and notice all your changes are in the new container. Cool!
Dockerfile
Making changes to a container and creating an image from it is one way to create a custom image, but there is a better way using a “Dockerfile”. Find full documentation on Dockerfiles here.
First create a directory to put your Dockerfile in as well as any source files. Navigate to that directory and create a “Dockerfile” with vim.
mkdir docker_file_example cd docker_file_example vim Dockerfile
Once in vim use the following code for your “Dockerfile”. The file contains the following: FROM
pulls the latest ubuntu image down, RUN
runs shell commands. ARG DEBIAN_FRONTEND=noninteractive
turns off the questions that were asked about location and timezone for an Apache installation. Then we set a WORKDIR to copy our source files to. Finally the remaining command starts the Apache server.
#get an image you want FROM ubuntu:latest #install all the tools you might want to use in your container RUN apt-get update RUN apt-get install curl -y RUN apt-get install vim -y #the following ARG turns off the questions normally asked for location and timezone for Apache ARG DEBIAN_FRONTEND=noninteractive RUN apt-get install apache2 -y #change working directory to root of apache webhost WORKDIR var/www/html #copy your files, if you want to copy all use COPY . . COPY index.html index.html #now start the server CMD ["apachectl", "-D", "FOREGROUND"]
Third create an index.html file in the same directory and add whatever html you want to serve.
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Dude your first Dockerfile example</title> </head> <body> <h2>You did it dude!</h2> <p>This is to inform you that you successfully created a container with ubuntu, curl, vim, apache server, a custom html file and started the server. Congratulations!</p> </body> </html>
Forth build your image as follows.
// build image using the "Dockerfile" docker image build -t my_dockerfile_image .
And finally use your image to create a new container. After it is built navigate to localhost:8082 to see your results.
//then use the image to create new container docker container run --name myWeb3 -d -p 8082:80 my_dockerfile_image dbdbf8236112a88bd8224fbde5ee7488d88d9b4b283c944b985e3ed38b7b53db
We can now save this image to our docker hub account. First we will rename it by entering the following docker image tag [imageName:tag] [dockerHubAccount/imageName:tag]
then we will push it to our docker hub account. Before pushing make sure you are logged into docker hub.
Note: If you don’t have a docker hub account your get one here for free.
docker image tag my_dockerfile_image:latest jimmysoftllc/my_dockerfile_image:latest docker push jimmysoftllc/my_dockerfile_image:latest
Then we can create another container using our image from docker hub.
docker container run --name myWeb4 -d -p 8083:80 jimmysoftllc/my_dockerfile_image:latest
docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2396543017a9 jimmysoftllc/my_dockerfile_image:latest "apachectl -D FOREGR…" 5 seconds ago Up 5 seconds 0.0.0.0:8083->80/tcp myWeb4 dbdbf8236112 my_dockerfile_image "apachectl -D FOREGR…" 4 minutes ago Up 4 minutes 0.0.0.0:8082->80/tcp myWeb3 21169bd38d00 my_web1_image "/bin/bash" 21 minutes ago Up 21 minutes 0.0.0.0:8081->80/tcp myWeb2 476ddd6b0f5f ubuntu "/bin/bash" 29 minutes ago Up 29 minutes 0.0.0.0:8080->80/tcp myWeb1
Inspect the image
You can inspect your image and see all the image metadata. In the metadata you will see all the configurations we did using the Dockerfile. Notice that our run command "Cmd": ["apachectl","-D","FOREGROUND"]
is in the same format as in our Dockerfile. I was curious why it required such a strange format and now it makes sense that it is a key value pair.
docker image inspect jimmysoftllc/my_dockerfile_image [ { "Id": "sha256:fc63b4fc7ec94c3c8da763cc875910777dc5e8485969e3f3d73718913e2fb39d", "RepoTags": [ "jimmysoftllc/my_dockerfile_image:latest", "my_dockerfile_image:latest" ], "RepoDigests": [ "jimmysoftllc/my_dockerfile_image@sha256:4acbda1d663dd014cd2a3fe8419a2f32157bd93827064b690e053e8150374fbc" ], "Parent": "sha256:d91e550d121cd0e3931e41ce9f8d1fec587762dd7da9ea1a4e0263b9fcce69c2", "Comment": "", "Created": "2020-09-11T21:59:09.7469464Z", "Container": "2cccebf5bad8d1950aa5f2fce5487bb24e8c0e7e770d87ee00286307e1d209ae", "ContainerConfig": { "Hostname": "2cccebf5bad8", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"apachectl\" \"-D\" \"FOREGROUND\"]" ], "Image": "sha256:d91e550d121cd0e3931e41ce9f8d1fec587762dd7da9ea1a4e0263b9fcce69c2", "Volumes": null, "WorkingDir": "/var/www/html", "Entrypoint": null, "OnBuild": null, "Labels": {} }, "DockerVersion": "19.03.12", "Author": "", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "apachectl", "-D", "FOREGROUND" ], "Image": "sha256:d91e550d121cd0e3931e41ce9f8d1fec587762dd7da9ea1a4e0263b9fcce69c2", "Volumes": null, "WorkingDir": "/var/www/html", "Entrypoint": null, "OnBuild": null, "Labels": null }, "Architecture": "amd64", "Os": "linux", "Size": 267780583, "VirtualSize": 267780583, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/d6c0aa77c2c015f1f0d8db3ca27637a0a0aeb40e09f857b22e3f10e88975d74d/diff:/var/lib/docker/overlay2/81712967ed9f688fa658ae16bcba9605d86cc634c3f1111e2d17fc0697f80f71/diff:/var/lib/docker/overlay2/59d086fa0ba32d3a8ad78d8b5429defb885f8f947e49a74cd3ca02e1759230e7/diff:/var/lib/docker/overlay2/beb13ed69bb4d80eae4ecb1e390938eec7cd658d8616b72bcf6ecbcf401ea43e/diff:/var/lib/docker/overlay2/8ab00975f513c741e35b09cacdf92c3c13bf58c00b776ba8ebc423552559b688/diff:/var/lib/docker/overlay2/e9a47806a9829339ff81890da8a0e9e8463e0a6a749b5d6c0f1e3ae360db4f3d/diff:/var/lib/docker/overlay2/f9155ad7c789de4f6a5081aa2b24e31ac84982553c228861aaddaa22587e8d02/diff:/var/lib/docker/overlay2/ad3e2af0283da6a5626d3b6948d87c3759551320434ab871d5d1391b4d031473/diff", "MergedDir": "/var/lib/docker/overlay2/76482b35e706085f2a978bb4745620c3fa6c202c1571c271740db01592cc71eb/merged", "UpperDir": "/var/lib/docker/overlay2/76482b35e706085f2a978bb4745620c3fa6c202c1571c271740db01592cc71eb/diff", "WorkDir": "/var/lib/docker/overlay2/76482b35e706085f2a978bb4745620c3fa6c202c1571c271740db01592cc71eb/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ "sha256:2ce3c188c38d7ad46d2df5e6af7e7aed846bc3321bdd89706d5262fefd6a3390", "sha256:ad44aa179b334bbf4aeb61ecef978c3c77a3bb27cb28bcb727f5566d7f085b31", "sha256:35a91a75d24be7ff9c68ce618dcc933f89fef502a59becac8510dbc3bf7a4a05", "sha256:a4399aeb9a0e1ddf9da712ef222fd66f707a8c7205ed2607c9c8aac0dbabe882", "sha256:28e65d7bf05221c2c1ead74135322b9d06ce2217b07d072b09dfe105d6b3609d", "sha256:2d1cba0b094a082547fff75183799f7fbbc58596cceba8d90223395f7101a2b3", "sha256:68dc2869f31711854e9dcc2966084b2fcaa8bc1a8ea92eb0a97e49238b249ac6", "sha256:98878591b454a11119fd1506a20deb81d304aaae05a4e2d8e3069dd27461ec93", "sha256:638631dfceb80ffe5d51febb2feb40cc1652ce537f1b61aba74e8c511b09838d" ] }, "Metadata": { "LastTagTime": "2020-09-11T22:02:46.3520449Z" } } ]
Review what we did
So let’s review. The diagram below shows all the steps that we did in this article.
First we created a container using ubuntu. Docker pulled ubuntu down from DockerHub and placed it in our image store. Then it ran the container.
We made changes to our container then then committed it to the image store.
Next we created a new container using our newly committed image.
Next we created an image from a Dockerfile and created a new container from that.
Finally we renamed and pushed our Dockerfile created image to Docker Hub.
