Docker 01: first 3 steps of your web app — Pragmatic Docker series
A 7 minutes story written on Sep 2018 by Adrian B.G.
Containers in general, but Docker in particular is a concept harder to grasp. I will try to write the simplest and most straightforward tutorial for a first hands-on experience for a developer.
Pragmatic Docker series for developers
- Docker 01 — first 3 steps of your web app (this article)
- Docker 02 — meet bash, variables and logs
- Docker 03 — persistence and compose multiple services (TODO)
- Docker 04 — hubs, deployments and cloud (TODO)
Containers for everyone
The size of this tutorial may seem scary but it is all boilerplate, I only introduce 3 new commands:
docker create network,
To keep the tutorial simple I will presume you already have:
- Bash (even on windows)
- basic knowledge on Linux, Docker and bash/CLI
- installed docker at least version 15 and git
- basic knowledge on developing back-end web services
- ~200MB free space on HDD (for the images)
Optional you should:
- be able to run
dockercommands with your user (without sudo) OR add
**sudo**to all commands and scripts from this tutorial
- allow HTTP traffic (port 80) if you use Google Cloud, AWS … and you want to access the web service trough a browser or remote> To learn about Docker I suggest the following resources: docker.com, IBM essentials, training.docker.com.This tutorial assumes that you already have a basic knowledge about Docker, hence the name “pragmatic”.
Docker terms #likeimfive
- Docker — collection of tools to deploy and run your apps
- Image — An executable blueprint for containers, a Class in OOP, a wrapper for your all app dependencies, code and configs
- Container — a set of isolated Linux processes in a bubble, an Object in OOP, the run-time instance of an Image
- Network — is like your Wireless home network where Docker is the router
- Volume — a portion of your hard-disk that can be accessed by a container
What are we going to do today
- Create a private docker network
- Run a container with a popular database (memcached in this case)
- Run a container with a simple web service that “talks” to the database> TL;TR: You will find all the scripts, configs with comments, and the web app source in this repository. The files are heavily annotated for a better understanding of how Docker works and how your code is affected by it.
We need a network in order for our containers to “talk” to each other. It will behave like a “bubble”, it will surround our containers and protect them from the outside “world”, and vice-versa. Imagine that we will create a WiFi network and Docker is the router.
In older docker tutorials you will notice the usage of the
linkparam of the
docker runcommand, but that feature is deprecated since late 2015.
Before we create our (running) containers we must build a “home” for them, a local private network. It sounds complicated but docker makes it easy for us:
$ docker network create tut-network $ docker network ls #list all the networks, to confirm it was created``
After this step, all the containers we will create will have the following parameter
docker run .... --network tut-network ..... All the containers that are not part of this network cannot access their open ports, including your machine (localhost), for security reasons. In order to gain access to the web app we will use another
docker run parameter that links a container port to a (localhost) port:
This tutorial only covers one service, namely memcached for simplicity reasons, you can repeat these steps with any other technology: MySQL, PhpMyAdmin, MongoDB you name it. You can use the search command or visit hub.docker.com:
docker search "mysql" to find more images.
We will keep the memcached port private, for security reasons. Only containers that run in our (newly) created docker private network
tut-network will have access to it. You can access the full bash script here.
$ docker run `#main command, run a container on my machine` \ --name tut-memcache `#unique name so you can access it later` \ --detach `#detach the process from the current bash` \ --network tut-network `#connect it to the private network` \ --restart=on-failure:3 `#if the memcached process crashes, docker will restart the container 3 times max` \ memcached:1.5.10-alpine `#the name of the image, see https://hub.docker.com/_/memcached/, alpine is small`
docker ps you should see the tut-memcache container running.
docker ps ... STATUS PORTS NAMES ... Up 2 seconds 11211/tcp tut-memcache
Memcached is a popular technology, so we used a pre-built image generated by the memcached developers.
Our app on the other hand is not so popular either so public, so we will have to build our own image (blueprint), before we can run it in a container.
For the app:
- you can build your own web app that talks to
- or you can use my simple Node app (JS for popularity reasons) that does NOT require NPM. You can clone
Because we will run the app in a container you don’t even need to have Node installed on your system.
Regarding the app, I kept everything as simple as I could. In order to use the memcached storage, at every request the web server receives, it increments a numerical value from the database:
Now we will take our app, dependencies and config files and copy them in a docker Image. The easiest (native) way to build an image is to have a Dockerfile:
FROM node:8.12.0-slim EXPOSE 80 ADD memjs ./memjs COPY app.js . CMD node app.js
This config file (download here) will “tell” docker to build an image that has NodeJS, with an open port 80, to copy the memjs folder (vanilla memcached client) and the app.js file inside the image. The last command is the entry-point of the container (main process). To get a local copy of the project you can clone it:
$ git clone https://github.com/bgadrian/docker-tutorials.git $ cd docker-tutorials/01-first-steps
Next step is to build the docker image, run this command in the folder that contains the Dockerfile.
`$ docker build \ -t tut-app-image:v1 `#tag it with this name:tag` \ -t tut-app-image:latest `#make it the latest version too` \ . `#build with the instructions found on ./Dockerfile` $ docker images `#to list all your docker images`
If you encounter permissions errors like
[context](https://docs.docker.com/install/linux/linux-postinstall/#configure-docker-to-start-on-boot)see this page.
Now we have an executable image (tut-app-image), that contains everything the app needs to run. This allows us to run the app on any computer that has Docker, without polluting the system with its dependencies and files.
$ docker run \ --name tut-app `#put it an unique name so you can access it later` \ --detach `#detach the process from the current bash` \ --network tut-network `#connect it to the private network we created, --link is deprecated` \ --restart=on-failure:3 `#if the app.js crashes, docker will restart the container 3 times max` \ --publish 0.0.0.0:80:80 `#link EXPOSEd port from Docker file to your localhost` \ tut-app-image:latest `#the name of the image:tag`
docker ps should confirm that the container is running. By accessing http://localhost (or your public ip for VMs) should increase a key stored in memcached, proof that the 2 containers can “speak” to each other.
$ curl localhost Hello from app.js! Request count: 0 $ curl localhost Hello from app.js! Request count: 1 $ curl localhost Hello from app.js! Request count: 2
Now you can modify the app.js file (in any way you want, go ahead I’ll wait ….). After that, you will have to also update the image and the container. To ease these operations I wrote a script that builds and run the app:
Do not forget to READ all the files from my repo, they are heavily annotated, hopefully it will help understand what each command and parameter does.
docker start|stop|restart|kill tut-app
docker ps -a— list all your containers
docker images— list all your images, don’t forget to clean them up
docker network ls— all your networks
docker network inspect tut-network— detailed view of a network
docker inspect tut-app— everything about a specific container
docker logs -f tut-app— tailing your app logs
- if you already have a server on port 80 you can run the app on 8080 or any other port:
- if you do not want to expose the server to the public change the binding ip to your localhost only:
- I used the memjs client, thanks to amit levy for building it
- If you use Windows I suggest moving to a more developer-friendly system
- Use the  (https://github.com/bgadrian/docker-tutorials/blob/master/1.tutorial-simple/d-cleanup.sh)`[d-cleanup.sh](https://github.com/bgadrian/docker-tutorials/blob/master/1.tutorial-simple/d-cleanup.sh)` scriptto purge this tutorial from your system
- If you build your own web service you have to create a new Dockerfile in a new folder