用docker搭建全栈式应用
简介
本文将以docker为基础,搭建一套简单的应用案例。该案例的应用架构将采用redis的master-slave模式作为数据存储,以django为框架提供web访问,以haproxy作为负载均衡。其架构图如下.
其中App1和App2属于同一个Django的APP.
准备工作
获取相应的docker images
可以直接从docker官方获取images
123$ docker pull django$ docker pull redis$ docker pull haproxy获取成功之后,检查一下images是否真的ok
12345$ docker imagesREPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZEhaproxy latest 46bc5babcc18 3 days ago 139.1 MBredis latest 84dbf5edc313 4 days ago 184.8 MBdjango latest 3b0bc67346a2 9 days ago 433.5 MB检查一下各自的版本(root权限) redis版本
123# docker run -it redis /bin/bashroot@a3ca293915cb:/data# redis-server -vRedis server v=3.2.0 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=5382f69a4e75566bhaproxy版本
1234# docker run -it haproxy /bin/bashroot@b11f07766c49:/# haproxy -vHA-Proxy version 1.6.5 2016/05/10Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>django版本
123# docker run -it django /bin/bashroot@1daf8d846018:/# django-admin version1.9.6
检查完后,可以用docker rm
把不用的containers删掉
|
|
从上,本文各版本采用如下:
service | version |
---|---|
haproxy | 1.6.5 |
django | 1.9.6 |
redis | 3.2.0 |
启动containers
本案例中containers的有关启动配置如下:
container | image | service | ports | volumes | links |
---|---|---|---|---|---|
redis-master | redis | redis-server | - | master:/data | - |
redis-slave1 | redis | redis-server | - | slave1:/data | redis-master:master |
redis-slave2 | redis | redis-server | - | slave2:/data | redis-master:master |
app1 | django | django app | - | app:/data | redis-master:redis |
app2 | django | django app | - | app:/data | redis-master:redis |
haproxy | haproxy | haproxy | 6301:6301 | haproxy:/data | app1:app1 app2:app2 |
启动redis
启动redis-master 在本地当前目录下创建子目录
master
,并将master
挂载到container里的/data
目录。container名字为redis-master
。为了方便调试,这里同时启动交互式模式-it
,并同时打开bash
123# mkdir master# docker run -it --name redis-master -v `pwd`/master:/data redis /bin/bashroot@3d187b223f56:/data#添加一个测试条目,取key为
redis_app
,该测试条目将被app1跟app2查询:12345root@3d187b223f56:/data# redis-cli127.0.0.1:6379> set redis_app "Hello redis app!"OK127.0.0.1:6379> get redis_app"Hello redis app!"启动redis-slave1 本文案例中将采用两个redis的slave,这里首先启动第一个。container名字为
redis-slave1
,同时为了方便的连接到redis master,这里直接用--link
将redis-master
container连接同时命名为master
。其实际效果相当于往/etc/hosts
添加一条记录,映射master
的域名到redis-master
的IP。通过这种方式,就不需要手工的添加master的IP,docker在启动的时候会自动添加。12345678910# docker run -it --name redis-slave1 --link redis-master:master -v `pwd`/slave1:/data redis /bin/bashroot@cef73ae816be:/data# cat /etc/hosts172.17.0.3 cef73ae816be127.0.0.1 localhost::1 localhost ip6-localhost ip6-loopbackfe00::0 ip6-localnetff00::0 ip6-mcastprefixff02::1 ip6-allnodesff02::2 ip6-allrouters172.17.0.2 master 3d187b223f56 redis-master可以看出,
--link
实际上在/etc/hosts
下添加了一条新的映射,分别把redis-master
的别名master
、container ID以及container name映射到master的IP。1172.17.0.2 master 3d187b223f56 redis-master如果没有错误,那么上面在master中设置的条目应该可以查询到:
123root@cef73ae816be:/data# redis-cli127.0.0.1:6379> get redis_app"Hello redis app!"启动redis-slave2 与redis-slave1同样的方式启动redis-slave2
12345678910# docker run -it --name redis-slave2 --link redis-master:master -v `pwd`/slave2:/data redis /bin/bashroot@1f22e9e15e8b:/data# cat /etc/hosts172.17.0.4 1f22e9e15e8b127.0.0.1 localhost::1 localhost ip6-localhost ip6-loopbackfe00::0 ip6-localnetff00::0 ip6-mcastprefixff02::1 ip6-allnodesff02::2 ip6-allrouters172.17.0.2 master 3d187b223f56 redis-master如果没有错误,那么上面在master中设置的条目应该可以查询到:
123root@cef73ae816be:/data# redis-cli127.0.0.1:6379> get redis_app"Hello redis app!"启动app1 app1需要连接到redis服务,因此需要把
redis-master
container连起来。同时为其创建app
目录,由于app1跟app2共享同一个Django项目,这里就不再单独为他们创建各自的目录。123# mkdir app# docker run -it --name app1 --link redis-master:redis -v `pwd`/app/:/data django /bin/bashroot@7e6e49321993:/#
配置
配置redis
获取redis初始配置
本文采用的版本为3.2.0, 可通过如下方式获取
1# wget https://raw.githubusercontent.com/antirez/redis/3.2.0/redis.conf配置redis-master
拷贝
redis.conf
到master
目录.1# cp redis.conf master/编辑改配置文件。默认里面有非常多的配置选项,这里只需要关注几个,并改成如下:
1234bind 0.0.0.0protected-mode yesdaemonize yeslogfile /var/log/redis.log由于已经将
./master
挂载到container的/data
目录,因此本地的改动在container里面也是可见的。配置redis-slave1
拷贝
redis.conf
到slave1
目录.1# cp redis.conf slave1/编辑改配置文件。默认里面有非常多的配置选项,这里只需要关注几个,并改成如下:
12345bind 0.0.0.0protected-mode yesdaemonize yeslogfile /var/log/redis.logslaveof master 6379最后一项
slaveof
将slave1设置为master的slave。同样,由于已经将./slave1
挂载到container的/data
目录,因此本地的改动在container里面也是可见的。配置redis-slave2
配置方式与
redis-slave1
一致,因此,直接拷贝slave1/redis.conf
到slave2
目录.1# cp slave1/redis.conf slave2/
配置Django App
配置app1
首先需要创建Django项目,这需要登录到
app1
系统中才能够调用Django命令来创建。12345678# docker attach app1root@7e6e49321993:/# cd /dataroot@7e6e49321993:/# django-admin startproject redisapproot@4b8240acb25a:/data# lsredisapproot@4b8240acb25a:/data# cd redisapp/root@4b8240acb25a:/data/redisapp# lsmanage.py redisapp接下来需要编辑Django代码以支持本案例中一个简单的API:
/query/
,此API将获取redis中的key为redis_app
的值(请注意,该值在上面启动redis的时候已经设置好了),并将结果返回给浏览器。由于container中大量的linux工具都没有,因此需要到主机上去编辑。app1
container将app
目录挂载进去了,因此在container中的创建的Django项目实际上在app
目录下也是可见的。安卓redis client
由于django app需要访问redis数据库,因此需要先安装redis client
123456root@7e6e49321993:/data/redisapp# pip install redisCollecting redisDownloading redis-2.10.5-py2.py3-none-any.whl (60kB)100% |████████████████████████████████| 61kB 123kB/sInstalling collected packages: redisSuccessfully installed redis-2.10.5编辑
views.py
文件1# vim app/redisapp/redisapp/views.py初始状态,
views.py
文件并不存在,添加并编辑,内容如下:123456789from django.http import HttpResponseimport socketimport redisdef query(request):r = redis.StrictRedis(host='redis', port=6379, db=0)value = r.get('redis_app')return HttpResponse('hello world from ' + socket.gethostname() + ', with value: ' + str(value))编辑
urls.py
文件1# vim app/redisapp/redisapp/views.py编辑内容如下,只要是添加
views.py
中定义的view函数跟url的对应:123456789from django.conf.urls import urlfrom django.contrib import adminfrom redisapp.views import *urlpatterns = [url(r'^admin/', admin.site.urls),url(r'^query/', query),]编辑
settings.py
文件1# vim app/redisapp/redisapp/settings.py主要是讲默认生成的
redisapp
添加到INSTALLED_APPS
中,添加完后应该如下:123456789INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','redisapp']初始化数据库
在启动django程序之前,必须初始化数据库。默认情况下,采用的sqlite数据库。
1234567891011121314151617root@2298dd360bbd:/data/redisapp# python manage.py migrateOperations to perform:Apply all migrations: admin, contenttypes, sessions, authRunning migrations:Rendering model states... DONEApplying contenttypes.0001_initial... OKApplying auth.0001_initial... OKApplying admin.0001_initial... OKApplying admin.0002_logentry_remove_auto_add... OKApplying contenttypes.0002_remove_content_type_name... OKApplying auth.0002_alter_permission_name_max_length... OKApplying auth.0003_alter_user_email_max_length... OKApplying auth.0004_alter_user_username_opts... OKApplying auth.0005_alter_user_last_login_null... OKApplying auth.0006_require_contenttypes_0002... OKApplying auth.0007_alter_validators_add_error_messages... OKApplying sessions.0001_initial... OK至此,app1已经全部配置并初始化完毕。
配置app2
app2与app1共享同一个Django项目,因此不需要为app2做任何特别的配置。
配置haproxy
haproxy依赖于它的配置文件,在配置文件中必须把app1跟app2都加进去,这样haproxy就可以为他们做load balance了。
1# vim haproxy/haproxy.cfg编辑内容如下:
12345678910111213141516171819202122232425262728293031323334globalmaxconn 50000daemonpidfile /var/run/haproxy.pidlog 127.0.0.1 local0nbproc 4#debugdefaultsmode httpbalance roundrobinmaxconn 25000option httplogoption abortoncloseoption httpcloseoption forwardforoption redispatchoption redispatchretries 3timeout client 30stimeout connect 30stimeout server 30slisten redis_proxybind :6301mode httpstats enablestats uri /statslog 127.0.0.1 local0 debugserver app1 app1:8001 check inter 2000 rise 2 fall 5server app2 app2:8002 check inter 2000 rise 2 fall 5如果需要调试,可以吧
global
中的debug
打开。特别注意的是关于app1跟app2的配置,必须跟他们启动的端口保持一致。因为在前面已经通过--link
将app
以及app2
连接进来,因此在写address:port
时候可以直接使用app1跟app2。
启动services
启动redis service
启动redis-master service
attach
redis-master
container1234# docker start redis-masterredis-master# docker attach redis-masterroot@3d187b223f56:/data#启动service
1234root@3d187b223f56:/data# redis-server redis.confroot@3d187b223f56:/data# redis-cli127.0.0.1:6379> get redis_app"Hello redis app!"
启动redis-slave1 service
attach
redis-slave1
container1234# docker start redis-slave1redis-slave1root@neil-centos1:hademo# docker attach redis-slave1root@cef73ae816be:/data# redis-cli启动service
1234root@cef73ae816be:/data# redis-server redis.confroot@cef73ae816be:/data# redis-cli127.0.0.1:6379> get redis_app"Hello redis app!"
启动redis-slave2 service
与启动redis-slave1 service一样,这里不再赘述。
启动app service
启动app1 service
attach
app1
container1234# docker start app1app1root@neil-centos1:hademo# docker attach app1root@7e6e49321993:/#启动service
12345678910root@7e6e49321993:/data# cd redisapp/root@7e6e49321993:/data# python manage.py runserver 0.0.0.0:8001Performing system checks...System check identified no issues (0 silenced).May 16, 2016 - 12:51:39Django version 1.9.6, using settings 'redisapp.settings'Starting development server at http://0.0.0.0:8001/Quit the server with CONTROL-C.
启动app2 service
attach
app2
container1234docker start app2app2# docker attach app2root@13d9f084c18b:/#启动service
12345678910root@13d9f084c18b:/data# cd redisapp/root@13d9f084c18b:/data/redisapp# python manage.py runserver 0.0.0.0:8002Performing system checks...System check identified no issues (0 silenced).May 16, 2016 - 12:56:08Django version 1.9.6, using settings 'redisapp.settings'Starting development server at http://0.0.0.0:8002/Quit the server with CONTROL-C.
启动haproxy service
attach
haproxy
container1234# docker start haproxyhaproxy# docker attach haproxyroot@7e0f805cd85c:/#启动service
123root@7e0f805cd85c:/data# haproxy -f haproxy.cfg[WARNING] 136/130034 (6) : Proxy 'redis_proxy': in multi-process mode, stats will be limited to process assigned to the current request.root@7e0f805cd85c:/data#
至此,所有的service已经成功启动。可以通过uri http://<host ip>:6031/query/
访问本案例中web service。还可以通过http://<host ip>:6031/stats
来查询haproxy的统计信息。
结语
本文中所涉及的配置以及代码可以在这个github repo找到: https://github.com/keysaim/demos/tree/master/docker-redis-django