├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── entrypoint.sh ├── favicon.ico ├── haproxy.cfg ├── index-master.html ├── index-slave.html ├── keepalived-master.conf └── keepalived-slave.conf /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.13.5-alpine 2 | 3 | RUN apk update && apk upgrade 4 | 5 | RUN apk add --no-cache bash curl ipvsadm iproute2 openrc keepalived && \ 6 | rm -f /var/cache/apk/* /tmp/* 7 | 8 | COPY entrypoint.sh /entrypoint.sh 9 | 10 | RUN chmod +x /entrypoint.sh 11 | 12 | CMD ["/entrypoint.sh"] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hengfeng Li 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-keepalived-docker-demo 2 | A small demo to run two nginx containers in active-passive mode by using keepalived and VIP. 3 | 4 | This demo is made to simulate a scenario with two running nginx servers, one as a master and another as a backup, in order to achieve high availability. 5 | 6 | There are some limitations in this demo: 7 | * I run it in a single host. Normally, it would be better to create a scenario with two hosts. 8 | * I didn't spend time to map the virtual IP from docker network to the host. Instead, I use a haproxy to do this task. 9 | * Running both nginx and keepalived in an Alpine container is not easy. rc-service could not manage keepalived properly. I thought to use supervisor or circus, but they are a bit heavy in space. So I directly run keepalived as a background daemon. However, the drawback is if it fails, it will not restart (not robust). 10 | 11 | ## How to run 12 | 13 | Run the following command: 14 | 15 | ```bash 16 | $ docker-compose up -d 17 | ``` 18 | 19 | Now, visit `localhost:8000` and you would see `Primary`. 20 | 21 | ```bash 22 | $ docker ps 23 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 24 | 42cc6637255b nginxkeepaliveddockerdemo_nginx_slave "/entrypoint.sh" 28 seconds ago Up 24 seconds 80/tcp nginxkeepaliveddockerdemo_nginx_slave_1 25 | 3f39a7e356ce haproxy:1.7-alpine "/docker-entrypoin..." 28 seconds ago Up 25 seconds 0.0.0.0:8000->6301/tcp nginxkeepaliveddockerdemo_proxy_1 26 | 6151af0d50db nginxkeepaliveddockerdemo_nginx_master "/entrypoint.sh" 28 seconds ago Up 24 seconds 80/tcp nginxkeepaliveddockerdemo_nginx_master_1 27 | ``` 28 | 29 | Try to pause the master server: 30 | 31 | ```bash 32 | $ docker pause nginxkeepaliveddockerdemo_nginx_master_1 33 | ``` 34 | 35 | Now, visit `localhost:8000` and you should see `Secondary`. 36 | 37 | Recover the master server and pause the slave server: 38 | ```bash 39 | $ docker unpause nginxkeepaliveddockerdemo_nginx_master_1 40 | $ docker pause nginxkeepaliveddockerdemo_nginx_slave_1 41 | ``` 42 | 43 | Visit `localhost:8000` and you should see `Primary` again. 44 | 45 | As you can see, when a master server is down, a backup server does automatic failover. 46 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | nginx_master: 4 | build: 5 | context: ./ 6 | dockerfile: ./Dockerfile 7 | volumes: 8 | - ./index-master.html:/usr/share/nginx/html/index.html 9 | - ./favicon.ico:/usr/share/nginx/html/favicon.ico 10 | - ./keepalived-master.conf:/etc/keepalived/keepalived.conf 11 | networks: 12 | static-network: 13 | ipv4_address: 172.20.128.2 14 | cap_add: 15 | - NET_ADMIN 16 | nginx_slave: 17 | build: 18 | context: ./ 19 | dockerfile: ./Dockerfile 20 | volumes: 21 | - ./index-slave.html:/usr/share/nginx/html/index.html 22 | - ./favicon.ico:/usr/share/nginx/html/favicon.ico 23 | - ./keepalived-slave.conf:/etc/keepalived/keepalived.conf 24 | networks: 25 | static-network: 26 | ipv4_address: 172.20.128.3 27 | cap_add: 28 | - NET_ADMIN 29 | proxy: 30 | image: haproxy:1.7-alpine 31 | ports: 32 | - 8000:6301 33 | volumes: 34 | - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg 35 | networks: 36 | - static-network 37 | 38 | networks: 39 | static-network: 40 | ipam: 41 | config: 42 | - subnet: 172.20.0.0/16 -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | /usr/sbin/keepalived -n -l -D -f /etc/keepalived/keepalived.conf --dont-fork --log-console & 4 | 5 | nginx -g "daemon off;" -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hengfengli/nginx-keepalived-docker-demo/ac8f88ea2b843350073c3c8776c40c386feecdcb/favicon.ico -------------------------------------------------------------------------------- /haproxy.cfg: -------------------------------------------------------------------------------- 1 | global 2 | log 127.0.0.1 local0 3 | maxconn 4096 4 | daemon 5 | nbproc 4 6 | 7 | defaults 8 | log 127.0.0.1 local3 9 | mode http 10 | option dontlognull 11 | option redispatch 12 | retries 2 13 | maxconn 2000 14 | balance roundrobin 15 | timeout connect 5000ms 16 | timeout client 5000ms 17 | timeout server 5000ms 18 | 19 | frontend main 20 | bind *:6301 21 | default_backend webserver 22 | 23 | backend webserver 24 | server ngxin_master 172.20.128.4:80 check inter 2000 rise 2 fall 5 -------------------------------------------------------------------------------- /index-master.html: -------------------------------------------------------------------------------- 1 |

Primary

-------------------------------------------------------------------------------- /index-slave.html: -------------------------------------------------------------------------------- 1 |

Secondary

-------------------------------------------------------------------------------- /keepalived-master.conf: -------------------------------------------------------------------------------- 1 | vrrp_script chk_nginx { 2 | script "pidof nginx" 3 | interval 2 4 | } 5 | 6 | vrrp_instance VI_1 { 7 | state MASTER 8 | interface eth0 9 | virtual_router_id 33 10 | priority 200 11 | advert_int 1 12 | unicast_src_ip 172.20.128.2 13 | unicast_peer { 14 | 172.20.128.3 15 | } 16 | 17 | authentication { 18 | auth_type PASS 19 | auth_pass letmein 20 | } 21 | 22 | virtual_ipaddress { 23 | 172.20.128.4/24 dev eth0 24 | } 25 | 26 | track_script { 27 | chk_nginx 28 | } 29 | } -------------------------------------------------------------------------------- /keepalived-slave.conf: -------------------------------------------------------------------------------- 1 | vrrp_script chk_nginx { 2 | script "pidof nginx" 3 | interval 2 4 | } 5 | 6 | vrrp_instance VI_1 { 7 | state BACKUP 8 | interface eth0 9 | virtual_router_id 33 10 | priority 100 11 | advert_int 1 12 | unicast_src_ip 172.20.128.3 13 | unicast_peer { 14 | 172.20.128.2 15 | } 16 | 17 | authentication { 18 | auth_type PASS 19 | auth_pass letmein 20 | } 21 | 22 | virtual_ipaddress { 23 | 172.20.128.4/24 dev eth0 24 | } 25 | 26 | track_script { 27 | chk_nginx 28 | } 29 | } --------------------------------------------------------------------------------