diff --git a/compose/usage.md b/compose/usage.md index 78621c4..7238c21 100644 --- a/compose/usage.md +++ b/compose/usage.md @@ -5,3 +5,203 @@ * 服务(service):一个应用容器,实际上可以运行多个相同镜像的实例。 * 项目(project):由一组关联的应用容器组成的一个完整业务单元。 + +可见,一个项目可以由多个服务(容器)关联而成,Compose 面向项目进行管理。 + +### 场景 +下面,我们创建一个经典的 Web 项目:一个 [Haproxy](www.haproxy.org),挂载三个 Web 容器。 + +创建一个 `compose-haproxy-web` 目录,作为项目工作目录,并在其中分别创建两个子目录:`haproxy` 和 `web`。 + +### Web 子目录 + +这里用 Python 程序来提供一个简单的 HTTP 服务,打印出访问者的 IP 和 实际的本地 IP。 + +#### index.py + +编写一个 `index.py` 作为服务器文件,代码为 +```sh +#!/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("
#"+ str(request[pair][1]) +": "+str(request[pair][0])+ " requests " + "from <"+guest+"> to WebServer <"+pair[1]+">
") + else: + file.write("#"+ str(request[pair][1]) +": "+str(request[pair][0])+ " requests " + "from <"+guest+"> to WebServer <"+pair[1]+">
") + file.write(" "); + 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.html` 文件,其内容会被 index.py 更新。 +```sh +$ touch index.html +``` + +#### Dockerfile +生成一个 Dockerfile,内容为 +```sh +FROM python:2.7 +WORKDIR /code +ADD . /code +EXPOSE 80 +CMD python index.py +``` + +### haproxy 目录 +在其中生成一个 `haproxy.cfg` 文件,内容为 +```sh +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 +编写 docker-compose.yml 文件,这个是 Compose 使用的主模板文件。内容十分简单,指定 3 个 web 容器,以及 1 个 haproxy 容器。 + +```sh +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 项目 +现在 compose-haproxy-web 目录长成下面的样子。 +```sh +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 容器了,更为灵活。