As you can see we will have lot of different solutions at the end of this tutorial. This structure is not production wise, it's just to show what Docker can do.
Docker Hub / Database
I've picked pgSQL for this tutorial, bust you must look at this more as concept, because you can do it with any DB (SQL and noneSQL).
With Docker your life should be easy, you don't have to install machines and DB's anymore, you just download image you need and use it. Most of the software have images ready in Docker Hub and lot of them already have preconfigured images made by users. But what is Docker Hub? It's official public images repository - https://hub.docker.com. You can register and upload your images to the hub for personal and public use. If you will continue use Docker you will meet personal registries (own by companies or private people), they work same way (later in our tutorials we will even install registry server).
One you logged in to Hub (but you don't have too) you can search for postgres. I've found 10 repositories - first one is official repository and 9 repositories made by users:
It's always suggested to take official repositories and make modifications for them, but if you're lazy and there is user repository that fit your needs (check what images include). If you're using user repository it's also important to check last update. Sometimes it's good to use old but stable versions, but sometime you need updated one.
Inside the repository you will see tags information (usually tags represent versions) and how to use the repository.
Now let's go to your machine and start building containers
As always we will use latest image and version, just because it's tutorial and we don't really care about it :)
Actually if you sure that you going to use common software you don't have to look in repository, just pull the name of it... The repository can be useful for usage, but also not must, as you can view image configuration... Anyway, let's pull our repo:
davidg@linux-cpl8:~> docker pull postgres:latest latest: Pulling from postgres 104de4492b99: Pull complete 065218d54d7d: Pull complete 6d342ad75f37: Pull complete 9433e325f9ad: Pull complete 7c38e9491f7e: Pull complete d9a636286bd1: Pull complete 4020db192fff: Pull complete b93ccbbdcc22: Pull complete 13af7ae40c45: Pull complete e6826f7776c8: Pull complete 5c67b212f3da: Pull complete 1e87f75b5751: Pull complete 24a73f6adf68: Pull complete effe3b6a83fc: Pull complete 65482096da78: Pull complete 089cc1d86ef7: Pull complete d4c43025a271: Pull complete 41684070b967: Pull complete f969e36858c2: Pull complete a2c56c0927fc: Pull complete 7bf0ec35adaf: Already exists postgres:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security. Digest: sha256:0b2d2e463174edacb17d976d72adc839c032bfcfdf6da6799e288014d59998f8 Status: Downloaded newer image for postgres:latest
~ $ docker run --name postgres_test -e POSTGRES_PASSWORD=d0ckerul3z -d postgres 415a2a8734845a8d9188e959bb7acf90ecf21da1479f8213a6df4e2ac096430a ~ $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 415a2a873484 postgres:latest "/docker-entrypoint. 5 minutes ago Up 5 minutes 5432/tcp postgres_test
I'll short the explanation of the network in few words. If you will run ifconfig you will notice new device:
~ $ ifconfig docker0 Link encap:Ethernet HWaddr 56:84:7A:FE:97:99 inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0 inet6 addr: fe80::5484:7aff:fefe:9799/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:4 errors:0 dropped:0 overruns:0 frame:0 TX packets:35 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:304 (304.0 b) TX bytes:6496 (6.3 Kb)
~ $ docker inspect postgres_test | grep IPAddress "IPAddress": "172.17.0.1",
~ $ psql -h 172.17.0.1 -U postgres postgres Password for user postgres: d0ckerul3z psql (9.3.6, server 9.4.4) WARNING: psql major version 9.3, server major version 9.4. Some psql features might not work. Type "help" for help. postgres=#
Application Container
Next part is to create an application that will connect to our DB and use it.
I'm using simple flask application for the basic things, but it can be anything you want:
And now for the interesting part, get this to container! You can always start simple CentOS container and copy files there and start them, but why use Docker for this?
In Docker we make it simpler and more automatic - we make and image, just like image you just downloaded!I'm using simple flask application for the basic things, but it can be anything you want:
import psycopg2 import psycopg2.extras import sys from flask import Flask app = Flask(__name__) @app.route("/") def test(): con = None result = '<h1>The Guardians of the Galaxy</h1><table border="1"><tr><th> </th><th>Character</th><th>Real Name</th></tr>' try: con = psycopg2.connect("host='postgres_test' dbname='postgres' user='postgres' password='d0ckerul3z'") cursor = con.cursor(cursor_factory=psycopg2.extras.DictCursor) cursor.execute("SELECT * FROM guardians") rows = cursor.fetchall() for row in rows: if row['teamleader']: result += "<tr><td>%s</td><td><b>%s</b></td><td><b>%s</b></td></tr>" % (row["id"], row["character"], row["realname"]) else: result += "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" % (row["id"], row["character"], row["realname"]) result += '</table>' except psycopg2.DatabaseError, e: result = 'Error %s' % e finally: if con: con.close() return result if __name__ == "__main__": db_data = ( ('Adam Warlock', 'Him', 'false'), ('Drax the Destroyer', 'Arthur Sampson Douglas', 'false'), ('Gamora', 'Gamora', 'false'), ('Quasar a.k.a. Martyr', 'Phyla-Vell', 'false'), ('Rocket Raccoon', 'Rocket Raccoon', 'true'), ('Star-Lord', 'Peter Quill', 'true'), ('Groot', 'Groot', 'false'), ('Mantis', 'Mantis', 'false'), ('Major Victory', 'Vance Astro', 'false'), ('Bug', 'Bug', 'false'), ('Jack Flag', 'Jack Harrison', 'false'), ('Cosmo the Spacedog', 'Cosmo', 'false'), ('Moondragon', 'Heather Douglas', 'false'), ) con = None try: con = psycopg2.connect("host='postgres_test' dbname='postgres' user='postgres' password='d0ckerul3z'") cur = con.cursor() cur.execute("DROP TABLE IF EXISTS Guardians") cur.execute("CREATE TABLE Guardians(Id SERIAL PRIMARY KEY, Character TEXT, RealName TEXT, TeamLeader BOOLEAN)") query = "INSERT INTO Guardians (Character, RealName, TeamLeader) VALUES (%s, %s, %s)" cur.executemany(query, db_data) con.commit() except psycopg2.DatabaseError, e: if con: con.rollback() print 'Error %s' % e sys.exit(1) finally: if con: con.close() app.run(host= '0.0.0.0')
And now for the interesting part, get this to container! You can always start simple CentOS container and copy files there and start them, but why use Docker for this?
How do we do it? Lets start with making new directory where new image files will be stored:
~ $ mkdir docker_image_1 ~ $ cd docker_image_1/ ~/docker_image_1 $
FROM centos:latest MAINTAINER David Golovan LABEL Description="guardians:1 - WebApp to print Guardians list" Vendor="Forthscale" Version="1.0" RUN yum -y update && yum -y install epel-release RUN yum -y install python-pip gcc postgresql postgresql-devel python-devel RUN pip install flask psycopg2 COPY guardians.py /opt/guardians/ CMD ["python", "/opt/guardians/guardians.py"]
- FROM - Image name + tag. This is the image on which our image is based, so it will contain everything from it.
- MAINTAINER - Just info about image owner if it goes public
- LABEL - Information about the image and it's version
- RUN - Execute shell command. We are executing yum install and pip install for packages, but it can be any command
- ADD - Copy file/directory from local server to the image. We are coping our scripts to /root/ of the image
- CMD - Command to execute once container is running. When this commands stops(error or just exit) the container will also stop, so always make sure to run here commands that don't exit :)
Build our image and we can start containers using it. Note: I've not pulled CentOS images before so our image build will pull on first build:
~/docker_image_1 $ docker build -t forthscale/guardians:1.0 . Sending build context to Docker daemon 5.632 kB Sending build context to Docker daemon Step 0 : FROM centos:latest latest: Pulling from centos f1b10cd84249: Pull complete c852f6d61e65: Pull complete 7322fbe74aa5: Already exists centos:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security. Digest: sha256:a4627c43bafc86705af2e8a5ea1f0ed34fbf27b6e7a392e5ee45dbd4736627cc Status: Downloaded newer image for centos:latest ---> 7322fbe74aa5 Step 1 : MAINTAINER David Golovan <davidg@forthscale.com> ---> Running in cded0075df8b ---> b582a22635b5 Removing intermediate container cded0075df8b Step 2 : LABEL Description "guardians:1 - WebApp to print Guardians list" Vendor "Forthscale" Version "1.0" ---> Running in e8ba10cb9536 ---> 3779b4331b2e Removing intermediate container e8ba10cb9536 Step 3 : RUN yum -y update && yum -y install epel-release ---> Running in 2bab13a17bf5 Loaded plugins: fastestmirror Determining fastest mirrors ..... ---> b7207970f3b8 Removing intermediate container 841af65c665a Step 4 : RUN yum -y install python-pip gcc postgresql postgresql-devel python-devel ---> Running in ffbb682e8747 Loaded plugins: fastestmirror ....... ---> 534c270b560b Removing intermediate container ffbb682e8747 Step 5 : RUN pip install flask psycopg2 ---> Running in 7f380d6f374c Downloading/unpacking flask ....... Downloading/unpacking psycopg2 Successfully installed flask psycopg2 Werkzeug Jinja2 itsdangerous markupsafe Cleaning up... ---> a7038e07d3fc Removing intermediate container b066cc5e4b21 Step 6 : COPY guardians.py /opt/guardians/ ---> a98ee8d4bc21 Removing intermediate container ffb5d8030f25 Step 7 : CMD python /opt/guardians/guardians.py ---> Running in c2c8da611dca ---> 70df63860b6e Removing intermediate container c2c8da611dca Successfully built 70df63860b6e
~/docker_image_1 $ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE forthscale/guardians 1.0 70df63860b6e About a minute ago 379.2 MB centos latest 7322fbe74aa5 2 weeks ago 172.2 MB postgres latest 7bf0ec35adaf 2 weeks ago 213.9 MB ubuntu latest 6d4946999d4f 3 weeks ago 188.3 MB ubuntu trusty 6d4946999d4f 3 weeks ago 188.3 MB ubuntu 14.04 6d4946999d4f 3 weeks ago 188.3 MB
~/docker_image_1 $ docker run -p 8080:5000 --link postgres_test:postgres_test --name guardian -d forthscale/guardians:1.0 fb2d7143af007fccdad3bf74c500a55562757c4a0fedc4ecdd9e9b35d6c22b99
- -p 8080:5000 - Open port and redirect it. 80 is public port and 5000 is port our application is listening (default flask port)
- --link - Linking to container. First write container name (that's why it's important to name your containers) and second write what name to use for it inside the container (I prefer to use same name).
Open your browser and go to http://localhost:8080
But wait, that's not all, you can make even more! We can have shared directory for all the containers and for example server static files from there.
In the Dockerfile add this lines before the CMD:
RUN mkdir /opt/guardians/static VOLUME ["/opt/guardians/static"]
app = Flask(__name__) result = '<h1>The Guardians of the Galaxy</h1><table border="1"><tr><th> </th><th>Character</th><th>Real Name</th></tr>'
app = Flask(__name__, static_url_path = "/static", static_folder = "static") result = '<img src="static/small_h.png" /><br /><h1>The Guardians of the Galaxy</h1><table border="1"><tr><th> </th><th>Character</th><th>Real Name</th></tr>'
~/docker_image_1 $ docker build -t forthscale/guardians:1.1
~/docker_image_1 $ docker stop guardian && docker rm guardian ~/docker_image_1 $ docker build -t forthscale/guardians:1.1 .
~/docker_image_1 $ mkdir /tmp/guardians ~/docker_image_1 $ wget -P /tmp/guardians https://www.docker.com/sites/default/files/legal/small_h.png
~/docker_image_1 $ docker run -p 8080:5000 --link postgres_test:postgres_test -v /tmp/guardians:/opt/guardians/static --name guardian -d forthscale/guardians:1.1
- In the Docker file you've added command to create new folder and make read/write with VOLUME command
- In guardins.py you've allowed static files in flask and their location and added HTML tag to show the image
- Build image was fast because it use cache for most of the parts of the file
- In docker run command flag -v allow to map any local directory to any read/write directory of the image. When running docker inspect [CONTAINER] it will list volumes with read/write permissions that you can rewrite.
Now you can build much more advanced containers for you applications
Provided by:Forthscale systems, cloud experts