DevOps实践

1、DevOps

所谓云原生,可以概括为四个点:DevOps+持续交付+微服务+容器

朴素的说,DevOps = Dev(开发人员)+Ops(运维人员)。实际上涵盖的角色范围会更广泛:除了开发,测试,运维,还涉及项目经理,产品经理等等跨职能部门相互合作,完成某一项目或者任务。

实际上DevOps从需求设计到开发、测试到运维,甚至是线上的运行反馈是整个全生命周期的,所以他是一个大桶多个部门协调的平台。至于工具和自动化只是实现的一种手段。或者说,DevOps是通过工具,自动化来达到通过工具链与持续集成、交付、反馈、优化进行端到端的整合,完成无缝的跨团队、跨系统协作。总结为三点:

  • DevOps理念:以客户、业务需求为导向,向着同一个目标前进,强调多个部门紧密沟通与协作的软件交付过程。它包括产品管理,软件开发及运营等各个方面。
  • DevOps核心实践:人员协作文化+持续交付能力支撑
  • DevOps目标:建立一种精诚合作的文化和环境,通过工具链与持续集成、交付、反馈、优化来实现跨团队、跨系统协作方式。

DevOps最佳实践手段就是CICD,这是绕不开的话题,DevOps是CICD思想的延伸,CICD是DevOps的技术核心,没有CICD和自动化测试,DevOps是没有意义的。

CI

持续集成。

是一种开发实践,倡导团队需要频繁的集成工作,每次集成都通过自动化构建(编译,构建,自动化测试)来验证,从而快速发现集成中的错误。让正在开发的软件始终处于可工作状态,让产品可以快速迭代,同时保持高质量。

CD

CD包含了两层内容,持续交付和持续部署。这两者是不相等的。

  • 持续交付:持续集成的延伸,他将集成后的代码部署到生产环境,确保可以以可持续的方式快速向客户发布新的更改。如果代码没有问题,可以继续手工部署到生产环境中。她强调的是,不管怎么更新,软件是随时随地可以交付的。
  • 持续部署:持续部署是持续交付的下一步,在持续交付的基础上,由开发人员或运维人员自助式的定期向生产环境部署稳定的构建版本,持续部署的目标是代码在任何时刻都是可部署的,并可自动进入到生产环境。

持续交付是指团队确保每个变更可以部署至生产环境,但也许并不需要实际部署,这通常可能是出于业务方面的原因。而持续部署是指每个变更可以自动部署到生产环境。只有成功实现持续交付的前提下,才能进行持续部署。

2、CICD实践

这部分总结我们实验室在CICD的实践。

实验室最开始的软件开发过程是这样的:后端同学按照文档写API,前端同学前期用mock模拟数据保证开发进度不受影响,在前后端联调的阶段,后端同学把自己的jar包手动运行在服务器上,供前端同学调用。现在前端同学反映API有问题,后端同学在debug之后重新打包放在服务器运行,这是一个非常繁琐且无趣的过程。于是我们开始探索CICD的实现。

没有使用厂商的集成工具,用的是gitlab+docker技术实现CICD。

1.相关概念介绍

  • CICD

    前面介绍过了

  • Docker

    Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。

    Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

    容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

  • Docker-compose

    Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。这非常适用于我们的微服务场景,每个微服务一般部署若干实例,每个实例手动启停的开销过大,可以借助docker-compose管理。

  • container/image

    镜像类似于虚拟机的镜像。是一个只读模板,一个独立的文件系统,包含运行容器需要的所有数据。

    dcoker利用容器来创建应用:docker容器是由docker镜像创建的运行实例。

    image-20210324204204902

  • .gitlab-ci.yml、Dockerfile、gitlab-runner

    • .gitlab-ci.yml:定义流水线阶段分级与每个阶段具体逻辑的文件
    • Dockerfile:用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
    • gitlab-runner:执行软件集成脚本的机器。

2.机器

  • CI机:安装docker和gitlab-runner
  • 服务机:安装docker和docker-compose
  • gitlab:代码仓库,在项目中设置环境变量和gitlab-runner
  • 阿里云镜像服务:需要使用云商提供的容器镜像服务来构建 远程镜像仓库,本文以阿里云为例。

CICD

3.步骤

