Thursday, 17 November 2016

How to Scale web Application With docker


Scaling Web Application with Docker


In the previous post i have discussed about building the laravel application with docker. In this post I will try scale the same application . if you haven't looked yet i will recommend you to read that post first.

we will clone the same project from the GitHub and will make necessary changes.

Scaling the web application is very simple in docker we can scale our application by docker-compose command very easily.

But before scaling our application we have to setup a load balancer which will evenly distribute the request to each instances of particular scaled service fortunately docker hub have official image of haproxy  which will act like a load balancer. We will use that image in our docker-compose.yml file and will make a service by using it. Let see it in action.


What is HAProxy?

Official docs
HAProxy is a free, open source high availability solution, providing load balancing and proxying for TCP and HTTP-based applications by spreading requests across multiple servers. It is written in C and has a reputation for being fast and efficient (in terms of processor and memory usage)

To know more about HAProxy read the official docs Click Here .
We will modify our docker-compose.yml file to configure HAProxy. Copy the code given below and paste it into docker-compose.yml file.

Docker-compose.yml file
loadbalancer:
image: eeacms/haproxy
container_name: loadbalancer
links:
 - web
ports:

 - "80:5000"
- "1936:1936"
web:
build: .
# container_name: laravel_container
volumes:
  - .:/var/www/html/app
links:
  - mysql
mysql:
image: mysql:latest
container_name: mysql_laravel
ports:
 - "3306:3306"
environment:
 MYSQL_ROOT_PASSWORD: hrhk
 MYSQL_PASSWORD: hrhk   
 MYSQL_DATABASE: laravel_db

In this docker-compose.yml file i have created three service
  1. Loadbalancer
  2. Web
  3. Mysql

Web Service
The definition and  configuration of web service defined in this  yml file is almost same with what we have defined in previous post/project except i have commented the container_name because when we scale our web service all web service will have same container name which conflict with each other and thus causing problem to scale our web service. We will leave it on docker engine to give the default name to each container. If you pay attention on web service in above docker-compose.yml file then you will notice that i haven’t used the port directive which i  have used  in the previous project. This is only because we want to scale only this particular service not mysql and load balancer service. Giving the port statically will result in port conflict error because we can not run multiple container at same port in the localhost. I hope you getting my point. Anyways Questions are welcomed.
Load Balancer Service
The purpose of this whole project is to teach you about horizontally scaling the web servers and managing the load balance.
This loadbalancer  is the main service which will receive all request from the client and distribute evenly to the the multiple web server running on the host. I have  used eeacms/haproxy image for building this service. In the previous project our laravel container was listening on the port 80 but in this project we will run the load balancer container on the port 80 and inside the loadbalancer container our load balancer service will be running on port 5000. Apart from making this container to run on port 80 i have linked loadbalancer service to  web service through which this load balancer service will  automatically discover the web service and can divert all incoming connection to healthy web servers.

Note
Loadbalancer service is running inside the Loadbalancer container on port 5000 where as Loadbalancer Container is running on host system at port 80.

Now the question arises why i am using eeacms/haproxy image instead of haproxy or tutum/haproxy image?

If i use official haproxy image than i have to manually configure the haproxy.cfg file located in etc/haproxy directory which will be very headache if we have large no of conatainers and other problem is whenever we fire docker-compose up all conatiners will be assigned a new ip thus we have to re configure our haaproxy.cfg file. To overcome this problem I am using this eeacms/haproxy which will automate all this task using python script.
This image will generate pre configured haproxy.cfg file having listed all the instance of scaled web service.
You must have to force recreate the loadbalancer service.

Haproxy configuration file looks like

global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /run/haproxy/admin.sock mode 660 level admin stats timeout 30s user haproxy group haproxy daemon # Default SSL material locations ca-base /etc/ssl/certs crt-base /etc/ssl/private # Default ciphers to use on SSL-enabled listening sockets. # For more information, see ciphers(1SSL). ssl-default-bind-ciphers kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL defaults log global mode http option httplog option dontlognull timeout connect 5000 timeout client 50000 timeout server 50000 errorfile 400 /etc/haproxy/errors/400.http errorfile 403 /etc/haproxy/errors/403.http errorfile 408 /etc/haproxy/errors/408.http errorfile 500 /etc/haproxy/errors/500.http errorfile 502 /etc/haproxy/errors/502.http errorfile 503 /etc/haproxy/errors/503.http errorfile 504 /etc/haproxy/errors/504.http

