用docker搭建全棧式應(yīng)用
簡(jiǎn)介
本文將以docker為基礎(chǔ),搭建一套簡(jiǎn)單的應(yīng)用案例医吊。本文將假定讀者已經(jīng)有一定的Docker應(yīng)用基礎(chǔ)钱慢,如懂得用docker pull
下載image,懂得用docker run
運(yùn)行一個(gè)container等卿堂。該案例的應(yīng)用架構(gòu)將采用redis的master-slave模式作為數(shù)據(jù)存儲(chǔ)束莫,以django為框架提供web訪問,以haproxy作為負(fù)載均衡草描。其架構(gòu)圖如下.
其中App1和App2屬于同一個(gè)Django的APP.
準(zhǔn)備工作
獲取相應(yīng)的docker images
- 可以直接從docker官方獲取images
$ docker pull django
$ docker pull redis
$ docker pull haproxy
- 獲取成功之后览绿,檢查一下images是否真的ok
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
haproxy latest 46bc5babcc18 3 days ago 139.1 MB
redis latest 84dbf5edc313 4 days ago 184.8 MB
django latest 3b0bc67346a2 9 days ago 433.5 MB
- 檢查一下各自的版本(root權(quán)限)
redis版本
# docker run -it redis /bin/bash
root@a3ca293915cb:/data# redis-server -v
Redis server v=3.2.0 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=5382f69a4e75566b
haproxy版本
# docker run -it haproxy /bin/bash
root@b11f07766c49:/# haproxy -v
HA-Proxy version 1.6.5 2016/05/10
Copyright 2000-2016 Willy Tarreau <willy@haproxy.org>
django版本
# docker run -it django /bin/bash
root@1daf8d846018:/# django-admin version
1.9.6
檢查完后,可以用docker rm
把不用的containers刪掉
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1daf8d846018 django "/bin/bash" 2 minutes ago Exited (0) 6 seconds ago sleepy_hypatia
b11f07766c49 haproxy "/docker-entrypoint.s" 3 minutes ago Exited (0) 2 minutes ago elegant_tesla
a3ca293915cb redis "docker-entrypoint.sh" 5 minutes ago Exited (0) 3 minutes ago stupefied_cray
# docker rm `docker ps -a | grep 'Exited' | awk '{print $1}'`
1daf8d846018
b11f07766c49
a3ca293915cb
從上穗慕,本文各版本采用如下:
service | version |
---|---|
haproxy | 1.6.5 |
django | 1.9.6 |
redis | 3.2.0 |
啟動(dòng)containers
本案例中containers的有關(guān)啟動(dòng)配置如下:
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 |
啟動(dòng)redis
-
啟動(dòng)redis-master
在本地當(dāng)前目錄下創(chuàng)建子目錄master
饿敲,并將master
掛載到container里的/data
目錄。container名字為redis-master
逛绵。為了方便調(diào)試诀蓉,這里同時(shí)啟動(dòng)交互式模式-it
,并同時(shí)打開bash
# mkdir master # docker run -it --name redis-master -v `pwd`/master:/data redis /bin/bash root@3d187b223f56:/data#
添加一個(gè)測(cè)試條目暑脆,取key為
redis_app
渠啤,該測(cè)試條目將被app1跟app2查詢:root@3d187b223f56:/data# redis-cli 127.0.0.1:6379> set redis_app "Hello redis app!" OK 127.0.0.1:6379> get redis_app "Hello redis app!"
-
啟動(dòng)redis-slave1
本文案例中將采用兩個(gè)redis的slave,這里首先啟動(dòng)第一個(gè)添吗。container名字為redis-slave1
沥曹,同時(shí)為了方便的連接到redis master,這里直接用--link
將redis-master
container連接同時(shí)命名為master
。其實(shí)際效果相當(dāng)于往/etc/hosts
添加一條記錄妓美,映射master
的域名到redis-master
的IP僵腺。通過這種方式,就不需要手工的添加master的IP壶栋,docker在啟動(dòng)的時(shí)候會(huì)自動(dòng)添加辰如。# docker run -it --name redis-slave1 --link redis-master:master -v `pwd`/slave1:/data redis /bin/bash root@cef73ae816be:/data# cat /etc/hosts 172.17.0.3 cef73ae816be 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 master 3d187b223f56 redis-master
可以看出,
--link
實(shí)際上在/etc/hosts
下添加了一條新的映射贵试,分別把redis-master
的別名master
琉兜、container ID以及container name映射到master的IP。172.17.0.2 master 3d187b223f56 redis-master
如果沒有錯(cuò)誤毙玻,那么上面在master中設(shè)置的條目應(yīng)該可以查詢到:
root@cef73ae816be:/data# redis-cli 127.0.0.1:6379> get redis_app "Hello redis app!"
-
啟動(dòng)redis-slave2
與redis-slave1同樣的方式啟動(dòng)redis-slave2# docker run -it --name redis-slave2 --link redis-master:master -v `pwd`/slave2:/data redis /bin/bash root@1f22e9e15e8b:/data# cat /etc/hosts 172.17.0.4 1f22e9e15e8b 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.2 master 3d187b223f56 redis-master
如果沒有錯(cuò)誤豌蟋,那么上面在master中設(shè)置的條目應(yīng)該可以查詢到:
root@cef73ae816be:/data# redis-cli 127.0.0.1:6379> get redis_app "Hello redis app!"
-
啟動(dòng)app1
app1需要連接到redis服務(wù),因此需要把redis-master
container連起來桑滩。同時(shí)為其創(chuàng)建app
目錄梧疲,由于app1跟app2共享同一個(gè)Django項(xiàng)目,這里就不再單獨(dú)為他們創(chuàng)建各自的目錄运准。# mkdir app # docker run -it --name app1 --link redis-master:redis -v `pwd`/app/:/data django /bin/bash root@7e6e49321993:/#
配置
配置redis
-
獲取redis初始配置
本文采用的版本為3.2.0, 可通過如下方式獲取
# wget https://raw.githubusercontent.com/antirez/redis/3.2.0/redis.conf
-
配置redis-master
拷貝
redis.conf
到master
目錄.# cp redis.conf master/
編輯改配置文件幌氮。默認(rèn)里面有非常多的配置選項(xiàng),這里只需要關(guān)注幾個(gè)胁澳,并改成如下:
bind 0.0.0.0 protected-mode yes daemonize yes logfile /var/log/redis.log
由于已經(jīng)將
./master
掛載到container的/data
目錄浩销,因此本地的改動(dòng)在container里面也是可見的。 -
配置redis-slave1
拷貝
redis.conf
到slave1
目錄.# cp redis.conf slave1/
編輯改配置文件听哭。默認(rèn)里面有非常多的配置選項(xiàng)慢洋,這里只需要關(guān)注幾個(gè),并改成如下:
bind 0.0.0.0 protected-mode yes daemonize yes logfile /var/log/redis.log slaveof master 6379
最后一項(xiàng)
slaveof
將slave1設(shè)置為master的slave陆盘。同樣普筹,由于已經(jīng)將./slave1
掛載到container的/data
目錄,因此本地的改動(dòng)在container里面也是可見的隘马。 -
配置redis-slave2
配置方式與
redis-slave1
一致太防,因此,直接拷貝slave1/redis.conf
到slave2
目錄.# cp slave1/redis.conf slave2/
配置Django App
-
配置app1
首先需要?jiǎng)?chuàng)建Django項(xiàng)目,這需要登錄到
app1
系統(tǒng)中才能夠調(diào)用Django命令來創(chuàng)建酸员。# docker attach app1 root@7e6e49321993:/# cd /data root@7e6e49321993:/# django-admin startproject redisapp root@4b8240acb25a:/data# ls redisapp root@4b8240acb25a:/data# cd redisapp/ root@4b8240acb25a:/data/redisapp# ls manage.py redisapp
接下來需要編輯Django代碼以支持本案例中一個(gè)簡(jiǎn)單的API:
/query/
蜒车,此API將獲取redis中的key為redis_app
的值(請(qǐng)注意,該值在上面啟動(dòng)redis的時(shí)候已經(jīng)設(shè)置好了)幔嗦,并將結(jié)果返回給瀏覽器酿愧。由于container中大量的linux工具都沒有,因此需要到主機(jī)上去編輯邀泉。app1
container將app
目錄掛載進(jìn)去了嬉挡,因此在container中的創(chuàng)建的Django項(xiàng)目實(shí)際上在app
目錄下也是可見的钝鸽。-
安卓redis client
由于django app需要訪問redis數(shù)據(jù)庫(kù),因此需要先安裝redis client
root@7e6e49321993:/data/redisapp# pip install redis Collecting redis Downloading redis-2.10.5-py2.py3-none-any.whl (60kB) 100% |████████████████████████████████| 61kB 123kB/s Installing collected packages: redis Successfully installed redis-2.10.5
-
編輯
views.py
文件# vim app/redisapp/redisapp/views.py
初始狀態(tài)庞钢,
views.py
文件并不存在拔恰,添加并編輯,內(nèi)容如下:from django.http import HttpResponse import socket import redis def 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
文件# vim app/redisapp/redisapp/views.py
編輯內(nèi)容如下基括,只要是添加
views.py
中定義的view函數(shù)跟url的對(duì)應(yīng):from django.conf.urls import url from django.contrib import admin from redisapp.views import * urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^query/', query), ]
-
編輯
settings.py
文件# vim app/redisapp/redisapp/settings.py
主要是講默認(rèn)生成的
redisapp
添加到INSTALLED_APPS
中颜懊,添加完后應(yīng)該如下:INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'redisapp' ]
-
初始化數(shù)據(jù)庫(kù)
在啟動(dòng)django程序之前,必須初始化數(shù)據(jù)庫(kù)风皿。默認(rèn)情況下河爹,采用的sqlite數(shù)據(jù)庫(kù)。
root@2298dd360bbd:/data/redisapp# python manage.py migrate Operations to perform: Apply all migrations: admin, contenttypes, sessions, auth Running migrations: Rendering model states... DONE Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying sessions.0001_initial... OK
至此揪阶,app1已經(jīng)全部配置并初始化完畢。
-
-
配置app2
app2與app1共享同一個(gè)Django項(xiàng)目患朱,因此不需要為app2做任何特別的配置鲁僚。
-
配置haproxy
haproxy依賴于它的配置文件,在配置文件中必須把a(bǔ)pp1跟app2都加進(jìn)去裁厅,這樣haproxy就可以為他們做load balance了冰沙。
# vim haproxy/haproxy.cfg
編輯內(nèi)容如下:
global maxconn 50000 daemon pidfile /var/run/haproxy.pid log 127.0.0.1 local0 nbproc 4 #debug defaults mode http balance roundrobin maxconn 25000 option httplog option abortonclose option httpclose option forwardfor option redispatch option redispatch retries 3 timeout client 30s timeout connect 30s timeout server 30s listen redis_proxy bind :6301 mode http stats enable stats uri /stats log 127.0.0.1 local0 debug server app1 app1:8001 check inter 2000 rise 2 fall 5 server app2 app2:8002 check inter 2000 rise 2 fall 5
如果需要調(diào)試,可以吧
global
中的debug
打開执虹。特別注意的是關(guān)于app1跟app2的配置拓挥,必須跟他們啟動(dòng)的端口保持一致。因?yàn)樵谇懊嬉呀?jīng)通過--link
將app
以及app2
連接進(jìn)來袋励,因此在寫address:port
時(shí)候可以直接使用app1跟app2侥啤。
啟動(dòng)services
啟動(dòng)redis service
-
啟動(dòng)redis-master service
-
attach
redis-master
container# docker start redis-master redis-master # docker attach redis-master root@3d187b223f56:/data#
-
啟動(dòng)service
root@3d187b223f56:/data# redis-server redis.conf root@3d187b223f56:/data# redis-cli 127.0.0.1:6379> get redis_app "Hello redis app!"
-
-
啟動(dòng)redis-slave1 service
-
attach
redis-slave1
container# docker start redis-slave1 redis-slave1 root@neil-centos1:hademo# docker attach redis-slave1 root@cef73ae816be:/data# redis-cli
-
啟動(dòng)service
root@cef73ae816be:/data# redis-server redis.conf root@cef73ae816be:/data# redis-cli 127.0.0.1:6379> get redis_app "Hello redis app!"
-
-
啟動(dòng)redis-slave2 service
與啟動(dòng)redis-slave1 service一樣,這里不再贅述茬故。
啟動(dòng)app service
-
啟動(dòng)app1 service
-
attach
app1
container# docker start app1 app1 root@neil-centos1:hademo# docker attach app1 root@7e6e49321993:/#
-
啟動(dòng)service
root@7e6e49321993:/data# cd redisapp/ root@7e6e49321993:/data# python manage.py runserver 0.0.0.0:8001 Performing system checks... System check identified no issues (0 silenced). May 16, 2016 - 12:51:39 Django version 1.9.6, using settings 'redisapp.settings' Starting development server at http://0.0.0.0:8001/ Quit the server with CONTROL-C.
-
-
啟動(dòng)app2 service
-
attach
app2
containerdocker start app2 app2 # docker attach app2 root@13d9f084c18b:/#
-
啟動(dòng)service
root@13d9f084c18b:/data# cd redisapp/ root@13d9f084c18b:/data/redisapp# python manage.py runserver 0.0.0.0:8002 Performing system checks... System check identified no issues (0 silenced). May 16, 2016 - 12:56:08 Django version 1.9.6, using settings 'redisapp.settings' Starting development server at http://0.0.0.0:8002/ Quit the server with CONTROL-C.
-
啟動(dòng)haproxy service
-
attach
haproxy
container# docker start haproxy haproxy # docker attach haproxy root@7e0f805cd85c:/#
-
啟動(dòng)service
root@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已經(jīng)成功啟動(dòng)』前牛可以通過uri http://<host ip>:6031/query/
訪問本案例中web service赁炎。還可以通過http://<host ip>:6031/stats
來查詢haproxy的統(tǒng)計(jì)信息。
結(jié)語
本文中所涉及的配置以及代碼可以在這個(gè)github repo找到: https://github.com/keysaim/demos/tree/master/docker-redis-django钾腺。此外徙垫,還可以訪問博主的個(gè)人博客:https://keysaim.github.io。