You are here

#docker using #docker-compose to build a lamp dev enviroment with front-end autoscale

Hi,

To continue developing our great php app, that we coded in the last ansible series
We are going to show and example on how we could bring up really quickly
A dev enviroment to test/develop our App using docker-compose.

We are going to create the same architecture as in the last blog series of ansible:
one load balancer, several web servers and a mysql database.

The beauty of this is that we can build and bring up the dev enviroment in minutes.

If I have time this is going to be the first post of a series where we will go trough the steps
of creating a CI/CD pipe for our great(rubish) php application, using docker and ansible.

So lets start to build the env so we can test with our stupid php app the lamp env is working.

First a quick word on the dir structure i'm going to use, it will later on make life easier:

[liquid@liquid-ibm:~/lamp-docker]$ tree liquid-app                                                                                                                                       (10-20 15:28)
liquid-app
├── docker
│   ├── dev
│   │   ├── docker-compose.yml
│   │   └── Dockerfile
│   └── release
├── scripts
└── src
    └── index.php

5 directories, 3 files

In this post we are going to work only on the development stage.
Where we just set up the enviroment to develop and run tests on our app.

The source code of our afromentioned app is in src/index.php
The app just to check if the db is working creates a table and inserts the hostname,ip,mem,kernel of the server that does the insert, so we can also test the load-balancing

LAMP application Test.

