首先介绍几个术语。
可见,一个项目可以由多个服务(容器)关联而成,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.htmlDockerfile
生成一个 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 200docker-compose.yml
编写 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 容器了,更为灵活。