Day04-管理 Docker 的各种组合(Docker Compose)

前言

今天的文章要来介绍点不一样的,想像一下一个完整的专案一定包含前端、後端、资料库这三个东西,假如今天我要利用 Docker 完成这三件事情的部属势必得写个简单的 shellscript 来跑 docker build 以及 docker run,但这样实在是难以管理,光是 image 的 ID 要一个一个对照就是件很麻烦的事了,当然这件事情 Docker 也知道,所以今天的文章就要利用另一种方式来管理多个 Docker image 及 container,这个工具就叫做 Docker Compose

什麽是 Docker Compose

就字面上的意思来说 compose 其实就是组合的意思,所以 Docker Compose 就是把专案内需要用到的 image 进行组合。既然都可以组合这些 image了,所以 Docker Compose 当然也可以管理利用这些 image 跑起来的container,接下来就正式进入 Docker Compose 的写法吧!

Docker Compose 写法

首先 Docker Compose 会以 YAML 格式进行设定,YAML 格式简单来说就是一种可读性高,用来表达资料序列化的格式,而 Docker Compose 的副档名会以 .yml 作为结尾,所以整体的档案名就会像是这样:docker-compose.yml 这种形式存在。

一般来说 Docker Compose 内部可以分为四个区块:

  • version

    version 代表着这份 Docker Compose file 的版本,不同的版本其 Docker engine 的支援度不同,目前有 version 1 、 version 2、 version 3 三种版本,最常使用且支援度最高的是 version 3,写法上也很简单就像这样:

    version: '3'
    
  • services

    对於 Docker Compose 来说,只要是此专案需要用到的东西都统称为服务(services),假如今天有一个专案需要以下几个结构:

    • 前端(web)
    • 网页服务器(nginx)
    • 反向代理服务器(middleware)
    • 後端(backend)
    • 资料库(db)

    则这个专案就会有五种服务,这些 service 的描述会写在一个大的 services 区块内,这样讲可能有点抽象,所以接下来笔者要带读者们一步一步把 service 建起来吧!笔者会尽量用浅显易懂的方式让读者可以了解 service 的写法。

    首先要先定义这个 service 的名称,这样 Docker Compose 在建立这些 service 时才会知道要用什麽名称来定义,写法就像这样:

    services:
      web:
        # write web settings
    

    如果有写过 python 的读者应该会觉得这种写法应该跟 python 是蛮像的,用冒号跟缩排的方式来概括这个区块要表达的事项,看了上面的写法应该不难懂 service 的定义方式,其中的 web 就代表有一个 service 叫做 web,定义好 service 的名称後接下来就要指定要这个 service 运行哪个 Docker image,写法就像这样:

    # image 来自 Dockerfile build
    web:
      build: ./front-end
    
    # image 来自 Docker Hub
    mysql:
      build: mysql:5.7
    

    定义好来源後接下来就要指定与外部对接的接口了,今天启动这个 service 最重要的功能就是要能与其他的 service 甚至是外来的 request 进行沟通,如果没有打开对应的接口这样不管如何操作都没办法使用到这个 service,所以下一步很重要的步骤就是指定对外的接口,写法就像这样:

    # ports 写法会是 "HOST:CONTAINER",其中 HOST 代表机器的接口,而 CONTAINER 代表的则是容器自身的对外接口
    ports:
      "80:80" # 这句话代表的是机器的 80 port 会对应到 container 的 80 port,所以只要 container 在启动时有开放 80 port,就可以很顺利的对应到机器对外的 80 port 了。
    
    # 假如只写一个则代表 "CONTAINER"
    ports:
      "80"
    

    假如每个服务彼此之间有相依性才能启动成功的话,Docker Compose 也可以很简单的达到这个需求,举例来说:一定要先把资料库启动才能让後端连接到资料库,这两个服务彼此之间就有相依性的存在了,这个写法也很简单就像下面这样:

    depends_on:
      - db # 这个写法就代表这个服务要启动前,db 这个服务一定要先启动才可以。
    

    最後就要来讲 container 的重启了,Docker Compose 最重要的就是管理各 service 启动後的 container,所以假如有 service 需要重启当然也要有对应的重启方式,这些也都可以利用 Docker Compose 进行设定,写法就像这样:

    # restart 有四种参数:"no"、always、on-failure、unless-stopped
    restart: always
    

    最後组合起来就会像这样:

    最後再来讲一个设定,相信大家在开发的时候很常使用到环境变数吧,像前端可能就会写一个 .env 档,然後搭配 process.env 的方式来取得环境变数的资料,但通常这些环境变数档案是不会轻易摆在专案内的,毕竟就是因为这些资料很敏感所以才会写成环境变数,这些其实也都可以利用 Docker Compose 的设定来完成,这边以资料库举例写法就像下面这样:

    environment:
      MYSQL_DATABASE: 'mytestdb'
      MYSQL_USER: 'testuser'
      MYSQL_PASSWORD: 'password'
      MYSQL_ROOT_PASSWORD: 'password'
    
  • volumes

    volumes 的存在就是为了让每次重启 container 时都还可以引用到之前所存放的资料,相信在前面的 Dockerfile 练习时有发现每次将 container 停止後,再次启动时会发现之前在里面设定好的档案都消失了,假如要能够保存这些资料就必须要利用 volume 这个观念,volumes 提供了一套让 container 可以保存或共享资料的方法,而 Docker Compose 可以轻松地做到这点,所以这也是笔者没有在前面的文章先提到这个观念的关系。

    而 volumes 可以搭配写在 service 区块内的 volumes 就像下面这样:

    services:
      db:
        image: db
        volumes:
          - data-volume:/var/lib/db
    
      volumes:
        data-volume:
    

    可以看到在 db 这个 service 内有一个 volumes,这个 volumes 拿到的 data-volume 这个档案会映射到 image 内的 /var/lib/db 这个资料夹内,那这个 data-volume 哪来呢?就是透过最外面的 volumes 传进来的,而最外层的 volumes 的 data-volume 则可以透过外部挂载的方式带进来,上面听起来有点像在绕口令,但可以想像成档案挂载这种方式就是由外到内一层一层传进去各个 service 内。

  • networks

    networks 主要在做的事情就是专案内各个 container 的沟通,没有 network 的帮忙是没办法顺利完成这个动作的,在 network 中会搭配一些 driver,常见的有:

    • bridge: 将各个服务建立一座可以互相沟通的桥梁。
    • overlay: 各 container 之间启用 multi-host 通讯,可以想像成多个机器内的 container 彼此之间可以互相沟通。

    但其实每个 container 在没有设定 network 前都会有一个 default network,这个 default network 就是 bridge,而 overlay 的 multi-host 通讯其实跟 Docker swarm 比较有关系,但是 Docker swarm 其实某种程度来说跟後续要介绍的 K8s 有点像所以这边就不细讨 network 的沟通方式了,这些都会在 K8s 的相关介绍中一一为读者说明,接下来就简单的演示一下 network 的写法:

    networks:
      myNetwork:
        driver: bridge
    

