docker——三剑客之Docker Compose

编排(Orchestration)功能是复杂系统实现灵活可操做性的关键。
特别是在Docker应用场景中,编排意味着用户能够灵活的对各类容器资源实现定义和管理。

做为Docker官方编排工具,Compose的重要性不言而喻,
它可让用户经过编写一个简单模板文件快速地建立和管理基于Docker容器地应用集群

Compose项目是Docker官方的开源项目,负责实现对Docker容器的快速编排。从功能上看,跟OpenStack中的Heat十分类似。
Compose定位是“定义和运行多个Docker容器的应用”,其前身是开源项目Fig,目前仍然兼容Fig格式的模板文件。

咱们知道使用一个DockerFile模板文件,可让用户很方便的定义一个单独的应用容器
若是须要定义多个容器就须要容器编排,那么就可使用Compose了。
Compose容许用户经过一个单独的docker-compose.yml模板文件(YAML格式)来定义一组相关联的应用容器为一个项目

Compose中有两个重要的概念:
  服务(service):一个应用的容器,实际上能够包括若干个运行相同镜像的容器实例
  项目(project)由一组关联的应用容器组成的一个完整的业务单元,在docker-compose.yml文件中定义。

Compose的默认管理对象是项目,经过子命令对项目中的一组容器进行便捷地生命周期管理。
Compose项目由Python编写,实现上调用了Docker服务提供地API来对容器进行地管理。
所以,只要所操做地平台支持Docker API,就能够在其上利用Compose来进行编排管理。php

 

1、安装与卸载

  (1)pip安装   

pip install -U docker-compose

(2)二进制包安装

curl -L https://github.com/docker/compose/releases/download/1.23.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

(3)在容器中执行

官方提供了安装脚本html

curl -L https://github.com/docker/compose/releases/download/1.23.0/run.sh > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

(4)卸载

  若是是二进制包安装,将包删除就能够了,若是是pip安装,使用pip uninstallpython

 

2、命令与说明

对于Compose来讲,大部分命令地对象既能够是项目自己,也能够指定为项目中地服务或者容器。
若是没有特别说明,命令对象将是项目,这意味着项目中全部的服务都会受到命令地影响。
命令格式:
  docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
options选项:mysql

Compose选项:nginx

命令使用说明:git

(1)build

构建或重建项目中的服务器。
格式:
  build [options] [--build-arg key=val...] [SERVICE...]
说明:
  --compress 使用gzip压缩构陷上下文
  --force-rm 删除构建过程当中的临时容器
  --no-cache 构建镜像过程当中不适用缓存,不然会加长构建过程
  --pull 始终尝试拉取较新版本的镜像
  --m, --memory MEM 设置构建容器的内存限制
  --build-arg key=val 设置服务构建时的变量
  --parallel 并行构建镜像github

(2)bundle

从Compose中生成Docker包
格式:
  bundle [options]
说明:
  --push-images 自动推送任何服务的镜像
  -o, --output PATH 设置文件的写入路径web

(3)config

验证并查看Compose文件
格式:
  config [options]
说明:
  --resolve-image-digests 将镜像标签标记为摘要
  -q, --quiet 仅验证配置,不打印任何东西
  --services 打印服务名,每行一个
  --volumes 打印卷名,每行一个
  --hash="*" 打印服务配置的哈希redis

(4)create

为服务建立容器,不推荐使用此命令。推荐up --no-start
格式:
  create [options] [SERVICE...]
说明:
  --force-recreate 从新配置容器,即便它们的配置和镜像没有改变
  --no-recreate 若是容器已经存在,不要从新建立
  --no-build 不构建镜像,即便丢失了
  --build 在建立容器以前构建镜像sql

(5)down

中止并删除容器以及网络、镜像和卷
格式:
  down [options]
说明:
  --rmi type 删除镜像,type是all或者local
  -v, --volumes 删除volumes中声明的命名卷
  --remove-orphans 删除没有在服务中定义的容器
  -t, --timeout TIMEOUT 以秒为单位指定关闭超时

(6)events

从容器接受实时事件
格式:
  events [options] [SERVICE...]
说明:
  --json 将事件做为json对象流输出

(7)images

列出建立容器所使用的镜像
格式:
  images [options] [SERVICE...]
说明:
  -q, --quiet 仅输出镜像的id

(8)kill

强制中止一个容器的服务
格式:
  kill [options] [SERVICE...]
说明:
  -s SIGNAL 发送SIGNAL信号,默认发送的是SIGKILL