准备工作

  • CI机:2核8G/100Mbps
  • 服务机:4core16G/100Mbps
  • 阿里云镜像容器服务:
    • 容器镜像服务ACR
    • 个人版
    • 创建命名空间
    • 创建镜像仓库
    • 代码源:本地代码

CI机器

  • 安装docker并设置自启动

    1
    2
    3
    curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
    systemctl enable docker
    systemctl start docker
  • 配置SSH key密钥对与服务机通信

    1
    2
    3
    ssh-keygen -t rsa -C "your_email@example.com"
    ssh-add ~/.ssh/id_rsa
    ssh-copy-id -i ~/.ssh/id_rsa.pub root@服务机IP

    这里配置是因为CICD的deploy阶段需要CIdergitlab-runner通过ssh连接来执行服务机命令。

  • 安装gitlab-runner并运行

    1
    2
    3
    4
    docker run -d --name gitlab-runner --restart always \
    -v /srv/gitlab-runner/config:/etc/gitlab-runner \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /root/.ssh:/root/.ssh \

    命令挂载了三个东西,分别是:

    • gitlab-runner的配置文件
    • 让runner里面可以执行宿主机的docker
    • 让CI机器能够免密访问服务机
  • 将runner注册到gitlab中

    1
    docker exec -it gitlab-runner gitlab-ci-multi-runner register
    • 第一步:输入gitlab URL(你们自己的gitlab地址)
    • 第二步:输入gitlab-ci token,去gitlab的CICD的配置里去copy
    • 第三步:输入runner的description、tags,其中tags会在后续的.gitlab-ci.yml中用到
    • 第四步:输入runner的执行器,选择docker
    • 第五步:选择默认的 docker 镜像
  • 编辑gitlab-runner配置文件

    配置文件挂载到宿主机了,编辑宿主机的文件

    1
    vim /srv/gitlab-runner/config/config.toml

    配置样例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    concurrent = 10
    [[runners]]
    name = "s****an"
    url = "https://co*******m/"
    token = "m********JFMHxD"
    executor = "docker"
    [runners.custom_build_dir]
    [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.docker]
    tls_verify = false
    image = "ccchieh/centos-common"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock", "/usr/bin/docker:/usr/bin/docker", "/data/.m2:/root/.m2", "/root/.ssh:/root/.ssh", "/root/.docker/:/root/.docker/"]

    这里修改的主要是concurrentvolumespull_policy

    • concurrent

      表示这个runner可以并行执行多少任务,常见的就是,同时提交多次任务或者在一次流水线中任务可以并行完成,例如在buile阶段,会同时执行下载依赖构建的操作。

      image-20210324213059916

    • volumes

      挂载的目录,就是docker run 的时候用的 -v 挂载的目录,因为我们后面需要编译Dockerfile,所以将docker的相关目录文件挂载上去,然后挂载下maven仓库(这里是直接挂载了,实际使用的时候也可以利用gitlab-ci文件里面的cache进行缓存)避免每次打包编译java项目的时候都要下载一次依赖(这里是针对maven的,其他依赖管理软件视情况而定)。最后我这里直接把宿主机的.ssh也挂载上去了,是为了后面可以操作远程部署的服务器,因为我这里都是内网机器,所以安全性暂时不考虑,这里再实际应用中请注意下,避免不必要的风险。

    • pull_policy

      表示只有当本地没镜像的时候才拉取镜像,避免每次都重新拉取镜像。

