首先介绍几个术语。
可见,一个项目可以由多个服务(容器)关联而成,Compose 面向项目进行管理。
下面,我们创建一个经典的 Web 项目:一个 Haproxy,挂载三个 Web 容器。
创建一个 compose-haproxy-web
目录,作为项目工作目录,并在其中分别创建两个子目录:haproxy
和 web
。
这里用 Python 程序来提供一个简单的 HTTP 服务,打印出访问者的 IP 和 实际的本地 IP。
编写一个 index.py
作为服务器文件,代码为
#!/usr/bin/python |
#authors: yeasy.github.com |
#date: 2013-07-05 |
import sys |
import BaseHTTPServer |
from SimpleHTTPServer import SimpleHTTPRequestHandler |
import socket |
import fcntl |
import struct |
import pickle |
from datetime import datetime |
from collections import OrderedDict |
class HandlerClass(SimpleHTTPRequestHandler): |
def get_ip_address(self,ifname): |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
return socket.inet_ntoa(fcntl.ioctl( |
s.fileno(), |
0x8915, # SIOCGIFADDR |
struct.pack('256s', ifname[:15]) |
)[20:24]) |
def log_message(self, format, *args): |
if len(args) < 3 or "200" not in args[1]: |
return |
try: |
request = pickle.load(open("pickle_data.txt","r")) |
except: |
request=OrderedDict() |
time_now = datetime.now() |
ts = time_now.strftime('%Y-%m-%d %H:%M:%S') |
server = self.get_ip_address('eth0') |
host=self.address_string() |
addr_pair = (host,server) |
if addr_pair not in request: |
request[addr_pair]=[1,ts] |
else: |
num = request[addr_pair][0]+1 |
del request[addr_pair] |
request[addr_pair]=[num,ts] |
file=open("index.html", "w") |
file.write("<!DOCTYPE html> <html> <body><center><h1><font color=\"blue\" face=\"Georgia, Arial\" size=8><em>HA</em></font> Webpage Visit Results</h1></center>"); |
for pair in request: |
if pair[0] == host: |
guest = "LOCAL: "+pair[0] |
else: |
guest = pair[0] |
if (time_now-datetime.strptime(request[pair][1],'%Y-%m-%d %H:%M:%S')).seconds < 3: |
file.write("<p style=\"font-size:150%\" >#"+ str(request[pair][1]) +": <font color=\"red\">"+str(request[pair][0])+ "</font> requests " + "from <<font color=\"blue\">"+guest+"</font>> to WebServer <<font color=\"blue\">"+pair[1]+"</font>></p>") |
else: |
file.write("<p style=\"font-size:150%\" >#"+ str(request[pair][1]) +": <font color=\"maroon\">"+str(request[pair][0])+ "</font> requests " + "from <<font color=\"navy\">"+guest+"</font>> to WebServer <<font color=\"navy\">"+pair[1]+"</font>></p>") |
file.write("</body> </html>"); |
file.close() |
pickle.dump(request,open("pickle_data.txt","w")) |
if __name__ == '__main__': |
try: |
ServerClass = BaseHTTPServer.HTTPServer |
Protocol = "HTTP/1.0" |
addr = len(sys.argv) < 2 and "0.0.0.0" or sys.argv[1] |
port = len(sys.argv) < 3 and 80 or int(sys.argv[2]) |
HandlerClass.protocol_version = Protocol |
httpd = ServerClass((addr, port), HandlerClass) |
sa = httpd.socket.getsockname() |
print "Serving HTTP on", sa[0], "port", sa[1], "..." |
httpd.serve_forever() |
except: |
exit() |
生成一个临时的 index.html
文件,其内容会被 index.py 更新。
$ touch index.html |
生成一个 Dockerfile,内容为
FROM python:2.7 |
WORKDIR /code |
ADD . /code |
EXPOSE 80 |
CMD python index.py |
在其中生成一个 haproxy.cfg
文件,内容为
global |
log 127.0.0.1 local0 |
log 127.0.0.1 local1 notice |
defaults |
log global |
mode http |
option httplog |
option dontlognull |
timeout connect 5000ms |
timeout client 50000ms |
timeout server 50000ms |
listen stats :70 |
stats enable |
stats uri / |
frontend balancer |
bind 0.0.0.0:80 |
mode http |
default_backend web_backends |
backend web_backends |
mode http |
option forwardfor |
balance roundrobin |
server weba weba:80 check |
server webb webb:80 check |
server webc webc:80 check |
option httpchk GET / |
http-check expect status 200 |
编写 docker-compose.yml 文件,这个是 Compose 使用的主模板文件。内容十分简单,指定 3 个 web 容器,以及 1 个 haproxy 容器。
weba: |
build: ./web |
expose: |
- 80 |
webb: |
build: ./web |
expose: |
- 80 |
webc: |
build: ./web |
expose: |
- 80 |
haproxy: |
image: haproxy:latest |
volumes: |
- haproxy:/haproxy-override |
- haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro |
links: |
- weba |
- webb |
- webc |
ports: |
- "80:80" |
- "70:70" |
expose: |
- "80" |
- "70" |
现在 compose-haproxy-web 目录长成下面的样子。
compose-haproxy-web |
├── docker-compose.yml |
├── haproxy |
│ └── haproxy.cfg |
└── web |
├── Dockerfile |
├── index.html |
└── index.py |
docker-compose up
命令,会整合输出所有容器的输出。
$sudo docker-compose up |
Recreating composehaproxyweb_webb_1... |
Recreating composehaproxyweb_webc_1... |
Recreating composehaproxyweb_weba_1... |
Recreating composehaproxyweb_haproxy_1... |
Attaching to composehaproxyweb_webb_1, composehaproxyweb_webc_1, composehaproxyweb_weba_1, composehaproxyweb_haproxy_1 |
此时访问本地的 80 端口,会经过 haproxy 自动转发到后端的某个 web 容器上,刷新页面,可以观察到访问的容器地址的变化。
访问本地 70 端口,可以查看到 haproxy 的统计信息。
当然,还可以使用 consul、etcd 等实现服务发现,这样就可以避免手动指定后端的 web 容器了,更为灵活。