(9)logs

查看容器的输出
格式:
  logs [options] [SERVICE...]
说明:
  --no-color 单色输出
  -f, --follow 遵循日志输出
  -t, --timestamps 显示时间戳
  --tail="all" 每一个容器的日志末尾显示行数

(10)port

打印某个容器端口所映射的公共端口
格式:
  port [options] SERVICE PRIVATE_PORT
说明:
  --protocol=proto 协议的类型,tcp或udp
  --index=index 若是有多个容器,则为容器的索引值,默认为1

(11)ps

列出项目中的全部容器
格式:
  ps [options] [SERVICE...]
说明:
  -q, --quiet 仅显示ID
  --services 显示服务
  --filter KEY=VAL 经过一个键值对过滤

(12)pull

为compose文件中定义的服务拉取镜像,但不启动容器。
格式:
  Usage: pull [options] [SERVICE...]
说明:
  --ignore-pull-failures 忽略拉取过程当中的错误
  --parallel 并行拉出多个镜像
  --no-parallel 禁用并行
  -q, --quiet 拉出而不打印进度信息
  --include-deps 拉取服务的声明做为依赖项

(13)rm

删除全部中止状态的服务容器
说明:
  -f,--force 强制直接删除,包括非中止状态的容器
  -v 删除容器所挂载的数据卷

(14)run

在指定服务上执行一个命令。
默认状况下,若是存在关联,则关联的服务也将会被自动启动,除非这些服务已经载运行中。
若是不但愿自动启动关联容器,可使用--no-deps选项。
格式:
  run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] SERVICE [COMMAND] [ARGS...]
说明:
  -d 在后台运行服务容器
  --name NAME 为容器指定一个名字
  --entrypoint CMD覆盖默认的容器启动指令
  -e KEY=VAL 设置环境变量,可屡次使用选项来设置多个环境变量
  -u, --user=“” 指定运行容器的用户名或者Uid
  --no-deps 不自动启动关联的服务容器
  --rm 运行命令后自动删除容器,d模式下忽略
  -p, --publish=[] 映射容器端口到本地主机
  --service-ports 配置服务端口,并映射到本地主机
  -T 不分配伪tty,意味着依赖tty的指令将没法运行

(15)scale

设置服务运行个容器个数
格式:
  scale [options] [SERVICE=NUM...]
说明:
  -t, --timeout TIMEOUT中止容器时候的超时,默认10秒

(16)up

该命令十分强大,它将尝试自动完成包括构建镜像,从新建立服务,启动服务,并关联服务相关容器的一系列操做。
连接的服务都将会被自动启动,除非已经处于运行状态。
若是容器已经存在,up将尝试将容器中止,而后建立。
格式:
  up [options] [--scale SERVICE=NUM...] [SERVICE...]
说明:
  -d 在后台运行服务器
  --no-color 单色输出
  --no-deps 不启动服务所关联的容器
  --force-recreate 强制从新建立容器
  --no-recreate 若是容器已经存在了,则不从新建立
  --no-build 不自动构建缺失的服务镜像
  -t, --timeout TIMEOUT 中止容器时候的超时

 

3、环境变量

环境变量能够用来配置Compose的行为:

 

4、模板文件

模板文件是使用Compose的核心,默认的模板文件名称为docker-compose.yml,格式为YAML格式。
每一个服务都必须经过image指定镜像或build(须要Dockerfile)来自动构成生成镜像。

先来看一个基本示例:

version: '2'
services:  
	web:    
	image: dockercloud/hello-world    
		ports:      
			- 8080    
		networks:      
			- front-tier      
			- back-tier  
	redis:    
		image: redis    
		links:      
			- web    
		networks:      
			- back-tier  
	lb:    
		image: dockercloud/haproxy    
		ports:      
			- 80:80    
		links:      
			- web    
		networks:      
			- front-tier      
			- back-tier    
		volumes:      
			- /var/run/docker.sock:/var/run/docker.sock 
networks:  
	front-tier:    
		driver: bridge  
	back-tier:
driver: bridge

能够看到一份标准配置文件应该包含version、service、network三大部分。
其中最关键的就是services和networks两部分。

下面是具体命令的书写规则:

(1)image

services:  
	web:    
	  image: dockercloud/hello-world  

在services标签下的第二个标签是web,这个名字是用户本身自定义,它就是服务器名称。
image则是指定服务的镜像名称。若是镜像在本地不存在,Compose将会尝试拉取这个镜像。
下面这些格式都是可行的:

image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd

(2)build

