Wednesday, February 22, 2017

Docker compose



Docker compose is an orchestration tool for docker that allows you to define a set of containers and their interdependencies in the form of a YAML file. Once the yaml file is created , we can use the docker-compose commands to create the whole application stack. Besides this we can track the application output and various other things.

The docker-compose is a tool for defining and running multi-container application. In this we create a compose file to configure our application services. Then by using a single command we create all images necessary for the container, Start the container. We just need to access the application running inside the container

Using Compose is basically a three-step process.
  1. Define your app’s environment with a Dockerfile so it can be reproduced anywhere.
  2. Define the services that make up your app in docker-compose.yml so they can be run together in an isolated environment.
  3. Lastly, run docker-compose up and Compose will start and run your entire app.

1.Let's write a basic example of running a java application inside the container. Below is the directory structure of my first docker compose example

[puppet@root$:/test/docker]$  tree docker-compose/
docker-compose/
── compose
│   ── docker-compose.yml
│   ── ping
│   └── PingPong
│        ── Dockerfile
│        └── PingPong.java
└── PingPong

2. Lets write our java code for the PingPong.java class

[puppet@root$]$  cat PingPong.java
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class PingPong {

          public static void main(String[] args) throws Exception {
          HttpServer server = HttpServer.create(new InetSocketAddress(19090), 0);
          server.createContext("/ping", new MyHandler());
          server.setExecutor(null);
          server.start();
          }

          static class MyHandler implements HttpHandler {
          @Override
          public void handle(HttpExchange t) throws IOException {
          String response = "pong\n";
          t.sendResponseHeaders(200, response.length());
          OutputStream os = t.getResponseBody();
          os.write(response.getBytes());
          os.close();
          }
          }
}

The above java code is a simple HTTP listener which responds to the requests made on the port 19090 with a response. Once the user sends a request called “ping” , the above code responds with a reponse of “pong”.

3.Besides the PingPong.java class we have the Dockerfile with contents as,

[puppet@root$]$  cat Dockerfile
FROM java:8
MAINTAINER Jagadish Manchala <jagadishm@example.com>
WORKDIR /
COPY PingPong.java /
RUN javac PingPong.java
EXPOSE 19090
CMD ["java","PingPong"]

The instructions are self explanatory. We are using a Java 8 based container. The work directory is / and we are moving the PingPong.java class to this contianer location “/”. We also compiling the java class and running the code by exposing the 19090 port.

4. Now come out of the directory and we can have the compose file as,
[puppet@root$]$  cat docker-compose.yml
version: '2'
services:
 PingPong:
          build: ./PingPong
          image: pingpong:1
          ports:
          - "5000:19090"
          volumes:
          - ../PingPong:/code

We start off with the line “version: ‘2’”, which tells Docker Compose we are using the new Docker Compose syntax. We define a single service called pingpong, which runs from an image called pingpong:1. It exposes a single port 5000 on the docker host that maps to port 19090 inside the container.

The “build: ./PingPong”

We’ve added a single line “build: ./PingPong” to the helloworld service. It instructs Docker Compose to enter the compose/PingPong directory, run a docker build there, and tag the resultant image as pingpong:1.

Now if you tried to run this as-is, using “docker-compose up”, docker could complain that it couldn’t find pingpong:1. That’s because it’s looking on the docker hub for a container image called pingpong:1. We haven’t created it yet. So now, let’s add the recipe to create this container image.

Now run the docker-compose up --force-recreate command and we can see below output as,

[puppet@root$:/test/docker/docker-compose/compose]$  docker-compose up --force-recreate
Creating network "compose_default" with the default driver
Building PingPong
Step 1 : FROM java:8
---> d23bdf5b1b1b
Step 2 : MAINTAINER Jagadish Manchala <jagadishm@example.com>
---> Running in 278b9e78e8ac
---> db3eeedd3862
Removing intermediate container 278b9e78e8ac
Step 3 : WORKDIR /
---> Running in cbfa16414694
---> 2ee46f20f539
Removing intermediate container cbfa16414694
Step 4 : COPY PingPong.java /
---> dc039d682eca
Removing intermediate container 2771d925c2d9
Step 5 : RUN javac PingPong.java
---> Running in 792a5fbfdc57
---> 3e9a9ddac467
Removing intermediate container 792a5fbfdc57
Step 6 : EXPOSE 19090
---> Running in 2efa5017a79e
---> 8164eb703ff4
Removing intermediate container 2efa5017a79e
Step 7 : CMD java PingPong
---> Running in ef7347c01dee
---> bd15166ab1a7
Removing intermediate container ef7347c01dee
Successfully built bd15166ab1a7
WARNING: Image for service PingPong was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating compose_PingPong_1
Attaching to compose_PingPong_1

