Docker Compose 是 Docker 官方編排(Orchestration)項目之一,負責快速在集羣中部署分佈式應用 html
Compose 經過一個配置文件來管理多個Docker容器,在配置文件中,全部的容器經過services來定義,而後使用docker-compose腳原本啓動,中止和重啓應用,和應用中的服務以及全部依賴服務的容器,很是適合組合使用多個容器進行開發的場景。python
經過第一部分中的介紹,咱們知道使用一個 Dockerfile 模板文件,可讓用戶很方便的定義一個單獨的應用容器。git
然而,在平常工做中,常常會碰到須要多個容器相互配合來完成某項任務的狀況。例如要實現一個 Web 項目,除了 Web 服務容器自己,每每還須要再加上後端的數據庫服務容器,甚至還包括負載均衡容器等。 github
Compose 剛好知足了這樣的需求。它容許用戶經過一個單獨的 模板文件(YAML格式)來定義一組相關聯的應用容器爲一個項目 (project)。 web
Compose 中有兩個重要的概念: docker
可見,一個項目能夠由多個服務(容器)關聯而成,Compose 的默認管理對象是項目,經過子命令對項目中的一組容器進行便捷地生命週期管理。 數據庫
Compose 項目由 Python 編寫,實現上調用了 Docker 服務提供的 API 來對容器進 行管理。所以,只要所操做的平臺支持 Docker API,就能夠在其上利用 Compose 來進行編排管理。 後端
以前也有查看過安裝好的docker-compose版本:瀏覽器
userdeMacBook-Pro:~ user$ docker-compose --version docker-compose version 1.23.2, build 1110ad01
安裝好後可使用docker-compose -h來查看其用法服務器
使用
下面就舉例說明,場景以下:
建立一個經典的 Web 項目:一個 Haproxy,掛載三個 Web 容器
首先建立一個compose-haproxy-web目錄,做爲項目工做目錄,並在其中分別建立兩個子目錄:haproxy和web
1》web 子目錄下的文件
這裏用 Python 程序來提供一個簡單的 HTTP 服務——打印出訪問者的 IP 和 實際的 本地 IP。
index.py
編寫一個 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.html文件,其內容會被 index.py 更新。
userdeMacBook-Pro:compose-haproxy-web user$ touch index.html
Dockerfile
生成一個 Dockerfile,內容爲:
FROM python:2.7 WORKDIR /code ADD . /code EXPOSE 80 CMD python index.py
2》haproxy 子目錄下
在其中生成一個 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 bind 0.0.0.0: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
3》主目錄下
docker-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"
4 》運行 compose 項目
如今 compose-haproxy-web 目錄長成下面的樣子。
compose-haproxy-web ├── docker-compose.yml ├── haproxy │ └── haproxy.cfg └── web ├── Dockerfile ├── index.html └── index.py
在該目錄下執行 docker-compose up命令,會整合輸出全部容器的輸出:
userdeMacBook-Pro:compose-haproxy-web user$ docker-compose up Building weba Step 1/5 : FROM python:2.7 2.7: Pulling from library/python 54f7e8ac135a: Pull complete d6341e30912f: Pull complete 087a57faf949: Pull complete 5d71636fb824: Pull complete 0c1db9598990: Pull complete 220bd9a491ba: Pull complete 97b15521fe5d: Pull complete 1b44c1054690: Pull complete 6b8b382a68d7: Pull complete Digest: sha256:1bb98a04d037d9766110499d36bf2f3a2aa43965b4aa345da91f6de75f3816d8 Status: Downloaded newer image for python:2.7 ---> f67e752245d6 Step 2/5 : WORKDIR /code ---> Running in 869b8b9e9950 Removing intermediate container 869b8b9e9950 ---> 37044ee1d056 Step 3/5 : ADD . /code ---> b669f2dcdda8 Step 4/5 : EXPOSE 80 ---> Running in cbd702f39940 Removing intermediate container cbd702f39940 ---> 794578c3e4ac Step 5/5 : CMD python index.py ---> Running in 65de9f4ac31d Removing intermediate container 65de9f4ac31d ---> 67764eb15cf9 Successfully built 67764eb15cf9 Successfully tagged compose-haproxy-web_weba:latest WARNING: Image for service weba was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Building webb Step 1/5 : FROM python:2.7 ---> f67e752245d6 Step 2/5 : WORKDIR /code ---> Using cache ---> 37044ee1d056 Step 3/5 : ADD . /code ---> Using cache ---> b669f2dcdda8 Step 4/5 : EXPOSE 80 ---> Using cache ---> 794578c3e4ac Step 5/5 : CMD python index.py ---> Using cache ---> 67764eb15cf9 Successfully built 67764eb15cf9 Successfully tagged compose-haproxy-web_webb:latest WARNING: Image for service webb was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Building webc Step 1/5 : FROM python:2.7 ---> f67e752245d6 Step 2/5 : WORKDIR /code ---> Using cache ---> 37044ee1d056 Step 3/5 : ADD . /code ---> Using cache ---> b669f2dcdda8 Step 4/5 : EXPOSE 80 ---> Using cache ---> 794578c3e4ac Step 5/5 : CMD python index.py ---> Using cache ---> 67764eb15cf9 Successfully built 67764eb15cf9 Successfully tagged compose-haproxy-web_webc:latest WARNING: Image for service webc was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Pulling haproxy (haproxy:latest)... latest: Pulling from library/haproxy a5a6f2f73cd8: Already exists 7746471d9b75: Pull complete 3149ba82c5fb: Pull complete Creating compose-haproxy-web_weba_1 ... done Creating compose-haproxy-web_webb_1 ... done Creating compose-haproxy-web_webc_1 ... done Creating compose-haproxy-web_haproxy_1 ... error ERROR: for compose-haproxy-web_haproxy_1 Cannot start service haproxy: b'Cannot link to a non running container: /compose-haproxy-web_webb_1 AS /compose-haproxy-web_haproxy_1/webb_1' ERROR: for haproxy Cannot start service haproxy: b'Cannot link to a non running container: /compose-haproxy-web_webb_1 AS /compose-haproxy-web_haproxy_1/webb_1' ERROR: Encountered errors while bringing up the project.
運行的過程當中出現了錯誤,即web服務的容器並無運行,致使haproxy服務要鏈接其時失敗
web服務的容器沒能成功運行的緣由是什麼,用下面的方法查看:
userdeMacBook-Pro:compose-haproxy-web user$ docker-compose up webb Starting compose-haproxy-web_webb_1 ... done Attaching to compose-haproxy-web_webb_1 webb_1 | File "index.py", line 46 webb_1 | if (time_now-datetime.strptime(request[pair][1],'%Y-%m-%d %H:%M:%S')).seconds < 3: webb_1 | ^ webb_1 | IndentationError: unindent does not match any outer indentation level compose-haproxy-web_webb_1 exited with code 1
能夠看見index.py代碼有錯,由於是複製過來的,因此縮進上可能會有點問題,解決該問題後,再運行:
userdeMacBook-Pro:compose-haproxy-web user$ docker-compose up --build .... Recreating compose-haproxy-web_weba_1 ... done Recreating compose-haproxy-web_webb_1 ... done Recreating compose-haproxy-web_webc_1 ... done Recreating compose-haproxy-web_haproxy_1 ... done Attaching to compose-haproxy-web_webc_1, compose-haproxy-web_webb_1, compose-haproxy-web_weba_1, compose-haproxy-web_haproxy_1 ....
注意:必定要添加--build參數,在開啓容器時構建鏡像
可是後面仍是有錯:
webb_1 | Traceback (most recent call last): webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line 290, in _handle_request_noblock webb_1 | self.process_request(request, client_address) webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line 318, in process_request webb_1 | self.finish_request(request, client_address) webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line 331, in finish_request webb_1 | self.RequestHandlerClass(request, client_address, self) webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line 654, in __init__ webb_1 | self.finish() webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line 713, in finish webb_1 | self.wfile.close() webb_1 | File "/usr/local/lib/python2.7/socket.py", line 283, in close webb_1 | self.flush() webb_1 | File "/usr/local/lib/python2.7/socket.py", line 307, in flush webb_1 | self._sock.sendall(view[write_offset:write_offset+buffer_size]) webb_1 | error: [Errno 32] Broken pipe
這是python2.X上的問題,爲了可以顯示一下效果,在網上找了個python3的http服務代碼(https://blog.csdn.net/aaa000830/article/details/79579579)替換上面的index.py:
#!/usr/bin/python3 from wsgiref.simple_server import make_server def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return ['<h1>Hello, web!</h1>'.encode()] httpd = make_server("127.0.0.1",80,application) httpd.serve_forever()
而後再運行:
userdeMacBook-Pro:web user$ docker-compose up --build Building weba Step 1/5 : FROM python:2.7 ---> f67e752245d6 Step 2/5 : WORKDIR /code ---> Using cache ---> 37044ee1d056 Step 3/5 : ADD . /code ---> 70d6fe477513 Step 4/5 : EXPOSE 80 ---> Running in 3069406cf7f7 Removing intermediate container 3069406cf7f7 ---> b6c2f2e4566c Step 5/5 : CMD python index.py ---> Running in f13845a5a7b8 Removing intermediate container f13845a5a7b8 ---> 9b10db8bc740 Successfully built 9b10db8bc740 Successfully tagged compose-haproxy-web_weba:latest Building webb Step 1/5 : FROM python:2.7 ---> f67e752245d6 Step 2/5 : WORKDIR /code ---> Using cache ---> 37044ee1d056 Step 3/5 : ADD . /code ---> Using cache ---> 70d6fe477513 Step 4/5 : EXPOSE 80 ---> Using cache ---> b6c2f2e4566c Step 5/5 : CMD python index.py ---> Using cache ---> 9b10db8bc740 Successfully built 9b10db8bc740 Successfully tagged compose-haproxy-web_webb:latest Building webc Step 1/5 : FROM python:2.7 ---> f67e752245d6 Step 2/5 : WORKDIR /code ---> Using cache ---> 37044ee1d056 Step 3/5 : ADD . /code ---> Using cache ---> 70d6fe477513 Step 4/5 : EXPOSE 80 ---> Using cache ---> b6c2f2e4566c Step 5/5 : CMD python index.py ---> Using cache ---> 9b10db8bc740 Successfully built 9b10db8bc740 Successfully tagged compose-haproxy-web_webc:latest Recreating compose-haproxy-web_webb_1 ... done Recreating compose-haproxy-web_weba_1 ... done Recreating compose-haproxy-web_webc_1 ... done Recreating compose-haproxy-web_haproxy_1 ... done Attaching to compose-haproxy-web_webb_1, compose-haproxy-web_weba_1, compose-haproxy-web_webc_1, compose-haproxy-web_haproxy_1
瀏覽器調用:
http://localhost:80
http://localhost:70
此時訪問本地的 80 端口,會通過 haproxy 自動轉發到後端的某個 web 容器上,刷新頁面,能夠觀察到訪問的容器地址的變化。
訪問本地 70 端口,能夠查看到 haproxy 的統計信息。
固然,還可使用 consul、etcd 等實現服務發現,這樣就能夠避免手動指定後端的 web 容器了,更爲靈活。