frontend localnodes bind *:80 mode http default_backend nodes

backend nodes mode http balance roundrobin option forwardfor http-request set-header X-Forwarded-Port %[dst_port] http-request add-header X-Forwarded-Proto https if { ssl_fc } option httpchk HEAD / HTTP/1.1\r\nHost:localhost server http-server1 127.0.0.1:9000 check server http-server2 127.0.0.1:9001 check server http-server3 127.0.0.1:9002 check

listen stats *:1936 stats enable stats uri / stats hide-version stats auth someuser:password




Load Balancing Configuration

To get started balancing traffic between our three HTTP listeners, we need to set some options within HAProxy:
  • frontend - where HAProxy listens to connections
  • backend - Where HAPoxy sends incoming connections
  • stats - Optionally, setup HAProxy web tool for monitoring the load balancer and its nodes



Mysql Service

This is the same service which i have used in the previous project please go and read about this service.
I have covered only theory now it is time for action. Run the following command


cd laravel-docker-app


sudo docker-compose up -d

Note:
  1. docker-compose is a tool not shipped with docker. We have install it manually google it
    2. Make sure you have stopped apache and mysql on localhost to free port 80 and 3306
        because  we will run loadbalancer container on 80 and mysql container on 3306
        sudo service apache2 stop
        sudo service mysql stop


If no error occurred then successfully two docker container was deployed. We can check by following command


sudo docker ps OR sudo docker-compose ps


CONTAINER ID        IMAGE                 COMMAND                 CREATED             STATUS              PORTS               NAMES
748a76f5242f    eeacms/haproxy:latest   "python /haproxy/main"   23 seconds ago Up 20 seconds       443/tcp, 0.0.0.0:80->80/tcp, 1936/tcp   laraveldockerapp_loadbalancer_1

7cb3ba02a3ee        laraveldockerapp_web   "/my_init"               25 seconds ago      Up 23 seconds       80/tcp                                  laraveldockerapp_web_1

7c306b02411f        mysql:latest           "docker-entrypoint.sh"   26 seconds ago      Up 25 seconds       0.0.0.0:3306->3306/tcp                  mysql_laravel



I already have  given the overview of this project in the previous post if you have no idea about this laravel project go and have a look.

Now we will test our web API for that we have to insert same fake records in mysql database. In the previous Post scroll Down till you get "Two container are running " and follow rest of Instruction. you will have Your laravel API Project build with docker will be Up and Running.

Now we will scale our service. Run this command
docker-compose scale web=15



Creating and starting laraveldockerapp_web_1 ... done
Creating and starting laraveldockerapp_web_2 ... done
Creating and starting laraveldockerapp_web_3 ... done
Creating and starting laraveldockerapp_web_4 ... done
Creating and starting laraveldockerapp_web_5 ... done
Creating and starting laraveldockerapp_web_6 ... done
Creating and starting laraveldockerapp_web_7 ... done
Creating and starting laraveldockerapp_web_8 ... done
Creating and starting laraveldockerapp_web_9 ... done
Creating and starting laraveldockerapp_web_10 ... done
Creating and starting laraveldockerapp_web_10 ... done
Creating and starting laraveldockerapp_web_12 ... done
Creating and starting laraveldockerapp_web_13 ... done
Creating and starting laraveldockerapp_web_14 ... done
Creating and starting laraveldockerapp_web_15 ... done

After Scaling you must have to recreate haproxy container by following command.

docker-compose -f docker-compose.yml up --force-recreate

After recreating You can check your ha proxy configuration by getting into loadbalancer container

sudo docker exec -it loadbalancer bash
cd etc\haproxy
cat haproxy.cfg

or

sudo docker exec -it loadbalancer cat etc\haproxy\haproxy.cfg


Haproxy also have UI stat page from where you can see all servers and their state

http://localhost:1936



We have scaled our web service. Check with sudo  docker ps  command you will see that 15 instance of our web service will be running on our localhost. How cool is that we have scaled our web server with single command this is the power of docker.

BUT we only have scaled it on our local host. In real scenario Scaling is done on multiple host machine may be 10 ,20 ,50 or more than that depending upon the load on the server.

Scaling the dockerized Application on different node was done in Two ways
  1. Docker swarm (Native clustering solution of docker)
  2. Kubernetes (based on google experience to cluster the application )
