Docker

What is container?

"A container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, run-time, system tools, system libraries and settings." - docker.com

First Docker App

Find the base operating system

$ uname -a
Linux 668a632cd1bc 4.15.0-30-generic #32~16.04.1-Ubuntu SMP Thu Jul 26 20:25:39 UTC 2018 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID:  Ubuntu
Description: Ubuntu 16.04.5 LTS
Release: 16.04
Codename: xenial


Install docker

Using the instruction in the link below

https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository


Check docker version

$ docker --version
Docker version 18.06.0-ce, build 0ffa825


Docker commands:

docker build -t friendlyhello .  # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello  # Run "friendlyname" mapping port 4000 to 80
docker run -d -p 4000:80 friendlyhello         # Same thing, but in detached mode
docker container ls                                # List all running containers
docker container ls -a             # List all containers, even those not running
docker container stop <hash>           # Gracefully stop the specified container
docker container kill <hash>         # Force shutdown of the specified container
docker container rm <hash>        # Remove specified container from this machine
docker container rm $(docker container ls -a -q)         # Remove all containers
docker image ls -a                             # List all images on this machine
docker image rm <image id>            # Remove specified image from this machine
docker image rm $(docker image ls -a -q)   # Remove all images from this machine
docker login             # Log in this CLI session using your Docker credentials
docker tag <image> username/repository:tag  # Tag <image> for upload to registry
docker push username/repository:tag            # Upload tagged image to registry
docker run username/repository:tag                   # Run image from a registry


View the docker images

$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE


Test your docker with hello-world. If you see the output as below, you are good to go.

$ docker run -it hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9db2ca6ccae0: Pull complete 
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/


Create a directory with the follow directory structure and contents of the file are also below.

$ tree ~/docker-app
docker-app
├── docker-compose.yml
└── flask-app
    ├── app.py
    ├── Dockerfile
    └── requirements.txt


Create a Docker file. Docker file contains the instruction for docker image.

$ cat ~/docker-app/flask-app/Dockerfile 
FROM ubuntu:latest
RUN apt-get update -y && apt-get install -y python-pip python-dev build-essential
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]


Python libraries to be installed using pip. Here we require Flask for web application using python.

$ cd 
$ cat ~/docker-app/flask-app/requirements.txt 
Flask==1.0.2
cassandra-driver


$ cat ~/docker-app/flask-app/app.py 
from flask import Flask
app = Flask(__name__)

count = 0

@app.route("/", methods = ["get", "post"])
def hello():
    return "Hello world!"

@app.route("/counter")
def counter():
    global count
    count += 1
    return str(count)

if __name__ == "__main__":
    app.run(debug = True, host = "0.0.0.0", port = 5000)

Go inside the flask-app directory and build the local copy of the image

$ cd ~/docker-app/flask-app/
$ sudo docker build -t flask-sample:latest .
Sending build context to Docker daemon  3.072kB
Step 1/8 : FROM ubuntu:latest
latest: Pulling from library/ubuntu
c64513b74145: Pull complete 
01b8b12bad90: Pull complete 
c5d85cf7a05f: Pull complete 
b6b268720157: Pull complete 
e12192999ff1: Pull complete 
Digest: sha256:3f119dc0737f57f704ebecac8a6d8477b0f6ca1ca0332c7ee1395ed2c6a82be7
Status: Downloaded newer image for ubuntu:latest
 ---> 735f80812f90
Step 2/8 : RUN apt-get update -y
 ---> Running in d3b9b1c77b39
Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB]
Get:2 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
Get:3 http://security.ubuntu.com/ubuntu bionic-security/universe Sources [14.3 kB]
...

  Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe
  Running setup.py bdist_wheel for itsdangerous: started
  Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
  Running setup.py bdist_wheel for MarkupSafe: started
  Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46