服务除了能够基于指定的镜像,还能够基于以分Dockerfile,在使用up启动之时执行构建任务。
这个构建标签就是build,它能够指定Dockerfile所在文件夹的路径。
Compose将会利用它自动构建这个镜像,而后利用这个镜像启动服务容器。

build: /path/to/build/dir

也能够时相对路径

build: ./dir

设定上下文根目录,而后以该目录为准指定Dockerfile。

build:
  context: ../
  dockerfile: path/of/Dockerfile

注意build都是一个目录,若是你要指定Dockerfile文件,须要在build标签的子级标签中使用dockerfile标签指定。
若是你同时指定了image和build两个标签,那么Compose会构建镜像而且把镜像命名为image后面的那个名字。

build: ./dir
image: webapp:tag

既然能够在docker-compose.yml中定义构建任务,那么必定少不了arg这个标签,
就像Dockerfile中的ARG指令,它能够在构建过程当中指定环境变量,可是在构建成功后取消。
在docker-compose.yml文件中也支持这样的写法:

build:
  context: .
  args:
    buildno: 1
    password: secret

下面这种写法也是支持的,通常来讲下面的写法更适合阅读。

build:
  context: .
  args:
    - buildno=1
    - password=secret

与ENV不一样的是,ARG是容许空指的,例如:

args:
  - buildno
  - password

这样构建过程能够向它们赋值。

注意:YAML的布尔值(true、false、yes、no、on、off)必需要使用引号引发来,不然会当成字符串解析。

(3)command

使用command能够覆盖容器启动后默认执行的命令。

command: bundle exec thin -p 3000

也能够写成相似Dockerfile中的格式

command: [bundle, exec, thin, -p, 3000]

(4)container_name

前面说过Compose的容器名称格式是:<项目名称><服务名称><序号>
虽然能够自定义项目名称、服务名称,可是若是你想彻底控制容器的命名,可使用下面标签指定:

container_name: app

这样容器的名字就指定为app了。

注意:指定容器名称以后,该服务将没法进行扩展,由于Docker不容许多个容器具备相同的名称。

(5)depends_on

在使用Compose时,最大的好处就是少打启动命令,但通常项目容器启动的顺序是有要求的。
若是直接从上到下启动容器,必然会由于容器依赖问题而启动失败。
例如咱们在没有启动数据库容器的时候启动了应用容器,这时候应用容器会应为找不到数据库而退出。
为了不这种状况咱们须要加入一个标签,就是depend_on,这个标签解决了容器的依赖、启动前后的问题。
例以下面容器会先启动redis和db两个服务,最后才启动web服务:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

注意的是,默认状况下使用docker-compose up web这样的方式启动web服务时,也会启动redis和db两个服务。由于配置文件中定义了依赖关系。

(6)dns

和--dns参数同样用途,格式以下:

dns: 8.8.8.8

也能够是一个列表:

dns:
  - 8.8.8.8
  - 9.9.9.9

此外dns_search的配置也相似:

dns_search: example.com
dns_search:
  - dc1.example.com
  - dc2.example.com

(7)tmpfs

挂载临时目录到容器内部,与run的参数同样效果:

tmpfs: /run
tmpfs:
  - /run
  - /tmp

(8)entrypoint

在Dockerfile中有一个指令ENTRYPOINT指令,用于指定接入点。
在docker-compose.yml中能够定义接入点,覆盖Dockerfile中的定义:

entrypoint: /code/entrypoint.sh

格式和Docker相似,不过还能够写成这样:

entrypoint:    
	php    
	- -d    
	- zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so    
	- -d    - memory_limit=-1    
	- vendor/bin/phpunit

(9)env_file

在docker-compose.yml中能够定义一个专门存放变量的文件。
若是经过docker-compose -f FILE指定配置文件,则env_file中路径会使用配置文件路径。
若是有变量名称与environment指令冲突,则之后者为准。格式以下:

env_file: .env

或者根据docker-compose.yml设置多个:

env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

注意的是这里所说的环境变量是对宿主机的Compose而言,若是在配置文件中有build操做,
这些变量并不会进入构建过程,若是要在构建中使用变量仍是首选前面所说的arg标签。
环境变量中的每一行必须符合格式,支持#开头的注释行。

(10)environment

与上面的env_file标签彻底不一样,这个标签的做用是设置镜像变量,
它能够保存变量到镜像里面,也就是说启动的容器也会包含这些变量设置,这是与arg最大的不一样。
通常arg标签的变量仅用在构建过程当中。
而enviroment和Dockerfile中ENV指令同样会把变量一直保存在镜像、容器中,相似docker run -e的效果。

