Docker集群部署
Docker是一个新兴的轻量级虚拟化技术,其易用、跨平台、可移植的特性使其在集群系统的搭建方面有着得天独厚的优势。Docker能够标准化封装应用程序所需的整个运行时环境,因此基于Docker,我们可以实现分布式应用集群的快速、准确、自动化部署。考虑到读者可能是初次接触Docker的新手,我们将降低难度,在一台机器上利用Docker自带的命令行工具,搭建一个Docker应用栈,利用多个容器来组成一个特定的应用。读者可参考应用栈部署的过程,一步一步搭建你的第一个Docker应用栈。对于有一定Docker使用经验的读者,也可尝试在多台机器上搭建一个真正的Docker集群,相信这个过程将对理解Docker相关工作原理大有裨益。
第一个Hello World
在Docker中,镜像是容器的基础,可以通过镜像来运行容器。本节将举例说明如何有效地利用Docker Hub中已有的镜像资源来搭建一个Docker应用栈。在开始搭建过程前,需要对所要搭建的应用栈进行简单的设计和描述:我们将搭建一个包含6个节点的Docker应用栈,其中包括一个代理节点、两个Web的应用节点、一个主数据库节点及两个从数据库节点。
HAProxy是负载均衡代理节点;Redis是非关系型的数据库,它由一个主数据库节点和两个从数据库节点组成;App是应用,这里是使用Python语言、基于Django架构设计一个访问数据库的基础Web应用。
- 获取应用栈各节点所需镜像
docker pull ubuntu
docker pull django
docker pull haproxy
docker pull redis
- 应用栈容器节点互联
在搭建第一个Hello World应用栈时,将在同一主机下进行Docker应用栈搭建。如果是一个真正的分布式架构集群,还需要处理容器的跨主机通信问题,在这里我们将不做介绍,请读者参考第4章高级实践中关于网络的处理方式。鉴于在同一主机下搭建容器应用栈的环境,只需要完成容器互联来实现容器间的通信即可,这里采用docker run命令的——link选项建立容器间的互联关系。这里介绍一下——link选项的用法,通过——link选项能够进行容器间安全的交互通信,使用格式name:alias,可在一个docker run命令中重复使用该参数。
通过上面的原理可以将——link设置理解为一条IP地址的单向记录信息,因此在搭建容器应用栈时,需要注意各个容器节点的启动顺序,以及对应的——link参数设置。应用栈各节点的连接信息如下:❏ 启动redis-master容器节点;❏ 两个redis-slave容器节点启动时要连接到redis-master上;❏ 两个APP容器节点启动时要连接到redis-master上;❏ HAProxy容器节点启动时要连接到两个APP节点上。综上所述,容器的启动顺序应为:redis-master → redis-slave → APP → HAProxy
- 应用栈容器节点启动
#启动redis容器
docker run -it --name redis-master redis /bin/bash
docker run -it --name redis-slave1 --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
通过——link选项来建立容器间的连接,不但可以避免容器的IP和端口暴露到外网所导致的安全问题,还可以防止容器在重启后IP地址变化导致的访问失效,它的原理类似于DNS服务器的域名和地址映射。当容器的IP地址发生变化时,Docker将自动维护映射关系中的IP地址,
说明 以上容器启动时,为了方便后续与容器进行交互操作,统一设定启动命令为/bin/bash,请在启动每个新的容器时都分配一个终端执行。启动的容器信息可以通过docker ps命令查看,示例如下:
[root@iZbp102fxl8duhse0avs3mZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
667ae707caea haproxy "docker-entrypoint.s…" 37 minutes ago Up 36 minutes 0.0.0.0:6301->6301/tcp, :::6301->6301/tcp Haproxy
0e206ce34569 django "/bin/bash" About an hour ago Up About an hour App2
ce9aa6740771 django "/bin/bash" About an hour ago Up About an hour App1
ee95a0ae7e5f redis "docker-entrypoint.s…" About an hour ago Up About an hour 6379/tcp redis-slave2
0df99da2ff2e redis "docker-entrypoint.s…" About an hour ago Up About an hour 6379/tcp redis-slave1
790681bc9bcf redis "docker-entrypoint.s…" About an hour ago Up About an hour 6379/tcp redis-master
- 应用栈容器节点的配置
在应用栈的各容器节点都启动后,需要对它们进行配置和修改,以便实现特定的功能和通信协作,下面按照容器的启动顺序依次进行解释。
● Redis Master主数据库容器节点的配置Redis Master主数据库容器节点启动后,我们需要在容器中添加Redis的启动配置文件,以启动Redis数据库。需要说明的是,对于需要在容器中创建文件的情况,由于容器的轻量化设计,其中缺乏相应的文本编辑命令工具,这时可以利用volume来实现文件的创建。在容器启动时,利用-v参数挂载volume,在主机和容器间共享数据,这样就可以直接在主机上创建和编辑相关文件,省去了在容器中安装各类编辑工具的麻烦。
在利用Redis镜像启动容器时,镜像中已经集成了volume的挂载命令,所以我们需要通过docker inspect命令来查看所挂载volume的情况。打开一个新的终端,执行如下命令:
[root@iZbp102fxl8duhse0avs3mZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9b651b43076f haproxy "docker-entrypoint.s…" 45 hours ago Up 45 hours 0.0.0.0:6301->6301/tcp, :::6301->6301/tcp Haproxy
0e206ce34569 django "/bin/bash" 8 days ago Up 3 days App2
ce9aa6740771 django "/bin/bash" 8 days ago Up 6 hours App1
ee95a0ae7e5f redis "docker-entrypoint.s…" 8 days ago Up 8 days 6379/tcp redis-slave2
0df99da2ff2e redis "docker-entrypoint.s…" 8 days ago Up 8 days 6379/tcp redis-slave1
790681bc9bcf redis "docker-entrypoint.s…" 8 days ago Up 6 hours 6379/tcp redis-master
# 查询redis-master的挂载路径
[root@iZbp102fxl8duhse0avs3mZ ~]# docker inspect redis-master |grep -A10 "Mounts"
"Mounts": [
{
"Type": "volume",
"Name": "2368ed92faa0896499ddfaba9aab884e48f0bea18f93b1e0b7905a6d42fda34a",
"Source": "/var/lib/docker/volumes/2368ed92faa0896499ddfaba9aab884e48f0bea18f93b1e0b7905a6d42fda34a/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
如上我们可以发现redis-master的容器对应挂载路径source:/var/lib/docker/volumes/2368ed92faa0896499ddfaba9aab884e48f0bea18f93b1e0b7905a6d42fda34a/_data,而它对应的容器里面的路径是/data
这样我们可以在容器外将需要配置的文件的准备好,笔者服务器么有装redis,所以只能从github上将redis.conf的配置辅助下来,自己重新配置,笔者这边redis.conf
protected-mode no
注释掉绑定的ip,允许外网访问
#bind 127.0.0.1 -::1
#无需密码访问
requirepass ''
#后台运行
daemonize yes
配置完成之后,将redis.conf文件复制到对应挂载的路径下面 /var/lib/docker/volumes/2368ed92faa0896499ddfaba9aab884e48f0bea18f93b1e0b7905a6d42fda34a/_data
然后进入容器会发现容器对应的路径/data下面会生成一样的文件
[root@iZbp102fxl8duhse0avs3mZ ~]# docker attach redis-master
root@790681bc9bcf:/usr/local/bin# cd /data/
root@790681bc9bcf:/data# ls
redis.conf
root@790681bc9bcf:/data#
退出docker 可用ctrl+p然后ctrl+q,可以退出容器,不停掉容器; 进入容器之后我们从之前的redis安装路径是在/usrl/local/bin,为了启动redis方便我们可以将redis.conf文件cp到对应的路径下面然后启动redis;redis-server redis.conf
最好在容器外可以查看redis无复
[root@iZbp102fxl8duhse0avs3mZ ~]# ps -ef|grep redis
root 49176 22675 0 14:32 ? 00:00:16 redis-server *:6379
root 49248 22824 0 14:34 ? 00:00:16 redis-server *:6379
root 49419 49382 0 14:38 ? 00:00:16 redis-server *:6379
root 50884 50816 0 21:01 pts/1 00:00:00 vim redis.conf
root 50951 50889 0 21:07 pts/2 00:00:00 docker attach redis-master
root 50993 50971 0 21:13 pts/3 00:00:00 grep --color=auto redis
用同样的方式可以将redis-slave1,redis-slave2启起来;
对于Redis的从数据库,需要修改如下几个参数:
pidfile /var/run/redis.pid
slaveof master 6379
- APP容器节点(Django)的配置
pip install redis
检查是否安装成功
python
import redis
print(redis.__file__)
#初始化一个web项目
root@ce9aa6740771:/usr/src/app# django-admin.py startproject redisweb
root@ce9aa6740771:/usr/src/app/dockerweb# ls
redisweb
root@ce9aa6740771:/usr/src/app/dockerweb# cd redis
bash: cd: redis: No such file or directory
root@ce9aa6740771:/usr/src/app/dockerweb# cd redisweb/
root@ce9aa6740771:/usr/src/app/dockerweb/redisweb# ls
db.sqlite3 helloworld manage.py redisweb
root@ce9aa6740771:/usr/src/app/dockerweb/redisweb# python manage.py startapp helloworld
db.sqlite3 helloworld manage.py redisweb
然后去容器外面编写redisweb里面的python文件,容器里面命令有限不利于开发
[root@iZbp102fxl8duhse0avs3mZ redisweb]# pwd
/root/projects/django/app1/dockerweb/redisweb
[root@iZbp102fxl8duhse0avs3mZ redisweb]# ll
总用量 48
-rw-r--r-- 1 root root 36864 7月 9 01:59 db.sqlite3
drwxr-xr-x 4 root root 4096 7月 13 07:39 helloworld
-rwxr-xr-x 1 root root 806 7月 9 00:41 manage.py
drwxr-xr-x 3 root root 4096 7月 13 07:42 redisweb
编写setting.py文件,添加helloworld的项目
[root@iZbp102fxl8duhse0avs3mZ app1]# cd dockerweb/redisweb/redisweb/
[root@iZbp102fxl8duhse0avs3mZ redisweb]# ls
__init__.py __pycache__ settings.py urls.py wsgi.py
[root@iZbp102fxl8duhse0avs3mZ redisweb]# vim settings.py
ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'helloworld',
]
在installed_app里面添加helloworld项目
[root@iZbp102fxl8duhse0avs3mZ app1]# cd dockerweb/redisweb/redisweb/
[root@iZbp102fxl8duhse0avs3mZ redisweb]# ls
__init__.py __pycache__ settings.py urls.py wsgi.py
[root@iZbp102fxl8duhse0avs3mZ redisweb]# vim settings.py
[root@iZbp102fxl8duhse0avs3mZ redisweb]# ls
__init__.py __pycache__ settings.py urls.py wsgi.py
配置helloworld项目的对外路径/helloworld
[root@iZbp102fxl8duhse0avs3mZ redisweb]# vim urls.py
from django.conf.urls import include,url
from django.contrib import admin
from helloworld.views import hello
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^helloworld$',hello),
]
开发完了之后,进入容器进行配置
root@ce9aa6740771:/usr/src/app/dockerweb/redisweb# python manage.py makemigrations
No changes detected
root@ce9aa6740771:/usr/src/app/dockerweb/redisweb# python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
No migrations to apply.
root@ce9aa6740771:/usr/src/app/dockerweb/redisweb#
配置完之后可以启动
python manage.py runserver 0.0.0.0:8001
然后就是配置haproxy
首先提供haproxy.cfg文件,
容器在配置中间件的时候,很多配置文件自己去配置,容器是轻量级,所以很多配置都不会提供,需要使用者根据自己需要自行配置
[root@iZbp102fxl8duhse0avs3mZ ~]# cat haproxy.cfg
global
log 127.0.0.1 local0 #日志输出配置,所有日志都记录在本纪, 通过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 5s
timeout client 50s
timeout server 50s
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
随后,进入到容器的volume目录/tmp下,将HAProxy的启动配置文件复制到HAProxy的工作目录中。执行过程如下
cp /tmp/haproxy.cfg /usr/local/sbin/
haproxy -f haproxy.cfg
执行可以访问6301端口