Docker 操作指南之 Compose 管理篇

举报
zuozewei 发表于 2024/03/22 10:45:10 2024/03/22
【摘要】 Docker 操作指南之 Compose 管理篇

一、简介

Compose 定位是“defining and running complex applications with Docker”,前身是 Fig,兼容 Fig 的模板文件。
Dockerfile 可以让用户管理一个单独的应用容器;而 Compose 则允许用户在一个模板(YAML 格式)中定义一组相关联的应用容器(被称为一个 project,即项目),例如一个 Web 服务容器再加上后端的数据库服务容器等。

image.png

该项目由 Python 编写,实际上调用了 Docker 提供的 API 来实现。

二、安装

安装 Compose 之前,要先安装 Docker,在此不再赘述。

1、pip安装

这种方式最为推荐。
执行命令。

sudo pip install -U docker-compose

安装成功后,可以查看 docker-compose 命令的用法。

2、二进制包

发布的二进制包可以在 https://github.com/docker/compose/releases 找到。
下载后直接放到执行路径即可。

#下载docker-compose-Linux-x86_64
cp docker-compose-Linux-x86_64 /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose

三、使用

1、术语

首先介绍几个术语。

  • 服务(service):一个应用容器,实际上可以运行多个相同镜像的实例。
  • 项目(project):由一组关联的应用容器组成的一个完整业务单元。

可见,一个项目可以由多个服务(容器)关联而成,Compose 面向项目进行管理。

2、场景

下面,我们创建一个经典的 Web 项目:一个 Haproxy,挂载三个Web容器。
创建一个 compose-haproxy-web 目录,作为项目工作目录,并在其中分别创建两个子目录:haproxy 和 web。

3、Web 子目录

这里用 Python 程序来提供一个简单的 HTTP 服务,打印出访问者的 IP 和 实际的本地 IP。

3.1、index.py

编写一个 index.py作为服务器文件,代码为

#!/usr/bin/python
#authors: 7d

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()

3.2、index.html

生成一个临时的 index.html 文件,其内容会被 index.py 更新。

touch index.html

3.3、Dockerfile

生成一个 Dockerfile,内容为

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

4、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 :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

5、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"

6、运行 compose 项目

现在 compose-haproxy-web 目录长成下面的样子。

compose-haproxy-web
├── docker-compose.yml
├── haproxy
│   └── haproxy.cfg
└── web
    ├── Dockerfile
    ├── index.html
    └── index.py

在该目录下执行 docker-compose up 命令,会整合输出所有容器的输出。

$sudo docker-compose up
Recreating composehaproxyweb_webb_1...
Recreating composehaproxyweb_webc_1...
Recreating composehaproxyweb_weba_1...
Recreating composehaproxyweb_haproxy_1...
Attaching to composehaproxyweb_webb_1, composehaproxyweb_webc_1, composehaproxyweb_weba_1, composehaproxyweb_haproxy_1

此时访问本地的 80 端口,会经过 haproxy 自动转发到后端的某个 web 容器上,刷新页面,可以观察到访问的容器地址的变化。
访问本地 70 端口,可以查看到 haproxy 的统计信息。

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

四、Compose 命令说明

大部分命令都可以运行在一个或多个服务上。如果没有特别的说明,命令则应用在项目所有的服务上。

执行 docker-compose [COMMAND] --help 查看具体某个命令的使用说明。

基本的使用格式是:

docker-compose [options] [COMMAND] [ARGS...]

1、选项

• --verbose 输出更多调试信息。
• --version 打印版本并退出。
• -f, --file FILE 使用特定的 compose 模板文件,默认为 docker-compose.yml
• -p, --project-name NAME 指定项目名称,默认使用目录名称。

2、命令

2.1、build

构建或重新构建服务。
服务一旦构建后,将会带上一个标记名,例如 web_db。
可以随时在项目目录下运行 docker-compose build 来重新构建服务。

2.2、config

查看当前文件夹下的 compose yml 配置文件

2.3、create

创建服务(容器),但不启动,需要start才能启动。

2.4、down

停止和删除容器,包括网络、镜像和磁盘。

2.5、 events

Receive real time events from containers

2.6、exec

在容器中执行命令。

在容器中创建一个文件:

#docker-compose exec memcached touch /root/test

连接到容器:

image.png

2.7、help

获得一个命令的帮助。