environment:
  RACK_ENV: development
  SHOW: 'true'
  SESSION_SECRET:

environment:
  - RACK_ENV=development
  - SHOW=true
  - SESSION_SECRET

(11)expose

这个标签进和Dockerfile中EXPOSE指令同样,用于指定暴露的端口,
可是只是做为一种参考,实际上docker-compose.yml的端口映射还得ports这样的标签。

expose:
 - "3000"
 - "8000"

(12)external_links

在使用Docker过程当中,咱们会有许多单独使用docker run启动的容器,
为了使Compose可以链接这些不在docker-compose.yml中定义的容器,
咱们须要一个特殊的标签,也就是external_links,它可让Compose项目里面的容器链接到那些项目配置外部的容器。
前提是外部容器中必须至少有一个容器是链接到项目内的服务的同一个网络里面。

external_links:
 - redis_1
 - project_db_1:mysql
 - project_db_1:postgresql

(13)extra_hosts

添加主机名的标签,就是往/etc/hosts文件中添加一些记录,与Docker client的--add-host相似:

extra_hosts:
 - "somehost:162.242.195.82"
 - "otherhost:50.31.209.229"

启动以后查看容器内部hosts:

162.242.195.82 somehost
50.31.209.229 otherhost

(14)labels

向容器添加元数据,和Dockerfile的LABEL指令一个意思,格式以下:

labels:
  com.example.description: "Accounting webapp"
  com.example.department: "Finance"
  com.example.label-with-empty-value: ""
labels:
  - "com.example.description=Accounting webapp"
  - "com.example.department=Finance"
  - "com.example.label-with-empty-value"

(15)links

前面讲过depends_on,那个标签解决启动顺序问题,这个标签解决的是容器的链接问题。
与Docker client的--link同样效果,会链接到其它服务中的容器。
格式以下:

links:
 - db
 - db:database
 - redis

使用的别名将会自动在服务容器中的/etc/hosts里建立。例如:

172.12.2.186  db
172.12.2.186  database
172.12.2.187  redis

相应的环境变量也将被建立。

(16)logging

这个标签用于配置日志服务。格式以下:

logging:
  driver: syslog
  options:
    syslog-address: "tcp://192.168.0.42:123"

默认的driver是json-file。只有json-file和journald能够经过docker-compose logs显示日志。
其它方式有其它的查看方式,但目前Compose还不支持。对于可选值可使用options指定。
详细信息:https://docs.docker.com/engine/admin/logging/overview/

(17)pid

将PID模式设置为主机PID模式,跟主机系统共享进程命名空间。
容器使用这个标签将可以访问和操做其它容器和宿主机的名称空间。

pid: "host"

(18)ports

映射端口的标签。
使用HOST.CONTAINER格式或者指定容器的端口,宿主机会随机映射端口。

ports:
 - "3000"
 - "8000:8000"
 - "49100:22"
 - "127.0.0.1:8001:8001"

注意:当使用HOST>CONTAINER格式来映射端口时,若是你使用的容器端口小于60可能会获得错误的结果。
由于YAML将会解析xxyy这种数字格式为60进制。因此建议采用字符串格式。

(19)security_opt

为一个容器覆盖默认的标签。简单说来就是管理所有服务的标签。
好比设置所有服务的user标签值为USER。

security_opt:
  - label:user:USER
  - label:role:ROLE

(20)stop_signal

设置一个信号来中止容器。在默认状况下是使用的是SIGTERM中止容器。
设置另外一个信号可使用stop_signal标签。

stop_signal: SIGUSR1

(21)volumes

挂载一个目录或者一个已存在的数据卷容器,能够直接使用[HOST:CONTAINER]这样的格式,
或者使用[HOST:CONTAINER:ro]这样的格式,后者对于容器来讲,数据卷是只读的,这样能够有效保护宿主机的文件系统。
Compose的数据卷指定路径能够是相对路径,使用.或者..来指定相对路径。
数据卷的格式能够是下面多种形式:

volumes:
  // 只是指定一个路径,Docker 会自动在建立一个数据卷(这个路径是容器内部的)。
  - /var/lib/mysql

  // 使用绝对路径挂载数据卷
  - /opt/data:/var/lib/mysql
  
  // 以 Compose 配置文件为中心的相对路径做为数据卷挂载到容器。
  - ./cache:/tmp/cache
  
  // 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。
  - ~/configs:/etc/configs/:ro
  
  // 已经存在的命名的数据卷。
  - datavolume:/var/lib/mysql