Successfully built itsdangerous MarkupSafe
Installing collected packages: Werkzeug, click, MarkupSafe, Jinja2, itsdangerous, Flask
Successfully installed Flask-1.0.2 Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 itsdangerous-0.24
Removing intermediate container 9e95964fae77
 ---> 631959518f29
Step 7/8 : ENTRYPOINT ["python"]
 ---> Running in 3bef8b04e02e
Removing intermediate container 3bef8b04e02e
 ---> 43005e09038a
Step 8/8 : CMD ["app.py"]
 ---> Running in 467b15172bff
Removing intermediate container 467b15172bff
 ---> 0ef49eefb5b5
Successfully built 0ef49eefb5b5
Successfully tagged flask-sample:latest


Find the local copies of the images.

$ sudo docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
flask-sample        latest              08106c7a59e7        About a minute ago   468MB
ubuntu              latest              735f80812f90        2 weeks ago          83.5MB


Run the container in detached mode (argument -d).

$ sudo docker run -d -p 5000:5000 flask-sample
c9e070076aa65f1014960b4e625c75d737042aa2ed0ef1c90ef2f9d7a06a4502


$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
c9e070076aa6        flask-sample        "python app.py"     4 seconds ago       Up 3 seconds        0.0.0.0:5000->5000/tcp   epic_heisenberg


View the log. -f at the end tails the log to the console.

$ sudo docker logs c9e070076aa6 -f
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 236-035-556
...


Test the web page

$ curl http://localhost:5000
Hello world!


If you want to bash terminal

$ sudo docker exec -it c9e070076aa6 bash


Inspect docker container