2.8、kill

通过发送 SIGKILL 信号来强制停止服务容器。支持通过参数来指定发送的信号,例如:

docker-compose kill -s SIGINT

2.9、logs

查看服务的输出。
-f 可以跟踪查看。

2.10、pause

暂停一个服务。使用 unpause 再次启动。

2.11、port

打印容器中的私有端口绑定后的公共端口。

2.12、ps

列出所有容器。

2.13、pull

拉取服务镜像。

2.14、push

推送服务镜像。

2.15、restart

重启服务容器。

2.16、rm

删除停止的服务容器。

2.17、run

在一个服务上执行一个命令。
例如:

docker-compose run ubuntu ping docker.com

将会启动一个 ubuntu 服务,执行 ping docker.com 命令。
默认情况下,所有关联的服务将会自动被启动,除非这些服务已经在运行中。
该命令类似启动容器后运行指定的命令,相关卷、链接等等都将会按照期望创建。
两个不同点:

  • 给定命令将会覆盖原有的自动运行命令;
  • 不会自动创建端口,以避免冲突。

如果不希望自动启动关联的容器,可以使用 --no-deps 选项,例如

docker-compose run --no-deps web python manage.py shell

将不会启动 web 容器所关联的其它容器。

2.18、scale

设置同一个服务运行的容器个数。
通过 service=num 的参数来设置数量。例如:

docker-compose scale web=2 worker=3

2.19、start

启动一个已经存在的服务容器。

2.20、stop

停止一个已经运行的容器,但不删除它。通过 docker-compose start 可以再次启动这些容器。

2.21、top

显示所有正在运行的进程。

image.png

2.22、up

构建,(重新)创建,启动,链接一个服务相关的容器。
链接的服务都将会启动,除非他们已经运行。

默认情况, docker-compose up 将会整合所有容器的输出,并且退出时,所有容器将会停止。
如果使用 docker-compose up -d,将会在后台启动并运行所有的容器。

默认情况,如果该服务的容器已经存在, docker-compose up 将会停止并尝试重新创建他们(保持使用 volumes-from 挂载的卷),以保证 docker-compose.yml 的修改生效。如果你不想容器被停止并重新创建,可以使用 docker-compose up --no-recreate

如果需要的话,这样将会启动已经停止的容器。

2.23、unpause

启动暂停的服务容器。

2.24、version

显示 docker 信息。
image.png

五、环境变量

环境变量可以用来配置 Compose 的行为。
DOCKER_ 开头的变量和用来配置 Docker 命令行客户端的使用一样。如果使用 boot2docker , $(boot2docker shellinit) 将会设置它们为正确的值。

1、COMPOSE_PROJECT_NAME

设置通过 Compose 启动的每一个容器前添加的项目名称,默认是当前工作目录的名字。

2、COMPOSE_FILE

设置要使用的 docker-compose.yml 的路径。默认路径是当前工作目录。

3、DOCKER_HOST

设置 Docker daemon 的地址。默认使用 unix:///var/run/docker.sock,与 Docker 客户端采用的默认值一致。

4、DOCKER_TLS_VERIFY

如果设置不为空,则与 Docker daemon 交互通过 TLS 进行。

5、DOCKER_CERT_PATH

配置 TLS 通信所需要的验证(ca.pem、cert.pem 和 key.pem)文件的路径,默认是 ~/.docker

六、YAML 模板文件

默认的模板文件是 docker-compose.yml,其中定义的每个服务都必须通过 image 指令指定镜像或 build 指令(需要 Dockerfile)来自动构建。
其它大部分指令都跟 docker run 中的类似。
如果使用 build 指令,在 Dockerfile 中设置的选项(例如:CMD, EXPOSE, VOLUME, ENV 等) 将会自动被获取,无需在 docker-compose.yml中再次设置。

1、image

指定为镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉去这个镜像。
例如:

image: ubuntu
image: orchardup/postgresql
image: a4bc65fd

2、build

指定 Dockerfile 所在文件夹的路径。 Compose 将会利用它自动构建这个镜像,然后使用这个镜像。

build: /path/to/build/dir

3、command

覆盖容器启动后默认执行的命令。

command: bundle exec thin -p 3000

4、links

链接到其它服务中的容器。使用服务名称(同时作为别名)或服务名称:服务别名 (SERVICE:ALIAS) 格式都可以。

