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., 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: --
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[:])
)[:])
def log_message(self, format, *args):
if len(args) < or "" not in args[]:
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]=[,ts]
else:
num = request[addr_pair][]+
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[] == host:
guest = "LOCAL: "+pair[]
else:
guest = pair[]
if (time_now-datetime.strptime(request[pair][],'%Y-%m-%d %H:%M:%S')).seconds < :
file.write("<p style=\"font-size:150%\" >#"+ str(request[pair][]) +": <font color=\"red\">"+str(request[pair][])+ "</font> requests " + "from <<font color=\"blue\">"+guest+"</font>> to WebServer <<font color=\"blue\">"+pair[]+"</font>></p>");
else:
file.write("<p style=\"font-size:150%\" >#"+ str(request[pair][]) +": <font color=\"maroon\">"+str(request[pair][])+ "</font> requests " + "from <<font color=\"navy\">"+guest+"</font>> to WebServer <<font color=\"navy\">"+pair[]+"</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) < and "0.0.0.0" or sys.argv[]
port = len(sys.argv) < and or int(sys.argv[])
HandlerClass.protocol_version = Protocol
httpd = ServerClass((addr, port), HandlerClass)
sa = httpd.socket.getsockname()
print "Serving HTTP on", sa[], "port", sa[], "..."
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
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:
stats enable
stats uri /
frontend balancer
bind 0.0.0.0:
mode http
default_backend web_backends
backend web_backends
mode http
option forwardfor
balance roundrobin
server weba weba: check
server webb webb: check
server webc webc: check
option httpchk GET /
http-check expect status
3》主目录下
docker-compose.yml
编写 docker-compose.yml 文件,这个是 Compose 使用的主模板文件。内容十分简单,指定 3 个 web 容器,以及 1 个 haproxy 容器
weba:
build: ./web
expose:
-
webb:
build: ./web
expose:
-
webc:
build: ./web
expose:
-
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:
- ""
- ""
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 / : 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 / : WORKDIR /code
---> Running in 869b8b9e9950
Removing intermediate container 869b8b9e9950
---> 37044ee1d056
Step / : ADD . /code
---> b669f2dcdda8
Step / : EXPOSE
---> Running in cbd702f39940
Removing intermediate container cbd702f39940
---> 794578c3e4ac
Step / : 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 / : FROM python:2.7
---> f67e752245d6
Step / : WORKDIR /code
---> Using cache
---> 37044ee1d056
Step / : ADD . /code
---> Using cache
---> b669f2dcdda8
Step / : EXPOSE
---> Using cache
---> 794578c3e4ac
Step / : 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 / : FROM python:2.7
---> f67e752245d6
Step / : WORKDIR /code
---> Using cache
---> 37044ee1d056
Step / : ADD . /code
---> Using cache
---> b669f2dcdda8
Step / : EXPOSE
---> Using cache
---> 794578c3e4ac
Step / : 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
webb_1 | if (time_now-datetime.strptime(request[pair][],'%Y-%m-%d %H:%M:%S')).seconds < :
webb_1 | ^
webb_1 | IndentationError: unindent does not match any outer indentation level
compose-haproxy-web_webb_1 exited with code
可以看见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 , in _handle_request_noblock
webb_1 | self.process_request(request, client_address)
webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line , in process_request
webb_1 | self.finish_request(request, client_address)
webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line , in finish_request
webb_1 | self.RequestHandlerClass(request, client_address, self)
webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line , in __init__
webb_1 | self.finish()
webb_1 | File "/usr/local/lib/python2.7/SocketServer.py", line , in finish
webb_1 | self.wfile.close()
webb_1 | File "/usr/local/lib/python2.7/socket.py", line , in close
webb_1 | self.flush()
webb_1 | File "/usr/local/lib/python2.7/socket.py", line , in flush
webb_1 | self._sock.sendall(view[write_offset:write_offset+buffer_size])
webb_1 | error: [Errno ] 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",,application)
httpd.serve_forever()
然后再运行:
userdeMacBook-Pro:web user$ docker-compose up --build
Building weba
Step / : FROM python:2.7
---> f67e752245d6
Step / : WORKDIR /code
---> Using cache
---> 37044ee1d056
Step / : ADD . /code
---> 70d6fe477513
Step / : EXPOSE
---> Running in 3069406cf7f7
Removing intermediate container 3069406cf7f7
---> b6c2f2e4566c
Step / : 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 / : FROM python:2.7
---> f67e752245d6
Step / : WORKDIR /code
---> Using cache
---> 37044ee1d056
Step / : ADD . /code
---> Using cache
---> 70d6fe477513
Step / : EXPOSE
---> Using cache
---> b6c2f2e4566c
Step / : CMD python index.py
---> Using cache
---> 9b10db8bc740
Successfully built 9b10db8bc740
Successfully tagged compose-haproxy-web_webb:latest
Building webc
Step / : FROM python:2.7
---> f67e752245d6
Step / : WORKDIR /code
---> Using cache
---> 37044ee1d056
Step / : ADD . /code
---> Using cache
---> 70d6fe477513
Step / : EXPOSE
---> Using cache
---> b6c2f2e4566c
Step / : 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 容器了,更为灵活。
Docker技术入门与实战 第二版-学习笔记-9-Docker Compose 项目-1-举例说明的更多相关文章
- Docker技术入门与实战 第二版-学习笔记-10-Docker Machine 项目-2-driver
1>使用的driver 1〉generic 使用带有SSH的现有VM/主机创建机器. 如果你使用的是机器不直接支持的provider,或者希望导入现有主机以允许Docker Machine进行管 ...
- Docker技术入门与实战 第二版-学习笔记-8-网络功能network-3-容器访问控制和自定义网桥
1)容器访问控制 容器的访问控制,主要通过 Linux 上的 iptables防火墙来进行管理和实现. iptables是 Linux 上默认的防火墙软件,在大部分发行版中都自带. 容器访问外部网络 ...
- Docker技术入门与实战 第二版-学习笔记-10-Docker Machine 项目-1-cli
Docker Machine 是 Docker 官方编排(Orchestration)项目之一,负责在多种平台上快速安装 Docker 环境 Docker Machine是一种工具,它允许你在虚拟主机 ...
- Docker技术入门与实战 第二版-学习笔记-7-数据管理(volume)
Docker 数据管理 为什么要进行数据管理呢?因为当我们在使用container时,可能会在里面创建一些数据或文件,但是当我们停掉或删除这个容器时,这些数据或文件也会同样被删除,这是我们并不想看见的 ...
- Docker技术入门与实战 第二版-学习笔记-5-容器-命令及限制内存与cpu资源
1.启动容器 启动容器有两种方式: 基于镜像新建一个容器并启动 将在终止状态(stopped)的容器重新启动 1)新建并启动——docker run 比如在启动ubuntu:14.04容器,并输出“H ...
- Docker技术入门与实战 第二版-学习笔记-3-Dockerfile 指令详解
前面已经讲解了FROM.RUN指令,还提及了COPY.ADD,接下来学习其他的指令 5.Dockerfile 指令详解 1> COPY 复制文件 格式: COPY <源路径> .. ...
- Docker技术入门与实战 第二版-学习笔记-8-网络功能network-1-单个host上的容器网络
Docker 中的网络功能介绍 Docker 允许通过外部访问容器或容器互联的方式来提供网络服务 1) 外部访问容器 容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -p或 -P参数 ...
- Docker技术入门与实战 第二版-学习笔记-6-仓库
仓库(Repository)是集中存放镜像的地方 一个容易混淆的概念是注册服务器(Registry). 实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像. ...
- Docker技术入门与实战 第二版-学习笔记-2-镜像构建
3.利用 commit 理解镜像构成 在之前的例子中,我们所使用的都是来自于 Docker Hub 的镜像. 直接使用这些镜像是可以满足一定的需求,而当这些镜像无法直接满足需求时,我们就需要定制这些镜 ...
随机推荐
- 【RabbitMQ】1、RabbitMQ的几种典型使用场景
RabbitMQ主页:https://www.rabbitmq.com/ AMQP AMQP协议是一个高级抽象层消息通信协议,RabbitMQ是AMQP协议的实现.它主要包括以下组件: 1.Serve ...
- python协程的简单了解
协程: 协程,又称微线程,纤程.英文名Coroutine. 可以在不同的函数间切换,而且切换的次数和时间都是由用户自己确定的. 协程的几种实现方式: (1)使用生成器yield实现. 如果不了解生成器 ...
- 排序算法(3)--Insert Sorting--插入排序[3]--Shell Sort--希尔排序
1.基本思想 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序:随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止. 2.实现原理 对于n ...
- js-ES6学习笔记-字符串的扩展
1.codePointAt方法是测试一个字符由两个字节还是由四个字节组成的最简单方法.codePointAt方法会正确返回32位的UTF-16字符的码点. function is32Bit(c) { ...
- 【java】开发中常用字符串方法
java字符串的功能可以说非常强大, 它的每一种方法也都很有用. java字符串中常用的有两种字符串类, 分别是String类和StringBuffer类. Sting类 String类的对象是不可变 ...
- Java基础笔记(2) 程序入口 关键字 标识符 常量 变量
提醒:关于那些和我一样新鸟来看资料的,能看懂多少看多少,看不懂的就是不重要,重要的你想我自己学习肯定要标注的,这些信息明白每个知识点实际作用就好了,其他的比如等会讲的常量内存,常量池这些都是我找的资料 ...
- show命令
数据库 show databases; 表 show tables; show tables in xxdb; show tables 'a*'; tblproperties show tblprop ...
- Expo大作战(十四)--expo中消息推送的实现
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
- UWP开发细节记录:加载图像文件到D2D位图和D3D纹理
在UWP中加载文件一般先创建 StorageFile 对象,然后调用StorageFile.OpenReadAsync 方法得到一个IRandomAccessStream 接口用来读取数据: Stor ...
- ubuntu下编译qt5
编译步骤参考: http://doc.qt.io/qt-5/linux.html 我们使用源代码和编译目录分离的编译方式, 这样避免编译主机系统和目标系统间的独立. 参考: Qt Configure ...