最後要提一下一个重要的观念,不管是 Dockerfile 还是 Docker Compose 都会用到映射这个观念,而映射简单来说就是把本地端的档案丢给 Docker Container 内部使用。

Docker Compose 指令集

上面讲完了 Docker Compose 的观念以及写法後,最後就要来提一下如何透过指令来建立这些 service 并且管理。

  • docker-compose build:建立由 Docker Compose 管理的 Docker image。

  • docker-compose up -d:建立并启动由 Docker Compose 管理的 container,加上 -d 代表的是让这些 container 运行在背景,只要打上 docker-compose up 都会让这些 container 以及 image 重新被建立。

  • docker-compose stop:停止由 Docker Compose 管理的所有 container。

  • docker-compose start:启动由 Docker Compose 管理的 container,与 docker-compose up -d 的差别在於,docker-compose start 只会执行已经存在但目前是暂停状态的 container。

  • docker-compose restart:重新启动由 Docker Compose 管理的 container。

  • docker-compose down:停止并删除由 Docker Compose 管理的 container。

小结

这三天终於把 Docker 比较基础的观念跟用法都讲完了,其实 Docker 能玩的东西非常多而且还有很多更进阶的用法,像是 swarm 等等,但碍於铁人赛的篇幅笔者就不一一细说了,而且刚刚提到的那些进阶的 Docker 工具其实在接下来的 K8s 文章中都会提到,所以笔者这边就只介绍比较基本的 Docker 观念及用法而已。

如果对於文章有任何问题都欢迎留言,接下来就要进入本系列文最重要的重头戏:K8s 的相关介绍了!


<<:  Day4 寻找合适的 Lua 开发工具

>>:  Day4 Python 基础教学 (三)

【JavaScript】用debugger进行除错

【前言】 本系列为个人前端学习之路的学习笔记,在过往的学习过程中累积了很多笔记,如今想藉着IT邦帮忙...

[Day 5] lock-free stack

前言 今天将会实作资料结构 stack , 之所以选择 stack 是因为其制作简单, 可以让读者轻...

[Python]如何Text to Speech: pyttsx3, gTTS

https://pythonprogramminglanguage.com/text-to-spee...

Day 05 - Scanners

本篇重点 Scanners介绍 当日成交金额排行 当日成交量排行 当日涨(跌)金额排行 最高/最低价...

30天零负担轻松学会制作APP介面及设计【DAY 03】

大家好,我是YIYI,今天我要来介绍规格表。 关於规格表 规格表可以帮助大家快速地了解到你所想制作的...