links:
 - db
 - db:database
 - redis

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

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

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

5、external_links

链接到 docker-compose.yml 外部的容器,甚至并非 Compose 管理的容器。参数格式跟 links 类似。

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

6、ports

暴露端口信息。
使用宿主:容器 (HOST:CONTAINER)格式或者仅仅指定容器的端口(宿主将会随机选择端口)都可以。

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

注:当使用 HOST:CONTAINER 格式来映射端口时,如果你使用的容器端口小于 60 你可能会得到错误得结果,因为 YAML将会解析 xx:yy 这种数字格式为 60 进制。所以建议采用字符串格式。

7、expose

暴露端口,但不映射到宿主机,只被连接的服务访问。
仅可以指定内部端口为参数

expose:
 - "3000"
 - "8000"

8、volumes

卷挂载路径设置。可以设置宿主机路径 (HOST:CONTAINER) 或加上访问模式 (HOST:CONTAINER:ro)。

volumes:
 - /var/lib/mysql
 - cache/:/tmp/cache
 - ~/configs:/etc/configs/:ro

9、volumes_from

从另一个服务或容器挂载它的所有卷。

volumes_from:
 - service_name
 - container_name

10、environment

设置环境变量。你可以使用数组或字典两种格式。
只给定名称的变量会自动获取它在 Compose 主机上的值,可以用来防止泄露不必要的数据。

environment:
  RACK_ENV: development
  SESSION_SECRET:

environment:
  - RACK_ENV=development
  - SESSION_SECRET

11、env_file

从文件中获取环境变量,可以为单独的文件路径或列表。
如果通过 docker-compose -f FILE 指定了模板文件,则 env_file 中路径会基于模板文件路径。
如果有变量名称与 environment 指令冲突,则以后者为准。

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

环境变量文件中每一行必须符合格式,支持 # 开头的注释行。

# common.env: Set Rails/Rack environment
RACK_ENV=development

12、extends

基于已有的服务进行扩展。例如我们已经有了一个 webapp 服务,模板文件为 common.yml

# common.yml
webapp:
  build: ./webapp
  environment:
    - DEBUG=false
    - SEND_EMAILS=false

编写一个新的 development.yml 文件,使用 common.yml 中的 webapp 服务进行扩展。

# development.yml
web:
  extends:
    file: common.yml
    service: webapp
  ports:
    - "8000:8000"
  links:
    - db
  environment:
    - DEBUG=true
db:
  image: postgres

后者会自动继承 common.yml 中的 webapp 服务及相关环节变量。

12、net

设置网络模式。使用和 docker client 的 --net 参数一样的值。

net: "bridge"
net: "none"
net: "container:[name or id]"
net: "host"

13、pid

跟主机系统共享进程命名空间。打开该选项的容器可以相互通过进程 ID 来访问和操作。

pid: "host"

14、dns

配置 DNS 服务器。可以是一个值,也可以是一个列表。

dns: 8.8.8.8
dns:
  - 8.8.8.8
  - 9.9.9.9

15、cap_add, cap_drop

添加或放弃容器的 Linux 能力(Capabiliity)。

cap_add:
  - ALL

cap_drop:
  - NET_ADMIN
  - SYS_ADMIN

16、dns_search

配置 DNS 搜索域。可以是一个值,也可以是一个列表。

dns_search: example.com
dns_search:
  - domain1.example.com
  - domain2.example.com

17、working_dir, entrypoint, user, hostname, domainname, mem_limit, privileged, restart, stdin_open, tty, cpu_shares

这些都是和 docker run 支持的选项类似。

cpu_shares: 73
working_dir: /code
entrypoint: /code/entrypoint.sh
user: postgresql
hostname: foo
domainname: foo.com
mem_limit: 1000000000
privileged: true
restart: always
stdin_open: true
tty: true

七、compose 使用实例

1、管理一个 tomcat 容器

前提,需要准备好一个 tomcat image
下面,开始准备 ym 文件

mkdir tomcat8880
vi docker-compose.yml
tomcat:
    image: tomcat7:v0.1
    container_name: tomcat8880
    restart: always
    ports:
        - 8880:8080
    volumes:
        - /data/container_ap/:/data/ap/

构建一个容器

cd tomcat8880
docker-compose up –d

启动完成后,可以看到该容器正在运行:
image.png

查看容器日志
image.png

停止该容器
image.png

