03/02/2018 - DOCKER
In this example we are going to create two identical Apache servers and one HAProxy container. When we want to access our website, we will be calling HAProxy, not the Apache servers. HAProxy will divert traffic to Apache servers in "round-robin" fashion.
ubuntu@linux:~/helloworld$ tree -a
.
└── docker
├── apache
│ ├── 1
│ │ ├── Dockerfile
│ │ └── index.html
│ └── 2
│ ├── Dockerfile
│ └── index.html
├── docker-compose.yml
├── .env
└── haproxy
├── Dockerfile
└── haproxy.cfg
5 directories, 8 files
ubuntu@linux:~/helloworld$ cat docker/apache/1/Dockerfile
FROM httpd:2.4
COPY index.html /usr/local/apache2/htdocs/index.html
ubuntu@linux:~/helloworld$ cat docker/apache/1/index.html
Serving from Apache Server 1
ubuntu@linux:~/helloworld$ cat docker/apache/2/Dockerfile
FROM httpd:2.4
COPY index.html /usr/local/apache2/htdocs/index.html
ubuntu@linux:~/helloworld$ cat docker/apache/2/index.html
Serving from Apache Server 2
ubuntu@linux:~/helloworld$ cat docker/haproxy/Dockerfile
FROM haproxy:1.7
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
You can access to HAProxy GUI via http://host_os_ip/haproxy?stats
and login with admin:admin
.
ubuntu@linux:~/helloworld$ cat docker/haproxy/haproxy.cfg
global
log /dev/log local0
log localhost local1 notice
maxconn 2000
daemon
defaults
log global
mode http
option httplog
option dontlognull
retries 3
timeout connect 5000
timeout client 50000
timeout server 50000
frontend http-in
bind *:80
default_backend webservers
backend webservers
stats enable
stats auth admin:admin
stats uri /haproxy?stats
balance roundrobin
option httpchk
option forwardfor
option http-server-close
server apache1 ${APACHE_1_IP}:${APACHE_EXPOSED_PORT} check
server apache2 ${APACHE_2_IP}:${APACHE_EXPOSED_PORT} check
ubuntu@linux:~/helloworld$ cat docker/.env
COMPOSE_PROJECT_NAME=helloworld
APACHE_EXPOSED_PORT=80
APACHE_1_IP=192.168.0.11
APACHE_2_IP=192.168.0.22
HA_PROXY_IP=192.168.0.33
NETWORK_SUBNET=192.168.0.0/24
ubuntu@linux:~/helloworld$ cat docker/docker-compose.yml
version: '3'
services:
apache_img_1:
container_name: ${COMPOSE_PROJECT_NAME}_apache_con_1
build: ./apache/1
expose:
- ${APACHE_EXPOSED_PORT}
networks:
public_net:
ipv4_address: ${APACHE_1_IP}
apache_img_2:
container_name: ${COMPOSE_PROJECT_NAME}_apache_con_2
build: ./apache/2
expose:
- ${APACHE_EXPOSED_PORT}
networks:
public_net:
ipv4_address: ${APACHE_2_IP}
haproxy_img:
build: ./haproxy
ports:
- 80:80
expose:
- 80
networks:
public_net:
ipv4_address: ${HA_PROXY_IP}
environment:
- APACHE_1_IP=${APACHE_1_IP}
- APACHE_2_IP=${APACHE_2_IP}
- APACHE_EXPOSED_PORT=${APACHE_EXPOSED_PORT}
networks:
public_net:
driver: bridge
ipam:
driver: default
config:
- subnet: ${NETWORK_SUBNET}
Validate "docker-compose.yml" file and see the mapping.
ubuntu@linux:~/helloworld/docker$ docker-compose config
networks:
public_net:
driver: bridge
ipam:
config:
- subnet: 192.168.0.0/24
driver: default
services:
apache_img_1:
build:
context: /home/ubuntu/helloworld/docker/apache/1
container_name: helloworld_apache_con_1
expose:
- '80'
networks:
public_net:
ipv4_address: 192.168.0.11
apache_img_2:
build:
context: /home/ubuntu/helloworld/docker/apache/2
container_name: helloworld_apache_con_2
expose:
- '80'
networks:
public_net:
ipv4_address: 192.168.0.22
haproxy_img:
build:
context: /home/ubuntu/helloworld/docker/haproxy
environment:
APACHE_1_IP: 192.168.0.11
APACHE_2_IP: 192.168.0.22
APACHE_EXPOSED_PORT: '80'
expose:
- 80
networks:
public_net:
ipv4_address: 192.168.0.33
ports:
- 80:80/tcp
version: '3.0'
When you run command below without -d
flag, you will see HAProxy pinging Apache servers every 1 seconds. This proves that our setup works fine.
ubuntu@linux:~/helloworld/docker$ docker-compose up
Creating network "helloworld_public_net" with driver "bridge"
Building haproxy_img
Successfully tagged helloworld_haproxy_img:latest
Building apache_img_2
Successfully tagged helloworld_apache_img_2:latest
Building apache_img_1
Successfully tagged helloworld_apache_img_1:latest
Creating helloworld_apache_con_1 ... done
Creating helloworld_apache_con_1 ...
Creating helloworld_apache_con_2 ...
helloworld_apache_con_1 | 192.168.0.33 - - [03/Feb/2018:21:15:29 +0000] "OPTIONS / HTTP/1.0" 200 -
helloworld_apache_con_2 | 192.168.0.33 - - [03/Feb/2018:21:15:29 +0000] "OPTIONS / HTTP/1.0" 200 -
helloworld_apache_con_1 | 192.168.0.33 - - [03/Feb/2018:21:15:31 +0000] "OPTIONS / HTTP/1.0" 200 -
helloworld_apache_con_2 | 192.168.0.33 - - [03/Feb/2018:21:15:33 +0000] "OPTIONS / HTTP/1.0" 200 -
helloworld_apache_con_1 | 192.168.0.33 - - [03/Feb/2018:21:15:33 +0000] "OPTIONS / HTTP/1.0" 200 -
helloworld_apache_con_2 | 192.168.0.33 - - [03/Feb/2018:21:15:31 +0000] "OPTIONS / HTTP/1.0" 200 -
helloworld_apache_con_1 | 192.168.0.33 - - [03/Feb/2018:21:15:35 +0000] "OPTIONS / HTTP/1.0" 200 -
...
You must configure Apache not to log pinging request coming from HAProxy otherwise Apache logs will be bloated quickly. If you want to see how it is done, head "HAProxy" section in my blog and apply what it says under "Webserver Logs" header.
If you want to see the details of each element, you can run docker inspect
command.
ubuntu@linux:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld_apache_img_2 latest 6f63653a9e68 9 minutes ago 177MB
helloworld_haproxy_img latest 363551ccafe6 9 minutes ago 136MB
helloworld_apache_img_1 latest 83bc617be089 9 minutes ago 177MB
...
ubuntu@linux:~$ docker network ls
NETWORK ID NAME DRIVER SCOPE
c72c538d9025 helloworld_public_net bridge local
...
ubuntu@linux:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4e4291f3a95c helloworld_apache_img_2 "httpd-foreground" 11 minutes ago Up 11 minutes 80/tcp helloworld_apache_con_2
ebba54230552 helloworld_apache_img_1 "httpd-foreground" 11 minutes ago Up 11 minutes 80/tcp helloworld_apache_con_1
a770b68939c5 helloworld_haproxy_img "/docker-entrypoin..." 11 minutes ago Up 11 minutes 0.0.0.0:80->80/tcp helloworld_haproxy_img_1
You can do benchmark with command below. It will send total of 10000 requests and 30 concurrent requests at a time. When you run this command, you can also run htop
command in one of the Apache containers to see how CPU and Memory are used.
ubuntu@linux:~$ ab -n 10000 -c 30 http://192.168.0.33/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.0.33 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software: Apache/2.4.29
Server Hostname: 192.168.0.33
Server Port: 80
Document Path: /
Document Length: 29 bytes
Concurrency Level: 30
Time taken for tests: 2.943 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 2730000 bytes
HTML transferred: 290000 bytes
Requests per second: 3397.93 [#/sec] (mean)
Time per request: 8.829 [ms] (mean)
Time per request: 0.294 [ms] (mean, across all concurrent requests)
Transfer rate: 905.89 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 6
Processing: 0 9 5.4 8 153
Waiting: 0 8 5.4 8 153
Total: 0 9 5.4 8 154
Percentage of the requests served within a certain time (ms)
50% 8
66% 9
75% 9
80% 10
90% 11
95% 13
98% 16
99% 25
100% 154 (longest request)
While both Apache servers are running.
ubuntu@linux:~$ for i in {1..10}; do curl 192.168.0.33:80; done
Serving from Apache Server 1
Serving from Apache Server 2
Serving from Apache Server 1
Serving from Apache Server 2
...
While only Apache 1 server is running.
ubuntu@linux:~$ for i in {1..10}; do curl 192.168.0.33:80; done
Serving from Apache Server 1
Serving from Apache Server 1
Serving from Apache Server 1
Serving from Apache Server 1
...
While only Apache 2 server is running.
ubuntu@linux:~$ for i in {1..10}; do curl 192.168.0.33:80; done
Serving from Apache Server 2
Serving from Apache Server 2
Serving from Apache Server 2
Serving from Apache Server 2
...
Apache servers are down.
ubuntu@linux:~$ curl 192.168.0.33:80
<html><body><h1>503 Service Unavailable</h1>
No server is available to handle this request.
</body></html>