Now in the above output , we have the docker-compose command waiting after the Attaching to compose_*** command. At this point when we see the docker images , we can see a pingpong image created. And When we run the “docker ps” command we can see the container running as below,

[puppet@root$:/test/docker/docker-compose/compose]$  docker ps
CONTAINER ID         IMAGE                    COMMAND               CREATED                
078f287da46c          pingpong:1              "java PingPong"        About a minute ago  
STATUS              PORTS                          NAMES
Up About a minute   0.0.0.0:5000->19090/tcp   compose_PingPong_1

Now we can test the Java Code running inside the Container using

[puppet@root$:/test/docker/docker-compose/compose]$  wget http://localhost:5000/ping
--2017-02-21 08:13:22--  http://localhost:5000/ping
Resolving localhost (localhost)... ::1
Connecting to localhost (localhost)|::1|:5000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5
Saving to: ‘ping’

100%[============================================================================================================>] 5        --.-K/s   in 0s          

2017-02-21 08:13:22 (1.52 MB/s) - ‘ping’ saved [5/5]

Or we can take a browser and type the URL as “http://localhost:5000/ping

To stop the application, simply type Ctrl-C at the terminal prompt and Docker Compose will stop the container and exit. You can go ahead and change the code in the PingPong directory, add new code or modify existing code, and test it out using “docker-compose up” again.

To run it in the background: docker-compose up -d.
To tail the container standard output: docker-compose logs -f.
To down the container: docker-compose down -v
To Force recreate: docker-compose up --force-recreate
Read More

Tuesday, February 21, 2017

Docker - Playing with Dockerfile

Docker images are created either by pulling them online or by creating on our local machine.

Containers can be created by using the docker run command. Once the container is started, we have to manually login to the container and perform the actions that we want the container to work with. Docker provides another way to create images by using the Dockerfile.

A Dockerfile is a text file with instructions written in a format understood by the Docker. To create an image we need to write our own docker instructions in the Dockerfile. There are only a handful of docker instructions available that goes into the Dockerfile. In this article we will see how we can create a basic container image using the Docker instructions in the DockerFile. We will cover most of the Instructions available in this example

Here is the example Dockerfile that we are going to use in this demo
[puppet@root$:/test/docker]$  cat Dockerfile
FROM ubuntu
MAINTAINER jagadish "jagadish.manchala@gmail.com"
RUN useradd -p redhat123 demoDock
USER demoDock
RUN id
USER root
WORKDIR /tmp
COPY test.txt /tmp
VOLUME /work
ONBUILD RUN mkdir /tmp/tempo
RUN apt-get update
RUN apt-get install -y nginx
ENV CONTAINER_NAME DemoContainer
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
EXPOSE 80

1. From sets the Base Image for subsequent instructions. In the above example we are building a image that is based on ubuntu image. Once we try to build the image , the ubuntu image is downloaded first or if available on the local repository it will use that.

2. MAINTAINER sets the AUthor field of the generated image

3. RUN executes any commands given in a new layer on the top of the current image and commits the result

4. ENV sets environment variables

5. USER sets the user name for following RUN / CMD and ENTRYPOINT commands

6. WORKDIR sets the working directory. This gets reflected when we login to the Container

7. COPY copies new files or directories to the container. The files are copied to a location specified in the instruction

8. ADD copies new files , directories. This works much like COPY instruction but with additional features. This can ADD files from a remote location. So if we want to download a package from online when a  container is started, we can use the ADD instruction

9. CMD sets default commands which can be overwritten from command line when docker container runs.

10. VOLUME creates a mount point for externally mounted volumes or other containers.

11. LABEL apply key value metadata to the image or container

12. ONBUILD adds a trigger instruction when the above created image is used as a base image for building another image

13. STOPSIGNAL sets the system call signal that will be sent to the container to exit

14. EXPOSE informs docker that the container listens on the specified network ports at runtime. This actually make the ports accessible from the host machine

When we build the image using the Dockerfile,

[puppet@root$:/test/docker]$  docker build -t sample-1 .
Sending build context to Docker daemon 3.584 kB
Step 1 : FROM ubuntu
 ---> f49eec89601e
Step 2 : MAINTAINER jagadish "jagadish.manchala@gmail.com"
 ---> Using cache
 ---> 94fd532ca66b
