七叶笔记 » golang编程 » Docker-compose解析

Docker-compose解析

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文件说明

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

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

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


# Base images 基础镜像
FROM centos:7.6.1810


# MAINTAINER 维护者信息
MAINTAINER  Leon<lijie_2016@migu.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 "#34; -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(/,/, "\nso:", $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: 


  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=
      - xpack.monitoring.elasticsearch.hosts=


  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工具的相关解析,希望对技术爱好者,尤其是初学者有所帮助,有关各种技术问题,欢迎大家随时留言沟通。

相关文章