项目文件编写与gitlab设置

  • 设置项目的gitlab-runner

    在CI机器配置完成,确保之前注册的runner是available的。

  • 编写Dockerfile

    每个项目需要编写一个Dockerfile实现项目的编译打包,这里以后端微服务的项目为例,文件如下:

    1
    2
    3
    4
    5
    6
    7
    8
    # 构建一个基本的微服务打包环境,将公用模块install
    FROM ccchieh/maven3-openjdk-8-cn as prod
    ARG MY_HOME=/usr/src/app
    COPY . $MY_HOME
    WORKDIR $MY_HOME
    ENV TIME_ZONE=Asia/Shanghai
    RUN ln -snf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime && echo $TIME_ZONE > /etc/timezone
    RUN /usr/local/bin/mvn-entrypoint.sh mvn clean install
  • 设置项目的环境变量

    .gitlab.yml支持环境变量,这样可以把一些敏感信息隐藏。

    这里设置了容器名称,进行私有仓库地址,部署服务器地址,服务器中的docker-compose的文件路径。

    其中镜像仓库地址的前缀需要去阿里云的镜像仓库查看。

  • 编写.gitlab-ci.yml

    .gitlab-ci.yml文件定义了CI/CD pipeline的阶段与具体逻辑,这里我们分为四个阶段(pre-build、build、deploy、cleanup)来打包构建项目的镜像:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    stages:
    - build
    - deploy
    - cleanup

    build: # 编译阶段
    stage: build
    only:
    - master
    tags:
    - shuishan
    script:
    - docker build --build-arg MODULE_NAME=$CONT_NAME --build-arg PROFILE_NAME=dockerdev -t $IMAGE_MASTER .
    - docker push $IMAGE_MASTER


    build-prod: # 编译阶段
    stage: build
    only:
    - prod
    tags:
    - shuishan-aliyun-runner
    script:
    - |
    docker build --build-arg MODULE_NAME=$CONT_NAME --build-arg PROFILE_NAME=docker -t $IMAGE_PROD .
    docker push $IMAGE_PROD

    deploy: # 部署阶段
    stage: deploy
    only:
    - master
    tags:
    - shuishan
    script:
    - |
    ssh $TEST_SERVER_ADDR docker-compose -f ${TEST_SERVER_PATH}/docker-compose.yml stop
    ssh $TEST_SERVER_ADDR docker-compose -f ${TEST_SERVER_PATH}/docker-compose.yml rm -sf $CONT_NAME
    ssh $TEST_SERVER_ADDR docker rmi $IMAGE_MASTER
    ssh $TEST_SERVER_ADDR docker-compose -f ${TEST_SERVER_PATH}/docker-compose.yml up -d --build


    deploy-prod: # 部署阶段
    stage: deploy
    only:
    - prod
    tags:
    - shuishan-aliyun-runner
    script:
    - |
    ssh $SERVER_ADDR docker-compose -f ${SERVER_PATH}/docker-compose.yml stop
    ssh $SERVER_ADDR docker-compose -f ${SERVER_PATH}/docker-compose.yml rm -sf $CONT_NAME
    ssh $SERVER_ADDR docker rmi $IMAGE_PROD
    ssh $SERVER_ADDR docker-compose -f ${SERVER_PATH}/docker-compose.yml up -d --build


    cleanup: # 清理作业
    stage: cleanup
    only:
    - master
    - prod
    tags:
    - shuishan
    script:
    - echo "回收垃圾"
    # - docker system prune -f # 删除docker中间无用镜像
    when: always

服务机

  • 连接

  • 安装docker自启

  • 安装docker-compose

  • 安装git

  • 编写docker-compose.yml

    Docker Compose默认的模板文件是docker-compose.yml,我们可以通过编在服务机上编写docker-compose.yml文件,将繁琐的docker命令整合成一个文件,最后通过一行命令来统一执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    version: "3"
    services:
    nacos:
    container_name: nacos-standalone
    image: nacos/nacos-server:latest
    environment:
    - PREFER_HOST_MODE=hostname
    - MODE=standalone
    restart: always
    volumes:
    - ./nacos/standalone-logs/:/home/nacos/logs
    - ./nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties
    ports:
    - "8848:8848"
    networks:
    default:
    aliases:
    - nacos
    redis:
    container_name: metasequoia-redis
    restart: always
    image: "redis:alpine"
    networks:
    default:
    aliases:
    - redis
    # 后端
    bx-gateway-zuul:
    container_name: bx-gateway-zuul
    image: registry.cn-shanghai.aliyuncs.com/shuishan-data/shuishan-gateway-server:master
    restart: always
    volumes:
    - ./bx-gateway-zuul-logs/:/logs/
    ports:
    - "8080:8080"
    networks:
    default:
    aliases:
    - api # 设置api服务的别名
    environment:
    - NACOS_ADDR=nacos
    - NACOS_PORT=8848
    user-center:
    container_name: user-center
    image: registry.cn-shanghai.aliyuncs.com/shuishan-data/shuishan-usercenter-server:master
    restart: always
    volumes:
    - ./user-center-logs/:/logs/
    environment:
    - NACOS_ADDR=nacos
    - NACOS_PORT=8848
    networks:
    default:
    aliases:
    - usercenter
    shuishan-datav-server:
    container_name: shuishan-datav-server
    image: registry.cn-shanghai.aliyuncs.com/shuishan-data/shuishan-datav-server:master
    restart: always
    volumes:
    - ./shuishan-datav-server-logs/:/logs/
    - ./data/:/root/data/
    environment:
    - NACOS_ADDR=nacos
    - NACOS_PORT=8848

    这里我们需要打包构建的服务镜像包括:nacos服务发现镜像、redis服务镜像、网关服务镜像、用户鉴权服务镜像、子服务镜像(shuishan-datav-server)。

  • 构建项目的容器镜像

    进入到包含docker-compose.yml的目录,执行:

    1
    docker-compose -f ./docker-compose.yml up -d --build

    该命令可以自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