Step 3 : RUN useradd -p redhat123 demoDock
 ---> Using cache
 ---> e357022cfa3d
Step 4 : USER demoDock
 ---> Using cache
 ---> 044aff8c76a6
Step 5 : RUN id
 ---> Using cache
 ---> e6ea35026fb1
Step 6 : USER root
 ---> Using cache
 ---> 6362a86140fe
Step 7 : WORKDIR /tmp
 ---> Using cache
 ---> eec500226e04
Step 8 : COPY test.txt /tmp
 ---> Using cache
 ---> 597eb11573fa
Step 9 : ADD http://unec.edu.az/application/uploads/2014/12/pdf-sample.pdf /tmp
Downloading [==================================================>] 7.945 kB/7.945 kB
 ---> 1e4e9c10e01b
Removing intermediate container 30e8c9b3d9ce
Step 10 : VOLUME /work
 ---> Running in d76ab01c6951
 ---> c94b506c1080
Removing intermediate container d76ab01c6951
Step 11 : ONBUILD run mkdir /tmp/tempo
 ---> Running in 4bfc6be733fb
 ---> 6bf428e22280
Removing intermediate container 4bfc6be733fb
Step 12 : RUN apt-get update
 ---> Running in 71e68b270971
Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
Get:3 http://archive.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
Get:4 http://archive.ubuntu.com/ubuntu xenial/main Sources [1103 kB]
Get:5 http://archive.ubuntu.com/ubuntu xenial/restricted Sources [5179 B]
Get:6 http://archive.ubuntu.com/ubuntu xenial/universe Sources [9802 kB]
Get:7 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages [1558 kB]
Get:8 http://archive.ubuntu.com/ubuntu xenial/restricted amd64 Packages [14.1 kB]
Get:9 http://archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [9827 kB]
Get:10 http://archive.ubuntu.com/ubuntu xenial-updates/main Sources [291 kB]
Get:11 http://archive.ubuntu.com/ubuntu xenial-updates/restricted Sources [2815 B]
Get:12 http://archive.ubuntu.com/ubuntu xenial-updates/universe Sources [162 kB]
Get:13 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages [604 kB]
Get:14 http://archive.ubuntu.com/ubuntu xenial-updates/restricted amd64 Packages [12.4 kB]
Get:15 http://archive.ubuntu.com/ubuntu xenial-updates/universe amd64 Packages [502 kB]
Get:16 http://archive.ubuntu.com/ubuntu xenial-security/main Sources [72.1 kB]
Get:17 http://archive.ubuntu.com/ubuntu xenial-security/restricted Sources [2392 B]
Get:18 http://archive.ubuntu.com/ubuntu xenial-security/universe Sources [22.4 kB]
Get:19 http://archive.ubuntu.com/ubuntu xenial-security/main amd64 Packages [268 kB]
Get:20 http://archive.ubuntu.com/ubuntu xenial-security/restricted amd64 Packages [12.0 kB]
Get:21 http://archive.ubuntu.com/ubuntu xenial-security/universe amd64 Packages [91.7 kB]
Fetched 24.8 MB in 1min 14s (331 kB/s)
Reading package lists...
 ---> 0a40de0d78a4
Removing intermediate container 71e68b270971
Step 13 : RUN apt-get install -y nginx
 ---> Running in 8b6c131cab89
Reading package lists...
Building dependency tree...
Reading state information...
The following additional packages will be installed:
*******************
Removing intermediate container 8b6c131cab89
Step 14 : ENV CONTAINER_NAME DemoContainer
 ---> Running in d1c3f7769b6f
 ---> ab78532293fa
Removing intermediate container d1c3f7769b6f
Step 15 : RUN service nginx restart
 ---> Running in 02170bfea4cb
 * Restarting nginx nginx
   ...done.
 ---> 2a6838821ad5
Removing intermediate container 02170bfea4cb
Step 16 : EXPOSE 80
 ---> Running in 566d56d32142
 ---> 8c35bcbe88be
Removing intermediate container 566d56d32142
Successfully built 8c35bcbe88be

The build is success and if we check the “docker images” command we can see the “sample-1” image in our local repository.

Now lets run the container using
[puppet@root$:/test/docker]$  docker run -d -p 8011:80 --name my2 sample-1
bf854781d85ddf6f5141435aa0dc9f05f7d7c7f2cbd7925806cbb89e3a7ecc45

Now we can test the nginx from our local machine using localhost:8011 URL


More article to comes , Happy learning
Read More