zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Docker-compose 工具解析

2023-03-15 22:06:50 时间

Docker-Compose 项目是 Docker 官方的一个开源项目,其主要职责是负责实现对Docker容器集群的快速编排。       Docker-Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)以及容器(container)。Docker-Compose 运行目录下的所有文件(docker-compose.yml,extends 文件或环境变量文件等)组成一个工程,若无特殊指定工程名即为当前目录名。一个工程当中可包含多个服务,每个服务中定义了容器运行的镜像,参数,依赖。一个服务当中可包括多个容器实例,Docker-Compose 并没有解决负载均衡的问题,因此需要借助其它工具实现服务发现及负载均衡。       Docker-Compose 的工程配置文件默认为 docker-compose.yml,可通过环境变量 COMPOSE_FILE 或 -f 参数自定义配置文件,其定义了多个有依赖关系的服务及每个服务运行的容器。使用一个 Dockerfile 模板文件,可以让用户很方便的定义一个单独的应用容器。在工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。       同时,Docker-Compose 允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。Docker-Compose 项目由 Python 编写,调用 Docker 服务提供的 API 来对容器进行管理。因此,只要所操作的平台支持 Docker API,就可以在其上利用 Compose 来进行编排管理。 

      关于微服务环境中的本地开发,我经常使用此工具。它也是轻量级的,只需要很小的努力。通过此工具,我们可以预先配置所需的环境和服务,然后专注于当前服务的开发,而不必过多去关注每个服务的运行方式以及服务与组件的相互调用关系,真没必要,除非迫不得已。

      借助 Docker-Compose,我们可以为应用服务、卷、挂载点、环境变量(几乎所有内容)以及所所涉及的依赖组件配置一个合理的网络。

      在介绍 Docker-Compose 工具之前,我们先了解下 Dockerfile。Dockerfile 是一个文本文件,其中包含用户可以在命令行上调用以组装镜像(Images)的所有命令。 我们可将其视为一个 Shell Script。它将多个命令收集到一个文档中,以完成一个任务。其简要的流程如下所示:

基于 Dockerfile 容器编排流程

    Dockerfile 的基本结构

       Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。

       Dockerfile 文件说明

       Docker 以从上到下的顺序运行 Dockerfile 的指令。为了指定基本映像,第一条指令必须是 FROM。一个声明以#字符开头则被视为注释。可以在 Docker 文件中使用 RUN、CMD、FROM、EXPOSE、ENV 等指令进行相关操作。

       下面我们看下 Dockerfile 的基础模版,以 Tengine 组件服务为例,具体如下所示:

 [administrator@JavaLangOutOfMemory ~ %]vi Dockerfile 
# Version 1.0

# Base images 基础镜像
FROM centos:7.6.1810

# MAINTAINER 维护者信息
MAINTAINER  Leon<lijie_2016@xxx.cn>

# 定义服务组件版本信息
ENV TENGINE_VERSION 2.1.2

# 定义服务组件环境变量
ENV CONFIG "
    --prefix=/etc/nginx 
    --sbin-path=/usr/sbin/nginx 
    --conf-path=/etc/nginx/nginx.conf 
    --error-log-path=/var/log/nginx/error.log 
    --http-log-path=/var/log/nginx/access.log 
    --pid-path=/var/run/nginx.pid 
    --lock-path=/var/run/nginx.lock 
    --http-client-body-temp-path=/var/cache/nginx/client_temp 
    --http-proxy-temp-path=/var/cache/nginx/proxy_temp 
    --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp 
    --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp 
    --http-scgi-temp-path=/var/cache/nginx/scgi_temp 
    --user=nginx 
    --group=nginx 
    --with-http_ssl_module 
    --with-http_realip_module 
    --with-http_addition_module 
    --with-http_sub_module 
    --with-http_dav_module 
    --with-http_flv_module 
    --with-http_mp4_module 
    --with-http_gunzip_module 
    --with-http_gzip_static_module 
    --with-http_random_index_module 
    --with-http_secure_link_module 
    --with-http_auth_request_module 
    --with-mail 
    --with-mail_ssl_module 
    --with-file-aio 
    --with-http_spdy_module 
    --with-ipv6 
    --with-jemalloc 
    "
# 定义文件目录操作相关信息
ADD ngx_user.patch /

ADD repositories /etc/apk/repositories

