使用容器技术快速搭建一个高可用服务集群。

应用栈具体结构

经典的 Web 高可用架构。由一个负载均衡代理容器,两个 Web 应用容器,和一个主从架构的数据库容器集群组成。使用容器技术,使你能够在资源有限的计算机上快速搭建实践一个高可用架构,提高学习效率 :>

准备工作

获取镜像

1
2
3
$ docker pull django
$ docker pull haproxy
$ docker pull redis

启动并互联容器栈节点

使用 --link 参数进行互联(现在这个参数已经成为 LEGACY ,官方推荐使用用户自定义 bridge 来替代它)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Boot the redis cluster.
$ docker run -it --name redis-master redis /bin/bash
$ docker run -it --name redis-slave1 --link redis-master:master redis /bin/bash
$ docker run -it --name redis-slave2 --link redis-master:master redis /bin/bash

# Boot the application cluster.
$ docker run -it --name APP1 --link redis-master:db -v /your/path/to/APP1:/usr/src/app django /bin/bash
$ docker run -it --name APP2 --link redis-master:db -v /your/path/to/APP2:/usr/src/app django /bin/bash

# Boot the HAProxy container.
$ docker run -it --name HAProxy --link APP1:APP1 --link APP2:APP2 -p 6301:6301 -v /your/path/to/HAProxy:/tmp haproxy /bin/bash

检查容器运行状态

1
2
3
4
5
6
7
8
$ docker ps                                            
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
54b9018f61e6        haproxy             "/docker-entrypoint.…"   21 hours ago        Up 21 hours         0.0.0.0:6301->6301/tcp   HAProxy
b5bd8728af08        django              "/bin/bash"              43 hours ago        Up 43 hours                                  APP2
7b9fdd342f3c        django              "/bin/bash"              43 hours ago        Up 43 hours                                  APP1
fb1c509d0245        redis               "docker-entrypoint.s…"   43 hours ago        Up 43 hours         6379/tcp                 redis-slave2
4b3284f6a268        redis               "docker-entrypoint.s…"   43 hours ago        Up 43 hours         6379/tcp                 redis-slave1
19419d882a8d        redis               "docker-entrypoint.s…"   43 hours ago        Up 43 hours         6379/tcp                 redis-master

可见,一个 haproxy 容器,两个 django 容器和三个 redis 容器都在正常运行中。

配置工作

配置 Redis 集群

拷贝默认的 redis.conf 模板至容器的 /data 目录下,对于 redis-master ,我们需要修改模板中的这些参数:

1
2
3
4
# bind 127.0.0.1
protected-mode no
daemonize yes
pidfile /var/run/redis.pid

修改后在其目录下运行 redis

1
$ /usr/local/bin/redis-server redis.conf

对于从数据库 redis-slave1redis-slave2 ,我们需要修改模板中的这些参数:

1
2
3
daemonize yes
pidfile /var/run/redis.pid
slaveof master 6379

修改后在其目录下执行该命令运行 redis

1
# /usr/local/bin/redis-server redis.conf

Redis 节点测试

redis-masterredis-slave1redis-slave2 上分别启动 Redis 客户端程序:

1
# redis-cli

redis-master 上写入 <“master”, “19419d882a8d”> 的数据:

1
2
127.0.0.1:6379> set master 19419d882a8d
OK

尝试在 redis-master 上获取刚写入的 “master” 数据:

1
2
127.0.0.1:6379> get master
"19419d882a8d"

redis-slave1redis-slave2 中获取 “master” 数据:

1
2
127.0.0.1:6379> get master
"19419d882a8d"

若能获取到则说明 Master 中的数据已经成功同步至 Slave 的数据库中,主从架构的 Redis 集群已经搭建好了。

配置应用集群

APP1APP2 这两个容器都需要进行配置。

首先安装 redis 的 Python SDK:

1
# pip install redis

在容器的 /usr/src/app 目录下创建 Django 应用:

1
2
3
4
# cd /usr/src/app/
# django-admin startproject redisweb
# cd redisweb/
# python manage.py startapp helloworld

新建好应用之后,可以在主机中挂载的目录 /your/path/to/APP1 进行编辑。

或是 /your/path/to/APP2

修改视图文件 redisweb/helloworld/views.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from django.shortcuts import render
from django.http import HttpResponse
import redis


def hello(request):
    str = redis.__file__
    str += "<br>"
    r = redis.Redis(host='db', port=6379, db=0)
    info = r.info()
    r.set('Hi', 'HelloWorld-APP1')
    str += ("Get Hi: %s <br>" % r.get("Hi"))
    str += ("Redis info: <br>")
    str += ("Key: Info Value")
    for key in info:
        str += ("%s: %s <br>" % (key, info[key]))
        
    return HttpResponse(str)

修改路由表 redisweb/redisweb/urls.py

1
2
3
4
5
6
7
8
from django.conf.urls import url
from django.contrib import admin
from helloworld.views import hello

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^helloworld$', hello),
]

修改 Django 应用设置,将 helloworld 应用加入 INSTALLED_APPS 中 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Line 33
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'helloworld',
]

修改完之上的文件之后,重新回到容器中,在目录 /usr/src/app/redisweb 下完成数据库的迁移:

1
2
# python manage.py makemigrations
# python manage.py migrate

为了使用 HAProxy 进行负载均衡的代理,我们将 APP1 的服务端口设为 8001APP2 的服务端口设为 8002 ,根据这个配置来启动应用服务器:

1
2
3
4
5
# APP1
# python manage.py runserver 0.0.0.0:8001

# APP2
# python manage.py runserver 0.0.0.0:8002

可以通过应用服务容器的 IP 地址来尝试访问下服务,如果都能够访问,则应用服务器配置成功。

配置 HAProxy

最后我们需要配置 HAProxy 节点,它将作为我们的网关,直接对外进行服务并完成应用的负载均衡。

HAProxy 容器的挂载目录下新建 haproxy.cfg 配置文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
global
    log 127.0.0.1   local0
    maxconn 4096
    chroot  /usr/local/sbin
    daemon
    nbproc  4
    pidfile /usr/local/sbin/haproxy.pid

defaults
    log 127.0.0.1    local3
    mode    http
    option  dontlognull
    option  redispatch
    retries 2
    maxconn 2000
    balance roundrobin
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

listen redis_proxy
    bind    0.0.0.0:6301
    stats   enable
    stats   uri /haproxy-stats
        server APP1 APP1:8001 check inter 2000 rise 2 fall 5
        server APP2 APP2:8002 check inter 2000 rise 2 fall 5

配置完后,在同目录下启动 HAProxy 服务:

1
# /usr/local/sbin/haproxy -f haproxy.cfg

大功告成

现在,通过 docker inspect 查看 HAProxy 容器的 IP,就可以在你喜欢的浏览器上看到我们刚才搭建的 Web 服务了!

查看 HAProxy 容器 IP:

1
2
$ HAPROXY_IP=$(docker inspect --format "{{ .NetworkSettings.IPAddress }}" HAProxy)
$ echo $HAPROXY_IP

访问 http://HAPROXY_IP/haproxy-stats 即为 HAProxy 的数据面板;http://HAPROXY_IP/helloworld 即为刚才我们所编写的应用!

Congratulations to yourself 🎉

本文大部分内容来自《Docker 容器与容器云(第2版)》的 2.3 章节,可能时间久远镜像更新,该书中部分操作无法重现,故写此博文将我的方法分享给各位,希望能够有所帮助。


知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。