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這兩個目錄,就是爲了更加的清晰明確。

相關文章
相關標籤/搜索