Docker(四)之应用栈

前言

  • 本文参考《Docker容器和容器云》2.3.2章节应用栈搭建过程,对原书中出现的问题,以及镜像不断的更新导致的错误,做了修改,特此说明;
  • Docker APP Stack:简化的Docker集群、快速、准确、自动化部署集群;

实验环境

  • 宿主机:Ubuntu-14.04_X64
  • Docker:1.12.6

前期准备

  • 我们将搭建一个包含6个节点的Docker应用栈,其中包括一个负载均衡代理节点、两个Web应用节点、一个主数据库节点及两个从数据库节点。

{% asset_img AppStack.jpg 应用栈结构图 %}

  • 获取镜像:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 获取Django镜像
$ docker pull django

# 获取HAProxy镜像
$ docker pull haproxy

# 获取Redis镜像
$ docker pull redis

# 查看本地镜像列表
$ docker images
  • 应用容器节点间的互联,由于我们使用的为同一台宿主机,若为真正的分布式架构集群,还应处理容器的跨主机通信问题,在使用run命令创建容器时,添加--link选项,通过容器名,进行容器间安全的交互通信;
  • 容器启动顺序:
    1. 启动1redis-master容器节点;
    2. 启动2redis-slave容器节点并连接redis-master节点;
    3. 启动2Web APP容器节点并连接redis-master节点;
    4. 启动1HAProxy容器节点并连接2个Web APP节点;

搭建指导

启动各节点并互连

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 请总共打开宿主机的7个终端, 每个终端连接一个容器
# 启动redis数据库节点
$ 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
 
# 启动APP节点
$ docker run -it --name APP1 --link redis-master:db -v ~/Projects/Django/App1:/usr/src/app django /bin/bash
$ docker run -it --name APP2 --link redis-master:db -v ~/Projects/Django/App2:/usr/src/app django /bin/bash
 
# 启动HAProxy节点
$ docker run -it --name HAProxy --link APP1:APP1 --link APP2:APP2 -p 6301:6301 -v ~/Projects/HAProxy:/tmp haproxy /bin/bash
 
# 查看运行中的容器
$ docker ps

配置redis数据库,在宿主机上创建redis数据库的配置文件

  • redis-master配置文件:
1
$ vim redis-master.conf
 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
27
28
daemonize yes
pidfile /var/run/redis.pid
port 6379
timeout 0
tcp-keepalive 0
databases 1
 
stop-writes-on-bgsave-error no
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /tmp/
 
maxmemory 2gb
maxmemory-policy allkeys-lru
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 512mb
hash-max-ziplist-entries 64
hash-max-ziplist-value 128
list-max-ziplist-entries 64
list-max-ziplist-value 128
set-max-intset-entries 64
zset-max-ziplist-entries 64
zset-max-ziplist-value 128
activerehashing yes
  • redis-slave配置文件:
1
$ vim redis-slave.conf
 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
27
28
29
daemonize yes
pidfile /var/run/redis.pid
port 6379
slaveof master 6379
timeout 0
tcp-keepalive 0
databases 1
 
stop-writes-on-bgsave-error no
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /tmp/
 
maxmemory 2gb
maxmemory-policy allkeys-lru
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 512mb
hash-max-ziplist-entries 64
hash-max-ziplist-value 128
list-max-ziplist-entries 64
list-max-ziplist-value 128
set-max-intset-entries 64
zset-max-ziplist-entries 64
zset-max-ziplist-value 128
activerehashing yes
  • 配置redis数据库(未简化):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 适用于1.12版本及以上
## 获取数据卷的挂载点
$ docker inspect -f '{{ .Mounts }}' redis-master
$ docker inspect -f '{{ .Mounts }}' redis-slave1
$ docker inspect -f '{{ .Mounts }}' redis-slave2
 
## 分别记录挂载点
## 形如:/var/lib/docker/volumes/<Container-ID>/_data
 
## 拷贝配置文件
$ cp redis-master.conf <redis-master的挂载点>
$ cp redis-slave.conf <redis-slave1的挂载点>
$ cp redis-slave.conf <redis-slave2的挂载点>
  • 配置redis数据库(简化版):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Docker版本:1.12