# 执行相关操作
RUN 
  addgroup -S nginx 
  && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx 
  && apk add --no-cache --virtual .build-deps 
    gcc 
    libc-dev 
    make 
    openssl-dev 
    pcre-dev 
    zlib-dev 
    linux-headers 
    curl 
    jemalloc-dev 
  && curl "http://tengine.taobao.org/download/tengine-$TENGINE_VERSION.tar.gz" -o tengine.tar.gz 
  && mkdir -p /usr/src 
  && tar -zxC /usr/src -f tengine.tar.gz 
  && rm tengine.tar.gz 
  && cd /usr/src/tengine-$TENGINE_VERSION/src/os/unix/ 
  && mv /ngx_user.patch ./ngx_user.patch 
  && patch ngx_user.c ngx_user.patch 
  && rm ngx_user.patch 
  && cd ../../../ 
#  && cd /usr/src/tengine-$TENGINE_VERSION 
  && ./configure $CONFIG --with-debug 
  && make 
  && mv objs/nginx objs/nginx-debug 
  && ./configure $CONFIG 
  && make 
  && make install 
  && rm -rf /etc/nginx/html/ 
  && mkdir /etc/nginx/conf.d/ 
  && mkdir -p /usr/share/nginx/html/ 
  && install -m644 html/index.html /usr/share/nginx/html/ 
  && install -m644 html/50x.html /usr/share/nginx/html/ 
  && install -m755 objs/nginx-debug /usr/sbin/nginx-debug 
  && strip /usr/sbin/nginx* 
  && runDeps="$( 
    scanelf --needed --nobanner /usr/sbin/nginx 
      | awk '{ gsub(/,/, "
so:", $2); print "so:" $2 }' 
      | sort -u 
      | xargs -r apk info --installed 
      | sort -u 
  )" 
  && apk add --virtual .nginx-rundeps $runDeps 
  && apk del .build-deps 
  && rm -rf /usr/src/nginx-$NGINX_VERSION 
  && apk add --no-cache gettext 
  
  # forward request and error logs to docker log collector
  && ln -sf /dev/stdout /var/log/nginx/access.log 
  && ln -sf /dev/stderr /var/log/nginx/error.log

# 定义文件的相关操作信息
COPY nginx.conf /etc/nginx/nginx.conf

COPY nginx.vh.default.conf /etc/nginx/conf.d/default.conf

# 定义服务组件所暴露的端口信息
EXPOSE 80 443

# 定义服务组件启动命令
CMD ["nginx", "-g", "daemon off;"]

        以上为简要的 Dockerfile 文件,然后我们对进行构建,具体命令如下所示:

[administrator@JavaLangOutOfMemory ~ %]docker build  -f Dockerfile -t leon-tengine .
  • docker build:用 Dockerfile 构建镜像的命令关键词。
  • [OPTIONS] : 命令选项,常用的指令包括 -t 指定镜像的名字。
  • -f 显示指定构建镜像的 Dockerfile 文件(Dockerfile 可不在当前路径下)。
  • 如果不使用 -f,则默认将上下文路径下的名为 Dockerfile 的文件认为是构建镜像的 “Dockerfile” 。
  • 上下文路径|URL:指定构建镜像的上下文的路径,构建镜像的过程中,可以且只可以引用上下文中的任何文件。

        至此,一个完整的容器镜像构建完成,此时,只需要借助 Docker 命令行直接启动即可。通过上面的介绍可以看出,Dockerfile 还是蛮个性化的,它可以依据我们个人喜好进行自定义镜像开发。

        接下来,我们解析下 Docker-Compose 工具,上面的实例借助 Docker 命令启动仅仅为单一组件的容器部署,若在某一特定场景下,我们需要快速部署集群式应用场景或多个不同组件的服务,同时方便管理,此时 Docker-Compose 工具最合适不过了。

       以下为简要的模版,具体如下:

[administrator@JavaLangOutOfMemory ~ %]vi docker-compose-elk.yaml
version: '3'
services:
  elasticsearch:
    image: docker.leon.com/elasticsearch/elasticsearch:7.4.2
    container_name: elasticsearch
    restart: always
    volumes:
      - elasticsearch:/usr/share/elasticsearch/data
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "9200:9200"
    networks:
      - elastic
    environment:
      - discovery.type=single-node
  
  kibana:
    image: docker.leon.com/kibana/kibana:7.4.2
    container_name: kibana
    restart: always
    volumes:
      - kibana:/usr/share/kibana/config:rw
      - /etc/localtime:/etc/localtime
    depends_on:
      - elasticsearch
    ports:
      - "5601:5601"
    networks:
      - elastic
    environment:
      SERVER_NAME: kibana.example.org
      ELASTICSEARCH_HOSTS: http://elasticsearch:9200

  logstash:
    image: docker.leon.com/logstash/logstash:7.4.2
    container_name: logstach
    command: logstash -f /usr/share/logstash/conf/logstash-kafka.conf
    restart: always
    tty: true 
    ports:
      - "5044:5044"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - logstash:/usr/share/logstash/conf/
    networks:
      - elastic
    depends_on:
      - elasticsearch
    environment:
      - elasticsearch.hosts=http://elasticsearch:9200
      - xpack.monitoring.elasticsearch.hosts=http://elasticsearch:9200

  zookeeper:
    image: docker.leon.com/zookeeper
    container_name: zookeeper
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - zookeeper:/data
      - zookeeper_log:/datalog
    networks:
      - elastic
    ports:
      - "2181:2181"

  kafka:
    container_name: kafka
    image: docker.leon.com/kafka
    depends_on:
      - zookeeper
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - kafka:/kafka
      - /etc/localtime:/etc/localtime:ro
    links:
      - zookeeper
    ports:
      - "9092:9092"
    networks:
      - elastic
    environment:
      - KAFKA_LISTENERS=INTERNAL://kafka:9092, OUT://kafka:29092
      - KAFKA_ADVERTISED_LISTENERS=INTERNAL://kafka:9092, OUT://kafka:29092
      - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,OUT:PLAINTEXT
      - KAFKA_INTER_BROKER_LISTENER_NAME=OUT
      - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
      - KAFKA_MESSAGE_MAX_BYTES=2000000
      - KAFKA_CREATE_TOPICS=logs:1:1

volumes:
  elasticsearch:
    driver: local-persist
    driver_opts:
      mountpoint: /elk/elasticsearch/
  kibana:
    driver: local-persist
    driver_opts:
      mountpoint: /elk/kibana/
  logstash:
    driver: local-persist
    driver_opts:
      mountpoint: /elk/logstash/conf/
  zookeeper:
    driver: local-persist
    driver_opts:
      mountpoint: /elk/zookeeper/data/
  zookeeper_log:
    driver: local-persist
    driver_opts:
      mountpoint: /elk/zookeeper/datalog/
  kafka:
    driver: local-persist
    driver_opts:
      mountpoint: /elk/kafka/

networks:
  elastic:

      基于上述 yaml 文件,我们针对“核心”的标签作简要分析如下:

Docker-Compose 模板文件是一个定义服务、网络和卷的 YAML 文件。Compose 模板文件默认路径是当前目录下的docker-compose.yml,可以使用 .yml 或 .yaml 作为文件扩展名。

      Docker-Compose 标准模板文件包含 version、services、networks 三大部分,最关键的是 services 和 networks 两个部分。

version 标签:Compose 目前有三个版本分别为 Version 1,Version 2,Version 3,Compose 区分 Version 1 和 Version 2(Compose 1.6.0+,Docker Engine 1.10.0+)。Version 2 支持更多的指令。Version 1 将来会被弃用。

      image 标签:指定服务的镜像名称或镜像 ID。如果镜像在本地不存在,Docker-Compose 将会尝试拉取镜像。

depends_on 标签:用于解决容器的依赖、启动先后的问题。在使用 Compose 时,最大的好处就是少打启动命令,但一般项目容器启动的顺序是有要求的,如果直接从上到下启动容器,必然会因为容器依赖问题而启动失败。例如在没启动数据库容器的时候启动应用容器,应用容器会因为找不到数据库而退出。

    volumes 标签:挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER] 格式,或者使用[HOST:CONTAINER:ro] 格式,后者对于容器来说,数据卷是只读的,可以有效保护宿主机的文件系统。指定路径可以是相对路径,使用 . 或者 .. 来指定相对目录。如果不使用宿主机的路径,可以指定一个 volume_driver。例如:volume_driver: mydriver。

links 标签:链接到其它服务中的容器。使用服务名称(同时作为别名),或者“服务名称:服务别名”(如SERVICE:ALIAS)。

    networks 标签:主要设置网络模式。

    以下为 Docker-compose 相关命令行:

[administrator@JavaLangOutOfMemory ~ %] docker-compose up (-d)
... docker-compose down
... docker-compose build 
... docker-compose logs (-f) 
... docker-compose run (--no-deps)
... 

      具体完整操作,可参考之前文章:Docker GUI工具-Portainer浅析

      以上为关于 Docker-Compose 工具的相关解析,希望对技术爱好者,尤其是初学者有所帮助,有关各种技术问题,欢迎大家随时留言沟通。