$ sudo docker inspect c9e070076aa6
[
    {
        "Id": "465c38e956c54f8058d3ebfdc183ff0fa9e18472b839dd3c5a24db4cbecc4396",
        "Created": "2018-08-12T16:25:03.053918463Z",
        "Path": "python",
        "Args": [
            "app.py"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 6776,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2018-08-12T17:34:00.395255705Z",
            "FinishedAt": "2018-08-12T17:17:45.173095557Z"
        },
....



Stop the docker container

$ sudo docker stop c9e070076aa6


Check the status of the containers. The container is in exited state.

$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
c9e070076aa6        flask-sample        "python app.py"     20 minutes ago      Exited (0) 6 seconds ago                       epic_heisenberg


If you want to start the stopped container, run the following command.

$ sudo docker container start c9e070076aa6
c9e070076aa6


Stop the container, delete the container and delete the docker image

$ sudo docker stop c9e070076aa6
$ sudo docker container rm c9e070076aa6
$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
$ sudo docker image rm flask-sample
Untagged: flask-sample:latest
Deleted: sha256:34eb09e6c97440208aa3b1ac3acd7cf1de8dc3098c314703f357f1a3c25f0457
Deleted: sha256:99e81e9f7fb1166e2e3ddb04c687d8a5e25db3728811a9c4cb772cab6e780ba9
Deleted: sha256:f96de24162fc365a0bdd4a9c88865f82ffc9e566253a92bad44e7c0bcc1de992
Deleted: sha256:f5997e0a9e5b55e3f4f438f1af8279af2bb5cf7bc6aff7aa9b9e4f8f2ee740d9
Deleted: sha256:c99064ae31c213ca213ee123d1afd3828bf9c982aa936a8dcce32ed0ad176b2c
Deleted: sha256:c4f6bbd734d7c3fd168e6a88e7ba8803e26477982bc8dc886c70a8c37a4ea9da
Deleted: sha256:06e71bb9cbb6cce78e58025885f62aa37530274bcfa7aff46912c40641da89ed
Deleted: sha256:a01ce400d3d13ee2706623550cb33d666b9dd23a57d77e332ea605a4dcca082e
Deleted: sha256:37b99233e7f0b0aa6d39157c6d2d1e5ca6ba516bd0d8dce296f2ab820e0f7bf0
Deleted: sha256:8617ef7db4290cd64f881e546db763edc7c2e4e207dbb356e9330d0cbdc99172
Deleted: sha256:1aab5a8ea148fff8394329f1e31995ad395aa5f04adf7e2d842d42c92593d438



Publish image to docker hub

If you do not user account, sign up hub.docker.org

Create a tag. This is ultimately be in used in docker hub.

$ sudo docker tag flask-sample:latest abasar/web-app:1.0
$ sudo docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
flask-sample        latest              34f43e4e623c        10 minutes ago      468MB
abasar/web-app      1.0                 34f43e4e623c        10 minutes ago      468MB
ubuntu              latest              735f80812f90        2 weeks ago         83.5MB


Login to push the image to docker hub.

$ sudo docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: <your hub.docker.org username>
Password: <your hub.docker.org password>
WARNING! Your password will be stored unencrypted in /home/training/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded


Push the image to docker hub.

$ sudo docker push abasar/web-app:1.0
The push refers to repository [docker.io/abasar/web-app]
435ae6a71b97: Pushed 
9f5518f577d2: Pushed 
e88cdd645a03: Pushed 
268a067217b5: Pushed 
c01d74f99de4: Pushed 
ccd4d61916aa: Pushed 
8f2b771487e9: Pushed 
f49017d4d5ce: Mounted from library/ubuntu 
1.0: digest: sha256:6e41e43ff2264abeb361a8fd3e300991b77fc619b98842459795fec1d32d39f4 size: 1988


You can test the image that you just created by pulling from docker hub.

$ sudo docker pull abasar/web-app:1.0
1.0: Pulling from abasar/web-app
c64513b74145: Already exists 
01b8b12bad90: Already exists 
c5d85cf7a05f: Already exists 
b6b268720157: Already exists 
e12192999ff1: Already exists 
c1f77516f7b5: Already exists 
4f1baabdad3a: Already exists 
bcf0e2b1cc0d: Already exists 
Digest: sha256:6e41e43ff2264abeb361a8fd3e300991b77fc619b98842459795fec1d32d39f4
Status: Downloaded newer image for abasar/web-app:1.0


See the images

$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
abasar/web-app      1.0                 34f43e4e623c        18 minutes ago      468MB


Run the custom built image that you just pulled from docker hub.

$ sudo docker run -p 5000:5000 abasar/web-app:1.0
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 236-035-556


Docker Compose


Install docker-compose following instructions

https://docs.docker.com/compose/install/


Essentially, you have to run the following two commands

$ sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

Check the version

$ docker-compose --version
docker-compose version 1.22.0, build f46880fe


Create docker-compose.yml file. For location, refer the directory tree below.

$ cat ~/docker-app/docker-compose.yml 
version: "3"
services:
  web:
    build: ./flask-app
    ports:
      - "5000:5000"
    volumes:
      - "./flask-app:/app"
    ports:
      - "5000:5000"
    networks:
      - webnet
networks:
  webnet:


Directory tree

$ tree ~/docker-app
/home/training/docker-app
├── docker-compose.yml
└── flask-app
    ├── app.py
    ├── Dockerfile
    └── requirements.txt


Bring up the container using docker compose

$ sudo docker-compose build
$ sudo docker-compose up
Starting docker-app_web_1 ... done
Attaching to docker-app_web_1
web_1  |  * Serving Flask app "app" (lazy loading)
web_1  |  * Environment: production
web_1  |    WARNING: Do not use the development server in a production environment.
web_1  |    Use a production WSGI server instead.
web_1  |  * Debug mode: on
web_1  |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
web_1  |  * Restarting with stat
web_1  |  * Debugger is active!
web_1  |  * Debugger PIN: 294-308-829


Reset docker environment

Stop all containers

$ docker stop $(docker ps -a -q)

Remove all containers

$ docker rm $(docker ps -a -q)

Remove all images

$ docker rmi -f $(docker images -a -q)