## 拷贝配置文件
$ cp redis-master.conf $(docker inspect -f '{{ .Mounts }}' redis-master | awk '{print $2}')
$ cp redis-slave.conf $(docker inspect -f '{{ .Mounts }}' redis-slave1 | awk '{print $2}')
$ cp redis-slave.conf $(docker inspect -f '{{ .Mounts }}' redis-slave2 | awk '{print $2}')

# Docker版本:1.13及以上
## 拷贝配置文件
$ cp redis-master.conf $(docker inspect -f '{{ .Mounts }}' redis-master | awk '{print $3}')
$ cp redis-slave.conf $(docker inspect -f '{{ .Mounts }}' redis-slave1 | awk '{print $3}')
$ cp redis-slave.conf $(docker inspect -f '{{ .Mounts }}' redis-slave2 | awk '{print $3}')
  • 测试操作:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 在redis-master节点
$ cp /data/redis-master.conf /usr/local/bin/redis.conf
$ cd /usr/local/bin/
$ redis-server redis.conf
$ redis-cli
127.0.0.1:6379> set stu_num 1441310222
127.0.0.1:6379> get stu_num
127.0.0.1:6379> exit

# 在redis-slave节点
$ cp /data/redis-slave.conf /usr/local/bin/redis.conf
$ cd /usr/local/bin/
$ redis-server redis.conf
$ redis-cli
127.0.0.1:6379> get stu_num
127.0.0.1:6379> exit

配置Web APP节点

在Web APP节点上

  • 安装redis模块:
1
$ pip install redis
  • 测试操作:
1
$ python
1
2
3
import redis
print(redis.__file__)
exit()
  • 创建Hello-WorldAPP
1
2
3
4
5
6
$ cd /usr/src/app
$ mkdir dockerweb
$ cd dockerweb
$ django-admin.py startproject redisweb
$ cd redisweb
$ python manage.py startapp helloworld

在宿主机上

  • 切换目录:
1
2
$ cd ~/Projects/Django/App1/dockerweb/redisweb/
$ cd ~/Projects/Django/App2/dockerweb/redisweb/
  • 配置视图(View):
1
$ vim helloworld/views.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from django.shortcuts import render
from django.http import HttpResponse
import redis


def hello(requset):
    r = redis.Redis(host='db', port=6379, db=0)
    # 在APP2节点修改为HelloWorld-APP2
    r.set('app_info', 'HelloWorld-APP1')
    app_info = bytes.decode(r.get('app_info'))
    stu_num_info = int(r.get('stu_num'))
    info = "Hello, everyone!<br/>Get Hi: {0}<br/>Get stu_num: {1}<br/>".format(app_info, stu_num_info)
    return HttpResponse(info)
  • 更改配置文件:
1
$ vim redisweb/settings.py
1
2
3
4
5
6
# 文件内容
ALLOWED_HOSTS = ['*',]
INSTALLED_APPS = [
    ...
    'helloworld',
]
  • 配置url
1
$ vim 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'^$', hello),
]

Web APP节点上

  • 初始化项目:
1
2
$ python manage.py makemigrations
$ python manage.py migrate
  • 启动服务:
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

配置HAProxy节点

在宿主机上

  • 切换目录:
1
$ cd ~/Projects/HAProxy/
  • 创建配置文件:
1
$ vim 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
27
28
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
    bind-process 1
    stats uri /haproxy-stats
    stats auth phil:NRG93012
        server APP1 APP1:8001 check inter 2000 rise 2 fall 5
        server APP2 APP2:8002 check inter 2000 rise 2 fall 5

HAProxy节点

  • 切换目录:
1
$ cd /tmp
  • 复制配置文件:
1
2
$ cp haproxy.cfg /usr/local/sbin

  • 切换目录:
1
$ cd /usr/local/sbin
  • 启动服务:
1
$ haproxy -f haproxy.cfg

测试操作

  • 使用浏览器访问,http://<宿主机的IP地址>:<6301>/
  • 多次请求代理节点,HAProxy节点会自动分发请求,达到负载均衡的目的;