启动该容器
image.png

2、管理一个java系统

该系统为本人在生产环境中的维护的一个系统,大致架构如下:

  • 前端:apache+tomcat 通过 jk 的方式整合在一起,应用程序发布到 tomcat
  • 中间层:memcached
  • 数据库:oracle

现在,将该系统放到 docker 中运行:

  • 容器1:apache、tomcat、应用程序
  • 容器2:memcached
  • 另一物理机:oracle

2.1、准备所需软件:

  1. 新建存放数据的目录
#mkdir –p /data/saba
  1. 将如下软件放至该目录
apache
tomcat(已整合完成)
jdk1.7.0_11
SabaWeb(应用系统)

2.2、准备 saba 启动脚本

cd /data/saba
vi run.sh
#!/bin/bash
#启用环境变量
source /root/.bash_profile
sleep 1
#启动apache
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/saba/nls/apache/lib
/saba/nls/apache/bin/httpd -f /saba/nls/apache/conf/httpd.conf
sleep 1
#启动tomcat
export LC_ALL=en_US.UTF-8
cd /saba/nls/tomcat/bin
sh startServer.sh start

2.3、准备 saba 镜像

mkdir –p /data/images/saba
vi Dockerfile
FROM centos:centos6
MAINTAINER 7d
RUN mkdir -p /saba/nls
RUN mkdir -p /home/ap/nls
RUN mkdir -p /saba/nls/contentserver/prod_content
RUN mkdir -p /saba/nls/contentserver/prod_assets
RUN echo 'export JAVA_HOME=/saba/nls/jdk1.7.0_11' >> /root/.bash_profile
RUN echo 'export PATH=$JAVA_HOME/bin:$PATH' >> /root/.bash_profile

VOLUME ["/saba/nls"]
EXPOSE 80
EXPOSE 8080
docker build -t="saba:v0.1" .

得到镜像:

REPOSITORY       TAG             IMAGE ID            CREATED             VIRTUAL SIZE
saba             v0.1            106dd7af0587        28 seconds ago         194.6 MB

2.4、准备 compose yml 文件

通过 compose 管理 memcached、saba7.2 两个容器(服务)

cd /data/compose-saba
vi docker-compose.yml
memcached:
  image: memcached:v0.1
  container_name: memcached
  ports:
      - "11211:11211"
  restart: always

saba7.2:
  image: saba:v0.1
  container_name: saba7.2
  ports:
      - "8088:8080"
      - "1080:80"
  volumes:
      - /data/saba:/saba/nls
  links:
      - memcached
  command: /saba/nls/run.sh

2.5、启动服务

创建完 yml 配置文件后,开始启动服务器:

cd /data/compose-saba
docker-compose up –d
Creating memcached ... 
Creating memcached ... done
Creating saba7.2 ... 
Creating saba7.2 ... done

2.6、相关操作

启动完成后,即可进行如下操作(所有操作需要在 yml 文件所在目录下进行):

cd /data/compose-saba

查看启动的服务(容器):
image.png

查看容器的日志:
image.png

启停所有(或单个)容器

docker-compse stop|start
docker-compse stop|start memcached

通过宿主机连接到容器

docker-compose exec memcached /bin/bash

image.png

八、常见问题

1、Docker容器启动后退出

  • 原因:docker 容器的主线程(dockfile 中 CMD 执行的命令)结束,容器会退出。
  • 解决方案:
    • 在脚本最后一行添加 tail -f /dev/null,这个命令永远完成不了,所以该容器永远不会退出。
    • 使用 supervisor 管理容器中程序。

2、容器时间不同步

容器启动时加入如下参数:

-v /etc/localtime:/etc/localtime:ro

3、容器中找不到 vi、ifconfig 命令

进入容器后,执行如下命令:

apt-get update
apt-get install net-tools vim

Dockerfile 中也可加入该内容,其他命令找不到亦然。

其他常用工具:

install iputils-ping net-tools netcat telnet unzip lrzsz vim

4、容器不支持中文

  1. locale 查看当前语言环境
    POSIX 不支持中文,C.UTF-8 支持中文
  2. locale -a 查看支持的语言环境
  3. 临时修改
  echo 'LANG=C.UTF-8' >> /etc/profile
  source /etc/profile
  1. 永久修改
    Dockerfile 中加入:ENV LANG C.UTF-8
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。