connect_error) { die("Connection failed: " . $conn->connect_error); } $sql = "CREATE TABLE Hosts ( id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, hostname VARCHAR(30) NOT NULL, ip VARCHAR(30) NOT NULL, kernel VARCHAR(30) NOT NULL, memory VARCHAR(50), reg_date TIMESTAMP )"; if ($conn->query($sql) === TRUE) { echo "Table Hosts created successfully"; } else { echo "Error creating table: " . $conn->error; } $sql = "INSERT INTO Hosts (hostname, ip, kernel, memory) VALUES ('$hostname', '$ip', '$uname', '$mem')"; if ($conn->query($sql) === TRUE) { echo "New record created successfullyi \n"; } else { echo "Error: " . $sql . "
" . $conn->error; } $sql = "SELECT id, hostname, ip, kernel, memory FROM Hosts"; $result = $conn->query($sql); if ($result->num_rows > 0) { // output data of each row while($row = $result->fetch_assoc()) { echo " id: " . $row["id"]. " - Name: " . $row["hostname"]. " " . $row["ip"]. " " . $row["memory"]. " " . $row["kernel"]. "
"; } } else { echo "0 results"; } ?>

To setup the env we use docker-compose:

Compose is a tool for defining and running multi-container Docker applications.
https://docs.docker.com/compose/

In our case we want to bring up what we call 3 services, load-balancer, web server and db server.

We define the docker-compose services in a file called docker-compose.yml that is in yml format.

[liquid@liquid-ibm:docker/dev]$ cat docker-compose.yml                                                                                                                                   (10-20 15:47)
version: '2'

services:
  web:
    build: 
      context: ../../
      dockerfile: docker/dev/Dockerfile
    volumes:
      - ../../src:/var/www/html
    links:
      - db
    environment:
      DBNAME: liquidapp
      DBUSER: liquiduser
      DBPASS: liquiduser
      DBHOST: db
  db: 
    image: mysql:5.6
    hostname: db
    expose:
      - "3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_USER: liquiduser
      MYSQL_PASSWORD: liquiduser
      MYSQL_DATABASE: liquidapp
  lb:
    image: dockercloud/haproxy
    links:
      - web
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - 80:80

So on to the file, we are using version 2 syntax, Version 2 files are supported by Compose 1.6.0+ and require a Docker Engine of version 1.10.0+

we are creating 3 services web,db and lb, lets take a look at the web service:

services:
  web:       -----> name of the service
    build:   -----> this service is bult from a docker file 
      context: ../../  ----> the build context we wan't it to be at the app dir level in the future we will need to add scripst and src files
      dockerfile: docker/dev/Dockerfile  ---> if the Dockerfile is not in the buildcontext dir it must be specified
    volumes:
      - ../../src:/var/www/html  --> we want to map our src app dir to the document root, so we can edit/update the app and check easily on the web
    links:
      - db    ----> we are linking the web service with the db service
    environment:
      DBNAME: liquidapp --> env variables that we have used in our Dockerfile to pass the Database info to the app on the docker-compose file
      DBUSER: liquiduser
      DBPASS: liquiduser
      DBHOST: db

Ok let's check the Dockerfile that we are going to build when we run the docker-compose up command:

[liquid@liquid-ibm:lamp-docker/liquid-app]$ tree                                                                                                                                         (10-20 15:58)
.
├── docker
│   ├── dev
│   │   ├── docker-compose.yml
│   │   └── Dockerfile
│   └── release
├── scripts
└── src
    └── index.php

5 directories, 3 files
[liquid@liquid-ibm:lamp-docker/liquid-app]$ cat docker/dev/Dockerfile                                                                                                                    (10-20 15:58)
FROM eboraas/apache-php   ----> We use the eboraas/apache-php image for our base

MAINTAINER Daniel Alexander Parkes
LABEL version="0.2"

RUN  echo 'export DBNAME=${DBNAME}\n\ ---> export variables to the envvars apache file so they are pased on to PHP
export DBUSER=${DBUSER}\n\            ---> This variables will be filled in at runtime by the docker-compose file
export DBPASS=${DBPASS}\n\
export DBHOST=${DBHOST}\n' >> /etc/apache2/envvars

#Here we are just making index.php the defaul file to read as default, and also removing the ssl apache instance
RUN sed -i -e 's/DirectoryIndex.*$/DirectoryIndex index.php/g' /etc/apache2/mods-available/dir.conf && \\
rm /etc/apache2/sites-enabled/default-ssl.conf

#We expose port 80 and run the apache web server.
EXPOSE 80 
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

So just to clarify the enviroment variables drama the workflow is:

The enviroment gets passed from the docker-compose file to the container, when apache starts reads the envvars file which passes the vars to PHP so it can connect to the DB.

Lets go to the next service in the docker-compose-yml file:

  db: 
    image: mysql:5.6 --> mysql official image
    hostname: db     --> we give it a hostname so the web can connect
    expose:
      - "3306"       ---> expose mysql port
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_USER: liquiduser    ---> with variables we can get a user created
      MYSQL_PASSWORD: liquiduser
      MYSQL_DATABASE: liquidapp ---> and also a database created

Finally for the load balancer service:

  lb:
    image: dockercloud/haproxy  --> we use tthe dockercloud haproxy image
    links: 
      - web                     ---> we link the web service so they can comunicate
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock --> to autoscale this image needs access to the docker socket file on the host
    ports:
      - 80:80    --> we map port 80 of the lb to the port 80 of the host 

So as you can see in a couple of minutes we have our dev env ready, let's try it out:

We run the compose up command with the -d switch so it runs in the backgroud and with
the --build option so the image is allways re-built when the up command is issued


[liquid@liquid-ibm:docker/dev]$ docker-compose up -d --build                                                                                                                             (10-20 16:33)
Creating network "dev_default" with the default driver
Building web
Step 1 : FROM eboraas/apache-php
 ---> 80012ded4d94
Step 2 : MAINTAINER Daniel Alexander Parkes
 ---> Using cache
 ---> 862823a41a02
Step 3 : LABEL version "0.2"
 ---> Using cache
 ---> 036001e286ae
Step 4 : RUN echo 'export DBNAME=${DBNAME}\nexport DBUSER=${DBUSER}\nexport DBPASS=${DBPASS}\nexport DBHOST=${DBHOST}\n' >> /etc/apache2/envvars
 ---> Using cache
 ---> 233d9ddb7b37
Step 5 : RUN sed -i -e 's/DirectoryIndex.*$/DirectoryIndex index.php/g' /etc/apache2/mods-available/dir.conf && rm /etc/apache2/sites-enabled/default-ssl.conf
 ---> Using cache
 ---> bec531a949ab
Step 6 : EXPOSE 80
 ---> Using cache
 ---> 70688672e4fb
Step 7 : CMD /usr/sbin/apache2ctl -D FOREGROUND
 ---> Using cache
 ---> c977bce31732
Successfully built c977bce31732
Creating dev_db_1
Creating dev_web_1
Creating dev_lb_1

We can check our containers:

[liquid@liquid-ibm:docker/dev]$ docker-compose ps                                                                                                                                        (10-20 16:34)
  Name                 Command               State                   Ports                 
------------------------------------------------------------------------------------------
dev_db_1    docker-entrypoint.sh mysqld      Up      3306/tcp                              
dev_lb_1    /sbin/tini -- dockercloud- ...   Up      1936/tcp, 443/tcp, 0.0.0.0:80->80/tcp 
dev_web_1   /usr/sbin/apache2ctl -D FO ...   Up      443/tcp, 80/tcp                       

If we connect against our docker host to port 80 that is mapped to the loadbalancer we get 

[liquid@liquid-ibm:docker/dev]$ curl http://172.17.40.57                                                                                                                                 (10-20 16:52)

LAMP application Test.

id: 1 - Name: bbba0e42b2ac - IP: 172.18.0.3 - Mem: 16126084kB - kernel 4.7.7-200.fc24.x86_64
id: 2 - Name: bbba0e42b2ac - IP: 172.18.0.3 - Mem: 16126084kB - kernel 4.7.7-200.fc24.x86_64

Here we can see that the hostname/Ip is allways the same because we only have 1 container.
Lets scale our web services to 4 containers, and see how the haproxy autoscales:


[liquid@liquid-ibm:docker/dev]$ S docker-compose scale web=4                                                                                                                             (10-20 16:52)
Creating and starting dev_web_2 ... done
Creating and starting dev_web_3 ... done
Creating and starting dev_web_4 ... done

[liquid@liquid-ibm:docker/dev]$ S docker-compose ps                                                                                                                                      (10-20 16:54)
  Name                 Command               State                   Ports                 
------------------------------------------------------------------------------------------
dev_db_1    docker-entrypoint.sh mysqld      Up      3306/tcp                              
dev_lb_1    /sbin/tini -- dockercloud- ...   Up      1936/tcp, 443/tcp, 0.0.0.0:80->80/tcp 
dev_web_1   /usr/sbin/apache2ctl -D FO ...   Up      443/tcp, 80/tcp                       
dev_web_2   /usr/sbin/apache2ctl -D FO ...   Up      443/tcp, 80/tcp                       
dev_web_3   /usr/sbin/apache2ctl -D FO ...   Up      443/tcp, 80/tcp                       
dev_web_4   /usr/sbin/apache2ctl -D FO ...   Up      443/tcp, 80/tcp                     

Here we can see our new web containers running, lets check the load balancer logs:

[liquid@liquid-ibm:docker/dev]$ S docker logs dev_lb_1
............
INFO:haproxy:Docker event: container dev_web_2 start
INFO:haproxy:HAProxy(PID:13) has been terminated
INFO:haproxy:Linked service: dev_web
INFO:haproxy:Linked container: dev_web_1, dev_web_2, dev_web_3, dev_web_4
............
backend default_service
  server dev_web_1 dev_web_1:80 check inter 2000 rise 2 fall 3
  server dev_web_2 dev_web_2:80 check inter 2000 rise 2 fall 3
  server dev_web_3 dev_web_3:80 check inter 2000 rise 2 fall 3
  server dev_web_4 dev_web_4:80 check inter 2000 rise 2 fall 3
INFO:haproxy:Reloading HAProxy
INFO:haproxy:HAProxy has been reloaded(PID: 15)
.............


As you can see the image has detected the new containers kicking in.
So it has reconfigure the haproxy cfg file and reloaded the daemon.

If we test with curl now:

[liquid@liquid-ibm:docker/dev]$ curl http://172.17.40.57 | grep IP                                                                                                                       (10-20 16:59)
 - IP:  172.18.0.3
 - IP:  172.18.0.3
 - IP:  172.18.0.6
 - IP:  172.18.0.6
 - IP:  172.18.0.5
 - IP:  172.18.0.5
 - IP:  172.18.0.7
 - IP:  172.18.0.7

We can see the loadbalancer is moving the requests between the four web nodes.

Unix Systems: 

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.
Error | HP-UX Tips & Tricks Site

Error

Error message

  • Warning: Cannot modify header information - headers already sent by (output started at /homepages/37/d228974590/htdocs/includes/common.inc:2567) in drupal_send_headers() (line 1207 of /homepages/37/d228974590/htdocs/includes/bootstrap.inc).
  • PDOException: SQLSTATE[42000]: Syntax error or access violation: 1142 INSERT command denied to user 'dbo229817041'@'217.160.155.192' for table 'watchdog': INSERT INTO {watchdog} (uid, type, message, variables, severity, link, location, referer, hostname, timestamp) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9); Array ( [:db_insert_placeholder_0] => 0 [:db_insert_placeholder_1] => cron [:db_insert_placeholder_2] => Attempting to re-run cron while it is already running. [:db_insert_placeholder_3] => a:0:{} [:db_insert_placeholder_4] => 4 [:db_insert_placeholder_5] => [:db_insert_placeholder_6] => http://www.hpuxtips.es/?q=content/docker-using-docker-compose-build-lamp-dev-enviroment-front-end-autoscale [:db_insert_placeholder_7] => [:db_insert_placeholder_8] => 54.90.207.75 [:db_insert_placeholder_9] => 1512951725 ) in dblog_watchdog() (line 157 of /homepages/37/d228974590/htdocs/modules/dblog/dblog.module).
The website encountered an unexpected error. Please try again later.