3.Dockerfile+docker compose

前因后果

项目本身前后端分工比较明确,前端同学前期通过mock模拟数据进行页面开发,可以做到和后端完全解耦。但是最后总是要把mock的数据换到remote,这不得不进行一些前后端联调的测试。作为后端管理人员,每次需要在发现问题后,将项目重新打包部署在服务器,是一件比较繁琐的事情,这时候CICD(持续交付持续集成)的需求就应运而生。

OK,思路是先把后端打包成docker镜像,然后去写配置。项目一开始是把nacos也打成一个jar包去运行,但是放到服务器就各种各样的问题。第一个想到的仍然是docker,官网查阅,果然有,运行也是如丝般顺滑。强烈安利Docker啊啊啊啊,一定要去用!!!

image-20200812092918803

以下内容来自菜鸟教程

我也不知道为啥要跟着敲一遍,可能印象更深刻

Dockerfile

Dockerfile是一个用来构建镜像的文本文件,文本文件包含了构建镜像需要的指令和说明。

使用dockerfile定制镜像

  • 定制一个nginx镜像

    1
    2
    FROM nginx
    RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html

    image-20200812094233894

  • from和run指令的作用

    • FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。

    • RUN:用于执行后面跟着的命令行命令。有以下俩种格式:

      • shell格式

        1
        2
        RUN <命令行命令>
        # <命令行命令> 等同于,在终端操作的 shell 命令。
      • exec格式

        1
        2
        3
        RUN ["可执行文件","参数1","参数2"]
        # RUN ["java", "-jar", "-Xmx1024m", "/usr/src/kfcoding-gateway.jar", "--spring.profiles.active=prod"]
        等价于 RUN java -jar -Xmx1024m /user/src/kfcoding-gateway.jar --spring.profiles.active=prod
      • attention

        dockerfile的指令每执行一次都会在docker上新建一层,过多无意义的层会造成镜像膨胀,例如:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        FROM centos
        RUN yum install wget
        RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
        RUN tar -xvf redis.tar.gz
        以上执行会创建 3 层镜像。可简化为以下格式:
        FROM centos
        RUN yum install wget \
        && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
        && tar -xvf redis.tar.gz
        使用&&只会创建一层镜像

开始构建镜像

在dockerfile文件存放的目录下,执行构建一个nginx:test(镜像名称:镜像标签),最后的.表示上下文路径

1
docker build -t nginx:test .

通过docker images查看镜像

上下文路径

上下文路径,是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包

解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。

如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。

注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