We will see both of them in Action but for that we need a real environment to experiment we will use google compute engine to make a cluster of node but for that you have to wait for my next post

Thanks











Wednesday, 16 November 2016

Laravel Application with Docker

Build Laravel application with docker

METHOD 3
In the previous post i already have discussed about building  hello world php application with docker .
Now in this tutorial i will build  Laravel Application using docker-compose.yml file.

According to official docs
The Compose file is a YAML file defining services, networks and volumes. The default path for a Compose file is ./docker-compose.yml.

We will create two services in docker-compose.yml file. Each service contain some definition in docker-compose.yml file.
  1. web
  2. mysql

A service definition contains configuration which will be applied to each container started for that service, much like passing command-line parameters to docker run. Likewise, network and volume definitions are analogous to docker network create and docker volume create.


Difference Between Dockerfile And docker-compose file

Dockerfile is used only to build the image and install all software dependency and packages where as docker-compose.yml file is used to configure the image like mounting the volume from the localhost, mapping the ports etc.

One Dockerfile involves in building only one image where as One docker-compose.yml file 
is involved in configuring the multiple images.

Why docker-compose.yml file?

docker-compose.yml file simplifies the configuration of multiple images in one single file.
docker-compose is a tool which is used to run this docker-compose.yml file and make a configured docker containers from it. We can run our Whole application with the single command  of docker-compose tool which is very awesome.
The main objective of docker compose tool is to manage the multiple container app on the single Node. This tool is not used to scale our multiple container app on different node for that we have docker swarm .


Overview about this laravel project


I have developed an application which provide api for the members/students. We can hit the api and get response as a  list of members/student in json format. All the records will be stored on mysql database.this project is uploaded to the github  
link


This web application will only expose two api
  1. /api/v1/getAllMember
  2. /api/v1/getMemberById/{id}

First api will return all member in json format stored in members table
Second api will return a particular member detail in json format

For the sake of simplicity i only have worked with these files only

Controller used :  MemberController.php
Model Used :     Member.php
Migration class:   2016_11_01_135853_create_member_table.php
Environment file  .env
Explore these class to have better understanding about project




If u explore the .env file u will find mysql configuration as follow
DB_CONNECTION=mysql  
DB_HOST=mysql_laravel  //container name where mysql will be running
DB_PORT=3306  // default port
DB_DATABASE=laravel_db //this is the database name automatically created when we run docker-compose.yml file
DB_USERNAME=root
DB_PASSWORD=hrhk

I will explain about this configuration later in this post but for now that’s all about laravel project



Root Directory of Project

In the root directory of project there is two main file for making containers of this laravel application

  1. Dockerfile.txt
  2. docker-compose.yml

Dockerfile.tx

Dockerfile.txt
FROM nimmis/apache-php5
MAINTAINER Syed Moinuddin Shibli

We already have built a hello world php app using Dockerfile.txt in method 1. Please refer for detail


Docker-compose.yml

docker-compose.yml
web:
build: .
container_name: laravel_container
volumes:
 - .:/var/www/html/app
ports:
   - "80:80"
links:
 - mysql
mysql:
image: mysql:latest
container_name: mysql_laravel
ports:
- "3306:3306"
environment:
   MYSQL_ROOT_PASSWORD: hrhk
   MYSQL_DATABASE: laravel_db



Note:
There are two versions of the Compose file format – version 1 (the legacy format, which does not support volumes or networks) and version 2 (the most up-to-date). For more information, see the Versioning section.



I will try to explain each and every line of docker-compose.ym file

Line 1)
Web: Microservice you can name it whatever you want

Line 2)
build: . This will tell docker engine to build an image from dockerfile located in current directory

Line 3)
container_name: laravel_container Providing the custom name to docker container

Line 4)
Volumes: this directive is used for mounting the host files to container

Line 5)
 - .:/var/www/html/app mount every thing of current directory into /var/www/html/app

Line 6)
Ports: this directive is used to map a host machine port with docker container port

Line 7)
   - "80:80" on port 80 of localhost our docker container is running which mapped with port 80
inside the  docker container where our apache is running

Line 8)
Links: this directive is used to create a link between microservice

Line 9)
- mysql here i am linking my web microservice with mysql micro service

Line 10)
mysql:  this is micro service for mysql database

Line 11)
image: mysql:latest Using official mysql image to build mysql container

