• Docker技术入门与实战 第二版-学习笔记-9-Docker Compose 项目-1-举例说明


    Docker Compose Docker 官方编排(Orchestration)项目之一,负责快速在集群中部署分布式应用

    Compose 通过一个配置文件来管理多个Docker容器,在配置文件中,所有的容器通过services来定义,然后使用docker-compose脚本来启动,停止和重启应用,和应用中的服务以及所有依赖服务的容器,非常适合组合使用多个容器进行开发的场景。

    通过第一部分中的介绍,我们知道使用一个 Dockerfile 模板文件,可以让用户很方便的定义一个单独的应用容器。

    然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。

    Compose 恰好满足了这样的需求。它允许用户通过一个单独的 模板文件(YAML格式)来定义一组相关联的应用容器为一个项目 (project)。

    Compose 中有两个重要的概念:

    • 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
    • 项目(project):由一组关联的应用容器组成的一个完整业务单元,在 文件中定义。

    可见,一个项目可以由多个服务(容器)关联而成,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 &lt<font color="blue">"+guest+"</font>&gt to WebServer &lt<font color="blue">"+pair[1]+"</font>&gt</p>");
                else:
                    file.write("<p style="font-size:150%" >#"+ str(request[pair][1]) +": <font color="maroon">"+str(request[pair][0])+ "</font> requests " + "from &lt<font color="navy">"+guest+"</font>&gt to WebServer &lt<font color="navy">"+pair[1]+"</font>&gt</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 的统计信息。

    当然,还可以使用 consuletcd 等实现服务发现,这样就可以避免手动指定后端的 web 容器了,更为灵活。

  • 相关阅读:
    面向对象下
    面向对象上
    将博客搬至CSDN
    矩阵的常用术语和操作
    python2.7 Unable to find vcvarsall.bat
    intellij创建maven web项目
    intellij 编译 springmvc+hibernate+spring+maven 找不到hbm.xml映射文件
    spring Thymeleaf 中文乱码
    visualstudiocode 调试electron
    android反编译工具总结
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/10124658.html
Copyright © 2020-2023  润新知