指令详解

  • COPY

    复制指令,从上下文目录中复制文件或者目录到容器里指定路径。格式:

    1
    2
    COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
    COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

    **[–chown=:]**:可选参数,用户改变复制到容器内文件的拥有者和属组。

    **<源路径>**:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。例如:

    1
    2
    COPY hom* /mydir/
    COPY hom?.txt /mydir/

    **<目标路径>**:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。

  • ADD

    ADD 指令和 COPY 的使用格式一致(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

    • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
    • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
  • CMD

    类似于RUN指令,运行的时间点不同

    • CMD在docker run 时候运行
    • RUN在docker build

    作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

    注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。格式:

    1
    2
    3
    CMD <shell 命令> 
    CMD ["<可执行文件或命令>","<param1>","<param2>",...]
    CMD ["<param1>","<param2>",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

    推荐第二种格式,执行过程比较明确、第一种的可执行文件是sh。

  • ENTRYPOINT

    类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

    但是, 如果运行 docker run 时使用了 –entrypoint 选项,此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序。

    优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

    注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。格式:

    1
    ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

    可以搭配CMD使用:一般变参使用CMD,这里的CMD等于是给ENTRYPOINT 传参,示例:

    假设构建了nginx:test镜像:

    1
    2
    3
    FROM nginx
    ENTRYPOINT ["nginx", "-c"] # 定参
    CMD ["/etc/nginx/nginx.conf"] # 变参
    • 不传参运行

      1
      $ docker run nginx:test

      容器默认运行一下命令,启动主进程

      1
      nginx -c /etc/nginx/nginx.conf
    • 传参运行

      1
      $ docker run  nginx:test -c /etc/nginx/new.conf

      容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

      1
      nginx -c /etc/nginx/new.conf
  • ENV

    设置环境变量,后续指令可以使用,格式:

    1
    2
    ENV <key> <value>
    ENV <key1>=<value> <key2>=<value2>...

    设置NODE_VERSION=7.2.0,之后通过$NODE_VERSION引用:

    1
    2
    3
    ENV NODE_VERSION 7.2.0
    RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
    && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
  • **ARG **

    构建参数,与 ENV 作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

    构建命令 docker build 中可以用 –build-arg <参数名>=<值> 来覆盖。格式:

    1
    ARG <参数名>[=<默认值>]
  • VOLUME

    定义匿名数据卷。启动容器忘记挂载数据卷,会自动挂载到匿名数据卷

    作用

    • 避免重要的数据,因为容器重启而丢失
    • 避免容器不断变大

    格式:

    1
    2
    VOLUME ["<路径1>", "<路径2>"...]
    VOLUME <路径>

    zai docker run的时候,可以通过-v参数修改挂载点

  • EXPOSE

    声明端口

    • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
    • 运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

    格式:

    1
    EXPOSE <端口1> [<端口2>...]
  • WORKDIR

    指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。

    docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

    格式:

    1
    WORKDIR <工作目录路径>
  • USER

    用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

    格式:

    1
    USER <用户名>[:<用户组>]
  • HEALTHEHECK

    用于指定某个程序或者指令来监控docker容器服务的运行状态

    格式:

    1
    2
    3
    4
    HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
    HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令

    HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
  • ONBUILD

    用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这是执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

    格式:

    1
    ONBUILD <其它指令>

    Docker Componse

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

Componse使用的三个步骤:

  • 使用dockerfile定义应用程序的环境
  • 使用docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
  • 最后,执行 docker-compose up 命令来启动并运行整个应用程序。

docker-componse.yml的配置案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# yaml 配置实例
version: '3'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}

使用

  • 准备

    1
    2
    3
    mkdir composetest
    cd composetest
    vi ap.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import time

    import redis
    from flask import Flask

    app = Flask(__name__)
    cache = redis.Redis(host='redis', port=6379)


    def get_hit_count():
    retries = 5
    while True:
    try:
    return cache.incr('hits')
    except redis.exceptions.ConnectionError as exc:
    if retries == 0:
    raise exc
    retries -= 1
    time.sleep(0.5)


    @app.route('/')
    def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)

    在示例中,redis是应用程序网络上的redis容器的主机名,port = 6379。

    1
    vi requirements.txt
    1
    2
    flask
    redis
  • 创建Dockerfile文件

    1
    vi Dockerfile
    1
    2
    3
    4
    5
    6
    7
    8
    9
    FROM python:3.7-alpine
    WORKDIR /code
    ENV FLASK_APP app.py
    ENV FLASK_RUN_HOST 0.0.0.0
    RUN apk add --no-cache gcc musl-dev linux-headers
    COPY requirements.txt requirements.txt
    RUN pip install -r requirements.txt
    COPY . .
    CMD ["flask", "run"]

    内容解释:

    • FROM python:3.7-alpine:从Python 3.7 映像开始构建镜像。
    • WORKDIR /code:将工作目录设置为 /code。
    • ENV:设置flask使用的环境变量
    • 5:安装gcc
    • 6、7:复制requirements.txt并安装依赖
    • 8: 将 . 项目中的当前目录复制到 . 镜像中的工作目录。
    • 9:容器提供默认的执行命令为:flask run。
  • 创建docker-compose.yml

    1
    vi docker-compose.yml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # yaml 配置
    version: '3'
    services:
    web:
    build: .
    ports:
    - "5000:5000"
    redis:
    image: "redis:alpine"

    定义了两个服务:web和redis

    • web:该 web 服务使用从 Dockerfile 当前目录中构建的镜像。然后,它将容器和主机绑定到暴露的端口 5000。此示例服务使用 Flask Web 服务器的默认端口 5000 。
    • redis:该 redis 服务使用 Docker Hub 的公共 Redis 映像。
  • 使用compose命令构建和运行应用

    1
    docker-compose up -d
    1
    2
    # 后台运行
    docker-compose up -d

