# 🇬🇧 Little survival Docker guide for web developers that do not understand anything to Docker

First of all, a BIG THANK YOU to Alessio Coltellacci (opens new window), because he knew how to understand my questions and to explain things to me simply.

This blog post isn't a complete guide about Docker, but only some recipes I use for my own needs.

# First image and first container

# Use case

What do we want? I need a kind of virtualization of an Ubuntu machine with NodeJS installed

So, create a directories structure like that:

.
├── 00-dev-setup
│   ├── Dockerfile
│   └── workspace
│       ├── index.js
│       └── package.json

# Prepare the Dockerfile

Create a Dockerfile file inside /00-dev-setup

Dockerfile:

FROM ubuntu:latest
LABEL maintainer="@k33g_org"
 
RUN apt-get update && \
    apt-get install -y apt-utils && \
    apt-get install -y curl && \
    apt-get install -y nano && \
    apt-get install -y gnupg2 && \
    curl -sL https://deb.nodesource.com/setup_11.x | bash  && \
    apt-get install -y nodejs  && \
    apt-get clean

WORKDIR /workspace

# Prepare the NodeJS application

Create the index.js file inside /00-dev-setup/workspace

index.js:

const express = require("express");
const bodyParser = require("body-parser");
const generate = require('project-name-generator');

let port = process.env.PORT || 9090;

let app = express();
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))

let machineName = generate({ words: 3, number: true }).dashed

app.disable('etag');

app.get('/', (req, res) => {
  res.send(`
    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
      </head>
      <body>
        <h1>👋 Hello 🌍, I am ${machineName}</h1>
      </body>
    </html>
  `)
})

app.listen(port)
console.log(`🌍 ${machineName} is started - listening on`, port)

Create the package.json file inside /00-dev-setup/workspace

package.json:

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "node index.js"
  },
  "main": "index.js",
  "dependencies": {
    "body-parser": "^1.17.2",
    "express": "^4.15.3",
    "project-name-generator": "^2.1.3"
  }
}

# Build the image

With these commands, I'll create a Docker image named dev-console

cd 00-dev-setup
docker build -t dev-console .

... wait a little moment

# Create and Run the container

cd 00-dev-setup
docker run -v $(pwd)/workspace:/workspace -p 8080:9090 --name dev-container -i -t dev-console

What did we do?

  • We created a container named dev-container from the image named dev-console
  • We are going to open a shell inside the running container, -i -t options
  • We can access to our http server on the 8080 http port (when our server listening on 9090 inside the container)
  • We told the container that it can access to workspace directory on the host file system with this option: -v $(pwd)/workspace:/workspace (that means you can update files from the container or and from the host)

all the options for docker run are here: https://docs.docker.com/engine/reference/commandline/run/ (opens new window)

Right now, you should get a prompt like that:

root@81efd35c7c10:/workspace#

If you type a ls command, you should obtain:

index.js package.json

# Start your http server

Inside the shell of the running container, type:

npm install

and then,

npm start

You should obtain:

🌍 half-spiky-visitor-1390 is started - listening on 9090

note the generated name of the server: half-spiky-visitor-1390

Now, from the host, you can access your website with http://localhost:8080 (opens new window)

Important remark:

  • I can create another container from the same image to run another http server,
  • but I will need to use another "external" http port:
  • docker run -v $(pwd)/workspace:/workspace -p 8081:9090 --name dev-container-other -i -t dev-console
  • here I used the 8081 port and my new container is named dev-container-other,
  • so, I will be able to access to the new http server with this adress http://localhost:8081 (opens new window)

to exit frm the container:

  • Ctrl-c (it stops the nodejs http server)
  • exit (you return to the shell of the host, and the container is stopped)

⚠️ Remark/Question: I thought that I needed to expose the HTTP port 8080, it looks like it works even without that (to be validated again)

# Some useful commands

  • return to the shell of the running container: docker start dev-container then docker attach dev-container
  • stop a running container: docker stop dev-container
  • remove a container: docker rm dev-container (you can recreate it with the docker run command)

# 2 containers and 2 http servers

# Use case

What do we want?

  • create 2 web servers from the same image (so, 2 containers)
  • access to these 2 websites with "fancy" domain names

For that we are going to use the "internal network" of Docker and Nginx 🙀.

# First, build 2 containers to serve http servers

It's simple:

docker network create my-web

then, type:

docker run -v $(pwd)/workspace:/workspace -p 8081:9090 --net my-web --name dev-container-one -i -t dev-console

then, in another host terminal, type:

docker run -v $(pwd)/workspace:/workspace -p 8082:9090 --net my-web --name dev-container-two -i -t dev-console

Start the http server (npm start) in the 2 container shells.

You can check that you can reach http://localhost:8081 (opens new window) and http://localhost:8082 (opens new window)

# Add a reverse proxy (Nginx)

In another directory, create a new Dockerfile:

FROM nginx
RUN rm /etc/nginx/conf.d/default.conf 
COPY default.conf /etc/nginx/conf.d

In the same directory create a default.conf file:

server {
  listen       80;
  server_name  hello-one.test;
  
  location / {  
      proxy_pass http://dev-container-one:9090;  
  }   
}

server {
  listen       80;
  server_name  hello-two.test;
  
  location / {  
      proxy_pass http://dev-container-two:9090;     
  } 
}

Build your new nginx image:

docker build -t nginx .

Create your new nginx container:

docker run -d --net my-web --name nginx-container -p 80:80 -p 443:443 nginx

this container is in a "detached mode" (-d option), so you don't get a new shell, and stay in the shell of the host.

In your etc/hosts file update this line:

127.0.0.1	localhost

like that:

127.0.0.1	localhost hello-one.test hello-two.test

And now, it's magic 🎩✨ you can reach your 2 web sites with http://hello-one.test and http://hello-two.test. 😀

Remark: if you want to add another website to Nginx, you need to rebuild the image and the container (there is probably a better solution)

# Magic way: use jwilder/nginx-proxy

There is a simpler way, using the nginx-proxy project: https://github.com/jwilder/nginx-proxy (opens new window)

First, remove all your containers (docker rm container_name), then type this command:

docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy

And now start 3 containers (each from the 00-dev-setup directory, or fromthe directory where you setup your project). Start each container with the environment variable named VIRTUAL_HOST that contains the domain name:

docker run -e VIRTUAL_HOST=yo-1.test -v $(pwd)/workspace:/workspace -p 8081:9090 --name dev-container-one -i -t dev-console
docker run -e VIRTUAL_HOST=yo-2.test -v $(pwd)/workspace:/workspace -p 8082:9090 --name dev-container-two -i -t dev-console
docker run -e VIRTUAL_HOST=yo-3.test -v $(pwd)/workspace:/workspace -p 8083:9090 --name dev-container-three -i -t dev-console

In each shell, run the npm start command

In your etc/hosts file update this line:

127.0.0.1	localhost

like that:

127.0.0.1	localhost yo-1.test yo-2.test yo-3.test

And now, you can access your containers with their 3 respective urls:

That's all for today. 🎄

Last Articles