Line 12)
container_name: mysql_laravel providing custom name for the mysql container
This is the same name which i have configured in the the .env file  as
DB_HOST=mysql_laravel. This database will be automatically created in mysql.you do not have to manually create it.


Note
You must have to set in .env file for mysql host container setting as localhost will not work in our case i have setted it up as DB_HOST=mysql_laravel


Line 13)
Ports: configuring the port

Line 14)
- "3306:3306" localhostport:mysqlport running inside the mysql container

Line 15)
Environment:   setting up the environment variable for the mysql

Line 16)
   MYSQL_ROOT_PASSWORD: hrhk

Line 17)
   MYSQL_DATABASE: laravel_db



Now everything is configured it’s time to run over application

Open terminal and clone the project anywhere you want. I am cloning it to var directory


cd /var




cd laravel-docker-app


sudo docker-compose up -d

Note:
  1. docker-compose is a tool not shipped with docker. We have install it manually google it
  2. Make sure u have stopped apache2 and mysql if they are running on the host machine
         sudo service apache2 stop
         sudo service mysql stop

If no error occurred then successfully two docker container was deployed. We can check by following command


sudo docker ps


CONTAINER ID         IMAGE         COMMAND      CREATED             STATUS             PORTS              NAMES

1de243a968a2   laraveldockerapp_web   "/my_init"    7 seconds ago   Up 5 seconds        0.0.0.0:80->80/tcp       laravel_container

6edcb67281ba   mysql:latest   "docker-entrypoint.sh"  8 seconds ago  Up 7 seconds  0.0.0.0:3306->3306/tcp   mysql_laravel


Two container are running

1)laravel_container
2)mysql_laravel : mysql database is running inside this container


We will insert 250 records in our database table using laravel default faker


You can assume each container as a separate virtual operating system which is using host linux kernel .

Now we will enter inside the container and will run some php artisan command

sudo docker exec -it laravel_container bash

Note:
We can also use container id instead of name to enter into container


Inside the container just move to the directory where we have mounted our code. See docker-compose file volume configuration section
cd /var/www/html/app


Gives permission to storage

chmod -R 777 storage


We still haven’t created our database table we can migrate our migration class for respective database tables. Run the following command.

php artisan migrate


Output will be as follow
**************************************
*     Application In Production!     *
**************************************

Do you really wish to run this command? (yes/no) [no]:
> y

Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2016_11_01_135853_create_member_table






Now We will use command line tool known as tinker  to insert fake records in database table.
php artisan tinker


Here we are using factory method to generate 250  fake records  into mysql table. See app/database/ModelFactory to know how to factory model class works
$members=factory(App\Member::class,250)->create();

After inserting the 250 fake record into database table from tinker type “exit” without quotes to exit from the php artisan tinker
exit



We have configured everything for laravel application in  our laravel container and mysql container. These two container are up and  running but still our application in laravel_container is not running.

For local environment we may simply use artisan server to run our application
For Production environment we must use fully featured server like apache and nginx


We will use both server one by one to run our laravel application

First we will see how to run laravel application with artisan server

In docker-compose.yml file web microservice will be run on port 80 inside the container which is mapped with port 80 of localhost. But on port 80 in the laravel container  apache server is running by default . so we must have to stop it first then we will run artisan server on port 80.

Run the following command from inside the laravel container. It will stop apache server running inside the laravel container.
sudo service apache2 stop

Run the following command from inside the laravel container
php artisan serve --port 80 --host 0.0.0.0

Note:
Make sure you run following command in /var/www/html/app


Now we have successfully hosted our laravel docker application  using artisan web server on the localhost.
check the following URL URL 1) localhost/api/v1/getAllMember
URL 2) localhost/api/v1/getMemberById/10


Warning This artisan web server was designed to aid application development. It may also be useful for testing purposes or for application demonstrations that are run in controlled environments. It is not intended to be a full-featured web server. It should not be used on a public network.
The web server runs a only one single-threaded process, so PHP applications will stall if a request is blocked.


if we want to run our laravel application in laravel container by apache in our local host.
For that we need to configure apache configuration file and map the root directory to /var/www/html/app/public.


Scaling our laravel application

what if our application become successful  and  grown to  such an extent that it have millions of users interaction per day ,  then definitely our app will not be able to handle the request these heavy request may cause our application to crash.
we can easy handle such situation with docker by horizontally scaling our application.
Click here to see the next post in which i have described about Scaling the web application.