若是你不使用宿主机的路径,你能够指定一个volume_driver.

volume_driver: mydriver

(22)volumes_from

从其余容器或者服务挂载数据卷,可选参数是:ro或:rw,
前者表示容器只读,后者表示容器对数据卷是可读可写的。默认状况下是可读可写的。

volumes_from:
  - service_name
  - service_name:ro
  - container:container_name
  - container:container_name:rw

(23)cap_add,cap_drop

添加或删除容器的内核功能

cap_add:
  - ALL  让容器拥有全部能力

cap_drop:  去掉某些能力
  - NET_ADMIN
  - SYS_ADMIN

(24)cgroup_parent

指定一个容器的父级cgroup

cgroup_parent: m-executor-abcd

(25)devices

设备映射列表。与Docker client的--device参数相似。

devices:
  - "/dev/ttyUSB0:/dev/ttyUSB0"  

(26)extends

这个标签能够扩展另外一个服务,扩展内容能够是来自当前文件,也能够是来自其它文件,
相同服务的状况下,后来者会有选择的覆盖原有配置。

extends:
  file: common.yml
  service: webapp

用户能够在任何地方使用这个标签,只要标签内容包含file和service两个值就能够了。
file的值能够是相对或者绝对路径,若是不指定file的值,那么Compose会读取当前YML文件的信息。
注意避免循环依赖。

(27)network_mode

网络模式,与Docker client的--net参数相似,只是相对多了一个service:[service name]的格式。

network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"

能够指定使用服务或者容器的网络。

(28)networks

加入指定网络。

services:
  some-service:
    networks:
     - some-network
     - other-network

关于这个标签还有一个特别的子标签aliases,这是一个用来设置服务别名的标签:

services:
  some-service:
    networks:
      some-network:
        aliases:
         - alias1
         - alias3
      other-network:
        aliases:
         - alias2

相同的服务能够在不一样的网络有不一样的别名。

(29)ulimits

指定容器的ulimits限制值。
例如,指定最大进程数为65535,指定文件句柄数为20000(软限制,应用能够随时修改,不能超过硬限制)和40000(系统硬限制,只能root用户提升)

ulimits:
	nproc: 65535
	nofile:
		soft: 20000
		hard: 40000

(30)其它

此外,还有包括cpu_shares、cpuset、domainname、hostname、ipc、mac_address、mem_limit、memswap_limit、
privileged、read_only、restart、stdin_open、tty、user、working_dir等指令。
指定使用CPU核0和核1,只用50%的CPU资源:

cpu_shares: 73
cpuset: 0,1

指定容器中运行应用的用户名:

user: nginx

指定容器中工做目录:

working_dir: /code

指定容器中搜索域名、主机名、mac地址等:

domainname: your_website.com
hostname: test
mac_address: 08-00-27-00-0C-0A

指定容器:

ipc: host

指定容器中内存和内存减缓去限制都为1G:

mem_limit: 1g
menswap_limit: 1g

容许容器运行一些特权命令:

privileged: true

指定容器退出后的重启策略为始终重启。该命令对保持服务始终运行十分有效,推荐配置为always或者unless-stopped

restart: always

以只读模式挂载容器的root文件系统,意味着不能对容器内容进行修改:

read_only: true

打开标准输入,能够接受外部输入:

stdin_open: true

模拟一个假的远程控制台:

tty: true

(31)读取环境变量

从1.5.0版本开始,Compose模板文件支持动态读取主机的系统环境变量。
例如:下面的Compose文件将从运行它的环境中读取变量${MONGO_VERSION}的值,并将其写入执行的指令中。

db:
	images: “mongo:${MONGO_VERSION}”

  

 

5、应用实例

1.nginx服务启动

dvc:
    image: centos
    volumes:
        - /root/composetest:/usr/share/nginx/html/:ro

nginx:
    image: nginx
    volumes_from:
        - dvc
    ports:
        - "8081:80"

  

2.web负载均衡

建立一个web项目:将Haproxy做为负载均衡器,后端挂载三个web容器。

首相建立一个haproxy_web目录做为项目的工做目录,其中分别建立两个子目录:web和haproxy。

[root@centos002 haproxy_web]# ls
docker-compose.yml  haproxy  web