yml配置指令参考

  • version

    指定本 yml 依从的 compose 哪个版本制定的。

  • build

    指定构建镜像上下文路径:

    例如webapp服务,指定为上下文路径./dir/Dockerfile所构建的镜像

    1
    2
    3
    4
    version:"3.7"
    services:
    webapp:
    build: ./dir

    或者,作为具有在上下文指定的路径的对象,以及可选的Dockerfile和args

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    version: "3.7"
    services:
    webapp:
    build:
    context: ./dir
    dockerfile: Dockerfile-alternate
    args:
    buildno: 1
    labels:
    - "com.example.description=Accounting webapp"
    - "com.example.department=Finance"
    - "com.example.label-with-empty-value"
    target: prod
    • content:上下文路径
    • dockerfile:添加构建镜像的dockerfile文件名
    • args:添加构建参数,这是只能在构建过程中访问的环境变量。
    • labels:设置构建镜像的标签。
    • target:多层构建,可以指定构建哪一层。
  • cap_add,cap_drop

    添加或删除容器拥有的宿主机的内核功能

    1
    2
    3
    4
    5
    cap_add:
    - ALL # 开启全部权限

    cap_drop:
    - SYS_PTRACE # 关闭 ptrace权限
  • cgroup_parent

    为容器指定父cgroup组,继承该组的资源限制

    1
    cgroup_parent: m-executor-abcd
  • command

    覆盖容器启动的默认命令

    1
    command: ["bundle", "exec", "thin", "-p", "3000"]
  • container_name

    指定自定义容器名称,而不是生成的默认名称

    1
    container_name: my-web-container
  • depends_on

    设置依赖关系

    • docker-compose up :以依赖性顺序启动服务。在以下示例中,先启动 db 和 redis ,才会启动 web。
    • docker-compose up SERVICE :自动包含 SERVICE 的依赖项。在以下示例中,docker-compose up web 还将创建并启动 db 和 redis。
    • docker-compose stop :按依赖关系顺序停止服务。在以下示例中,web 在 db 和 redis 之前停止。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    version: "3.7"
    services:
    web:
    build: .
    depends_on:
    - db
    - redis
    redis:
    image: redis
    db:
    image: postgres

    web服务不会等redis db完全启动之后才启动

  • deploy

    指定与服务的部署和运行有关的配置。只在swarm模式下生效

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    version: "3.7"
    services:
    redis:
    image: redis:alpine
    deploy:
    mode:replicated
    replicas: 6
    endpoint_mode: dnsrr
    labels:
    description: "This redis service label"
    resources:
    limits:
    cpus: '0.50'
    memory: 50M
    reservations:
    cpus: '0.25'
    memory: 20M
    restart_policy:
    condition: on-failure
    delay: 5s
    max_attempts: 3
    window: 120s

    可选参数:

    • endpoint_mode:vip:访问集群服务的方式

      1
      2
      3
      4
      endpoint_mode: vip 
      # Docker 集群服务一个对外的虚拟 ip。所有的请求都会通过这个虚拟 ip 到达集群服务内部的机器。
      endpoint_mode: dnsrr
      # DNS 轮询(DNSRR)。所有的请求会自动轮询获取到集群 ip 列表中的一个 ip 地址。
    • labels:在服务上设置标签。可以用容器上的 labels(跟 deploy 同级的配置) 覆盖 deploy 下的 labels。

    • model:指定服务提供的模式

      • replicated:复制服务。复制制定服务到集群的机器上。

      • global:全局服务,服务将部署到集群的每个节点上。

      • 下图中黄色的方块是 replicated 模式的运行情况,灰色方块是 global 模式的运行情况。

        image-20200812203843354
    • replicas:mode是replicated时,需要此参数配置具体运行的节点数量

    • resources:配置服务器资源使用的限制

    • restart_policy:配置如何在退出容器时重新启动容器。

    • rollback_config:配置在更新失败的情况下应如何回滚服务。

    • update_config:配置应如何更新服务,对于配置滚动更新很有用。

  • devices:指定设备映射列表

    1
    2
    devices:
    - "/dev/ttyUSB:/dev/ttyUSB0"
  • dns:自定义DNS服务器

    1
    2
    3
    4
    5
    dns: 8.8.8.8

    dns:
    - 8.8.8.8
    - 9.9.9.9
  • dns_search:自定义 DNS 搜索域。可以是单个值或列表。

    1
    2
    3
    4
    5
    dns_search: example.com

    dns_search:
    - dc1.example.com
    - dc2.example.com
  • entrypoint:覆盖默认容器的entrypoint

    1
    entrypoint:/code/entrypoint.sh

    也可以是以下格式

    1
    2
    3
    4
    5
    6
    7
    entrypoint:
    - php
    - -d
    - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so
    - -d
    - memory_limit=-1
    - vendor/bin/phpunit
  • env_file:从文件添加环境变量

    1
    env_file: .env
    1
    2
    3
    4
    env_file:
    - ./common.env
    - ./apps/web.env
    - /opt/secrets.env
  • environment:添加环境变量。

    1
    2
    3
    environment:
    RACK_ENV: development
    SHOW: 'true'
  • expose:暴露端口,但不映射到宿主机,仅可以指定内部端口为参数

    1
    2
    3
    expose:
    - "3000"
    - "8000"
  • extra_hosts:添加映射名

    1
    2
    3
    extra_hosts:
    - "somehost:162.242.195.82"
    - "otherhost:50.31.209.229"

    以上会在此服务的内部容器中 /etc/hosts 创建一个具有 ip 地址和主机名的映射关系:

    1
    2
    162.242.195.82  somehost
    50.31.209.229 otherhost
  • healthcheck:用于检测 docker 服务是否健康运行。

    1
    2
    3
    4
    5
    6
    healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost"] # 设置检测程序
    interval: 1m30s # 设置检测间隔
    timeout: 10s # 设置检测超时时间
    retries: 3 # 设置重试次数
    start_period: 40s # 启动后,多少秒开始启动检测程序
  • image:指定容器运行的镜像

    1
    2
    3
    4
    5
    image: redis
    image: ubuntu:14.04
    image: tutum/influxdb
    image: example-registry.com:4000/postgresql
    image: a4bc65fd # 镜像id
  • logging:服务的日志记录

    driver:指定服务容器的日志记录驱动程序,默认值为json-file。有以下三个选项

    1
    2
    3
    driver: "json-file"
    driver: "syslog"
    driver: "none"

    仅在 json-file 驱动程序下,可以使用以下参数,限制日志得数量和大小。

    1
    2
    3
    4
    5
    logging:
    driver: json-file
    options:
    max-size: "200k" # 单个文件大小为200k
    max-file: "10" # 最多10个文件

    当达到文件限制上限,会自动删除旧得文件。

    syslog 驱动程序下,可以使用 syslog-address 指定日志接收地址。

    1
    2
    3
    4
    logging:
    driver: syslog
    options:
    syslog-address: "tcp://192.168.0.42:123"
  • network_mode:设置网络模式

  • restart

    1
    2
    3
    4
    restart: "no"
    restart: always
    restart: on-failure
    restart: unless-stopped
  • secrets:存储敏感数据,例如密码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    version: "3.1"
    services:

    mysql:
    image: mysql
    environment:
    MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my_secret
    secrets:
    - my_secret

    secrets:
    my_secret:
    file: ./my_secret.txt
  • security_opt:修改容器默认的schema标签

    1
    2
    3
    4
    5
    security-opt:
    - label:user:USER # 设置容器的用户标签
    - label:role:ROLE # 设置容器的角色标签
    - label:type:TYPE # 设置容器的安全策略标签
    - label:level:LEVEL # 设置容器的安全等级标签
  • stop_grace_period:指定在容器无法处理 SIGTERM (或者任何 stop_signal 的信号),等待多久后发送 SIGKILL 信号关闭容器。

    1
    2
    stop_grace_period: 1s # 等待 1 秒
    stop_grace_period: 1m30s # 等待 1 分 30 秒

    默认10s

  • stop_signal

  • sysctls

  • tmpfs

  • ulimits

  • volumes

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

请我喝杯咖啡吧~

支付宝
微信