[root@centos002 haproxy_web]# tree .
.
|-- docker-compose.yml
|-- haproxy
|   `-- haproxy.cfg
`-- web
    |-- Dockerfile
    |-- index.html
    `-- index.py

2 directories, 5 files

(1)web子目录

index.py

#!/usr/bin/python

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>");
        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("</center></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.py里面说的什么咱们不须要去深究,只须要知道用来响应HTTP的请求,返回结果就好了。
同时它还会像index.html的文件中写入标签元素。

index.html

  这是一个空的文件,它的内容由index.py动态写入。

Dockerfile

FROM python:2.7
WORKDIR /code
ADD . /code
EXPOSE 80
CMD python index.py

咱们知道Dockerfile用来建立一个容器。
这里会建立一个python2.7的容器,同时还会执行index.py文件

(2)hpproxy子目录

该目录将配置haproxy镜像,这里面的配置实际上是haproxy的配置文件,咱们将会利用这个配置文件来构建咱们想要的镜像。

global
  log 127.0.0.1 local0
  log 127.0.0.1 local1 notice
  maxconn 4096

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
  mode http
  stats enable
  stats hide-version
  stats scope .
  stats realm Haproxy\ Statistics
  stats uri /
  stats auth user:pass

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)dockerfile-compose.yml

这个文件Compose使用的主模板文件。
其中会指定启动3个web容器(weba、webb、webc),以及一个haproxy容器。

#建立三个web服务
weba: 
    build: ./web  指定Dockerfile文件所在的路径,Compose将会使用这个文件来构建镜像,而后利用这个镜像来建立容器
    expose:
        - 80

webb:
    build: ./web
    expose:
        - 80

webc:
    build: ./web
    expose:
        - 80

haproxy:
    image: haproxy:1.6  拉取镜像
    volumes:  挂载配置文件到指定的位置
        - ./haproxy:/haproxy-override
        - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    links:  links标签解决容器的链接问题,它会链接到另外的三个容器,以此来达到编排的目的。
        - weba
        - webb
        - webc
    ports:
        - "80:80"  端口映射,可使用docker ps来查看
        - "70:70"

(4)运行compose项目

启动:
  docker-compose up 会自动查找当前目录下的docker-compose.yml文件,若是没有会去上级目录查找
查看容器信息:

[root@centos002 haproxy_web]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                    NAMES
9eac212b5a4d        haproxy:1.6         "/docker-entrypoint.…"   44 minutes ago      Up 44 minutes       0.0.0.0:70->70/tcp, 0.0.0.0:80->80/tcp   haproxy_web_haproxy_1_8843fbe31d1f
696b91016f39        haproxy_web_webc    "/bin/sh -c 'python …"   44 minutes ago      Up 44 minutes       80/tcp                                   haproxy_web_webc_1_159bfc7f2644
dfcb7f1a0e8e        haproxy_web_weba    "/bin/sh -c 'python …"   44 minutes ago      Up 44 minutes       80/tcp                                   haproxy_web_weba_1_f047051398b2
0508a092fca0        haproxy_web_webb    "/bin/sh -c 'python …"   44 minutes ago      Up 44 minutes       80/tcp                                   haproxy_web_webb_1_ef0b108c4e3b

咱们能够看到建立并启动了四个容器,haproxy完成了两个端口的映射,web服务默认使用的80端口,expose只是声明。

查看镜像:
理论上说会下载python2.7和haproxy1.6的镜像,同时又会在python镜像的基础上建立3个web镜像。

  查看负载均衡的效果:
咱们连续刷新三次:

咱们能够看到在三个ip以前来回切换,这样就达到了负载均衡的效果。

(5)总结

再回来看目录结果:

[root@centos002 haproxy_web]# tree
.
|-- docker-compose.yml
|-- haproxy
|   `-- haproxy.cfg
`-- web
    |-- Dockerfile
    |-- index.html
    `-- index.py

2 directories, 5 files

貌似有好几个目录,可是其实核心目录只有一个就是docker-compose.yml。
咱们稍微改一下,也许就会更加清晰明确。

weba:
	image:nginx

webb:
	image:nginx

webc:
	image:nginx

haproxy:
    image: haproxy:1.6
    volumes:
        - haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    links:
        - weba
        - webb
        - webc
    ports:
        - "80:80"
        - "70:70"

这样咱们的目录结构:

[root@centos002 web_haproxy]# tree
.
|-- docker-compose.yml
`-- haproxy.cfg  这个文件没有的话,不能实现负载均衡的

0 directories, 2 files

  之因此建立web和haproxy这两个目录,就是为了更加的清晰明确。

相关文章
相关标签/搜索