美团点评业务之技术解密,日均请求数十亿次的容器平台 - infoq

转载自 InfoQ

本文介绍美团点评的 Docker 容器集群管理平台(以下简称容器平台)。该平台始于 2015 年,基于美团云的基础架构和组件而开发的 Docker 容器集群管理平台。目前该平台为美团点评的外卖、酒店、到店、猫眼等十几个事业部提供容器计算服务,承载线上业务数百个,容器实例超过 3 万个,日均线上请求超过 45 亿次,业务类型涵盖 Web、数据库、缓存、消息队列等等。

容器到来之前

在容器平台实施之前,美团点评的所有业务都是运行在美团私有云提供的虚拟机之上。美团点评大部分的线上业务都是面向消费者和商家的,业务类型多样,弹性的时间、频度也不尽相同,这对弹性服务提出了很高的要求。

在这一点上,虚拟机已经难以满足需求,主要体现以下两点:

  • 虚拟机弹性能力较弱,部署效率低,认为干预较多,可靠性差。

  • IT 成本高。由于虚拟机弹性能力较弱,业务部门为了应对流量高峰和突发流量,普遍采用预留大量机器和服务实例的做法。资源没有得到充分使用产生浪费。

容器时代

架构设计

美团点评将容器管理平台视作一种云计算模式,因此云计算的架构同样适用于容器。

如前所述,容器平台的架构依托于美团私有云现有架构,其中私有云的大部分组件可以直接复用或者经过少量扩展开发,如图所示。


360 的容器化之路

【编者的话】容器化技术作为“搅局者”,势必面临适配公司已有架构的挑战,本文将为大家介绍360如何让Docker落地。主要包括三方面内容:一,结合公司业务特点,如何使Docker适配现有技术架构 ,完成线上环境快速部署扩容;二,“让产品失败的更廉价”,使用Docker构建PaaS环境加速中小业务快速孵化上线;三,使用Docker技术,在构建持续集成环境方面的一些积累。

容器化技术作为“搅局者”,势必面临适配公司已有架构的挑战,本文将为大家介绍360如何让Docker落地。主要包括三方面内容:一,结合公司业务特点,如何使Docker适配现有技术架构 ,完成线上环境快速部署扩容;二,“让产品失败的更廉价”,使用Docker构建PaaS环境加速中小业务快速孵化上线;三,使用Docker技术,在构建持续集成环境方面的一些积累。

以Docker为主的容器化技术现在可谓风生水起,大家都觉得它可能会颠覆整个IT格局。我们刚开始接触到Docker的时候也觉得它非常好,有很多优点吸引我们。因为它的颠覆性我们称它为“搅局者”。

改造“搅局者”Docker

我们先来看看这位搅局者的优点:

  1. Namespace、CGroups虚拟化, 相比传统虚拟化会有更好性能,反映在生产环境中就是能更大程度的利用资源。
  2. 启动速度快,虚拟机最快也得30秒-1分钟,它的启动创建都是秒级。
  3. 镜像分层技术,解决了快速变更环境的问题。

这些优点很吸引我们,我们非常希望把它用在生产环境中,但是我们发现理想很美好,现实很残酷。我们之前基础架构都是使用传统虚拟机化技术就是虚拟机。我们要使用Docker就会面临这几个问题 :

  1. 不能SSH,紧急问题怎么排查?
  2. 怎么监控?
  3. 基础服务如何对接?
  4. 最重要的问题: 这东西稳定么,线上业务当然不能出问题。

所以,在应用Docker的时候,我们犯了犹豫,因为按照它推荐的方式,我们无法直接立马就在线上业务使用。因为Docker本身也对业务的架构设计有一定要求,比如我们常说的容器无状态,容器中不要留中间数据。我们发现公司的业务架构改造起来困难很大,涉及到方方面面,所以我们决定要Docker去适应公司的架构。

接下来我们就是要解决Docker技术”落地”的问题。

我们对Docker改造点主要有:

  1. 容器内部绑定独立IP。
  2. 容器内部开启多进程服务。
  3. 自动添加监控。
  4. CPU配额硬限制。
  5. 容器绑定独立IP这样外部可直接SSH了。

我们考虑在容器内部运行多个进程服务,因为默认容器只开启一个进程,这无法满足我们要求,所以我们大胆进行了改造。我们甚至在镜像里实现了chkconfig让以前的RPM包原生可用。

自动添加监控让创建的容器自动添加到Zabbix中。CPU配额硬限制 Docker 1.7版本已经支持了,我们在这之前自己实现了一套。

改造Docker支持这些功能后,我们又开发了一套调度系统,负责管理调度在集群上如何创建容器,我们也调研了一些开源的调度系统,发现都不满足需求,所以自己开发了一套。

通过这些手段我们就可以让Docker技术“落地”了,而带来的好处是,之前的体系我们要上线新的业务大约需要40分钟,使用Docker缩短到了5分钟。

这是分享的第一部分因为“搅局者”Docker使用遇到了困境,所以我们对它进行了一些改造,更好适配公司场景,让技术“落地”。

基于Docker做一个内部PaaS平台

紧接着我们基于Docker做了一个内部PaaS平台。公司每天会上线很多业务,这些业务有些是体量很大的重要业务,有些是带有试错性质的小业务。

传统业务上线的步骤会非常得严谨,流程会比较长,这些流程其实也对业务稳定性会有保障。有些试错性质的小业务,使用同样的流程变得不太合适,所以我们就想加速小业务上线流程,让他们可以快速上线,验证自己得价值。基于这种考虑,而且Docker天生的特点就特别适合干这个。

这是界面的一个截图,主要是前端Web UI去访问一个调度层 ,调度层通过调用Docker API来创建容器。目前PaaS平台支持PHP、Node.js、Python、Java等语言。

除了创建容器,我们还需要,创建Git仓库、配置访问代理等,总之研发一键就可以让业务进入待上线状态,只要他传完代码就可以上线了。

目前这个平台跑了300+业务,让很多研发只要有一个idea,就可以快速实施上线,很受他们欢迎。

这也是我们应用Docker的第二部分,通过私有PaaS平台,加速业务孵化。

关于持续集成

第三部分是关于持续集成。

持续集成当然是Docker最纯粹的玩法了,通过『Dockerfile-构建镜像-创建新容器』来完成环境的变更。

这块比较复杂,我们大致分了9个模块,比如调度模块、监控模块、存储模块等。

首先我们做了一个配置转换模块来转换Dockerfile,这样即可以统一镜像构建标准,同时也降低了编写Dockerfile的学习成本。

调度模块就直接用的Mesos和Marathon,镜像Registry直接使用了 Registry V2因为它性能更好对高并发支持也很好,最后是镜像构建模块,使用的是Jenkins CI。

但是我们发现一个问题:镜像构建在高并发下其实并不快。 比如装一个RPM包,SSH肯定会比重新build快得多。所以我们做了很多优化在镜像构建这一块,现在结果是100个任务同时构建我们也能达到和传统集群管理如Puppet一样的效率。

Q&A

问:开发自己的调度系统大概花了多久,有遇到特别的技术难点吗? 答:大约2个工程师一个月样子,没有太多得困难。因为调度逻辑比较简单。

问:您刚才说,通过绑定独立的IP就可以直接使用SSH了。 答:通过绑定独立的IP就可以直接使用SSH了,官方关于network那篇文档有介绍实现方案。

问:一般Docker的服务封装是no daemon的,这时如果重启服务,容器也会退出的,如何debug? 答:可使用supvirsod或者monit等将no daemon封装一下。

问:你们服务的注册发现用的是什么? 答:我们基础架构组开发了一个,名字叫QConf,已经开源在GitHub上

问:你说Zabbix做的一个容器监控,那有没有一个基于宿主机的监控方式?因为据我所知你这样的话每个容器都要运行一个代理吧。 答:我们就是使用宿主机里安装Zabbix代理,通过Zabbix自发现来动态获取容器列表,再基于自定义的监控脚本获得每个容器的监控数值。

问:你们的那些业务跑在Docker里了? 答:目前360的很多Web2.0业务已经跑在了上面,像影视、新闻、免费WIFI等。

问:Docker建议无状态,那么是否意味着不建议存放数据?比如MySQL,还是说通过-v来解决? 答: 这其实是数据存储问题,你可以使用分布式存储来存储数据,只要数据和逻辑分离容器就无状态了。

问:Docker建议无状态,那么是否意味着不建议存放数据?比如MySQL,还是说通过-v来解决? 答:我理解就是容器无状态就是基于镜像创建的马上就能线上使用。

问:线上Docker的稳定性如何? 答:目前运行都很稳定,没有出现容器异常崩溃等情况。

问:Container中跑多个进程,那么PID为1的进程你们是由什么控制的,直接由对应的应用程序还是其他什么? 答:之前用了supervisord 现在使用S6。

问:Registry面对大量的并发,有测试出大致的性能占比吗,整个registry是mirror还是其他架构? 答:Registry目前我们更新到了V2,我们测试V2在高并发pull和push上性能非常好,镜像存储使用共享存储,这样Registry也可以横向扩展。

问:如果容器配置用户可以直接访问的IP,在宿主虚拟机中是否可以基于Open vSwitch实现,否则会太依赖虚拟机网络? 答:这个可以的,实际上我们也测试过没问题,当时基于稳定性考虑没有使用。

问:奇虎的CPU配额管理是如何实现的? 答:这个Docker 1.7已经实现了,我们和官方的实现思路是一致的。

问:关于容器中数据存储是怎么做的,如果是共享存储如何进行对应? 答: 可以试试GlusterFS或者Ceph。

问:容器绑IP,容器重启后IP要重新绑吧,IP会变吗? 答:需要重新绑,可做成自动化脚本。


Linux 下的 TCPing

以前写了一篇 《禁ping也能ping的工具:tcping》,介绍了由 Eli Fulkerson 编写的 Windows 下的 tcping 这个工具, ping 那些不允许 ping 的主机。

今天介绍 Linux 下的 TCPing,来自 richard。

什么是 TCPing

tcping 是模仿 icmp 协议下的 ping 命令,不一样的是 tcping 走的是 tcp 协议。所以 TCPing 还支持监听具体某个端口的状态。

因此,即使服务器禁止 Ping,也可以通过 TCPing 来测试与服务器的连接。

安装

使用 richard 的 tcpping 首先要安装好 tcptraceroute:

sudo apt-get install tcptraceroute bc
cd /usr/local/bin
sudo wget http://www.vdberg.org/~richard/tcpping
sudo chmod 755 tcpping

我给 tcping 也做了一个备份:https://cdn.kelu.org/blog/2017/11/tcpping,可以在我网站下载。

用法

tcpping v1.7 Richard van den Berg richard@vdberg.org

Usage: tcpping [-d] [-c] [-C] [-w sec] [-q num] [-x count] ipaddress [port]

    -d   print timestamp before every result
    -c   print a columned result line
    -C   print in the same format as fping's -C option
    -w   wait time in seconds (defaults to 3)
    -r   repeat every n seconds (defaults to 1)
    -x   repeat n times (defaults to unlimited)

也可以使用man tcptraceroute查看用法。

例如:

tcpping xxx.com

tcpping xxx.com 12345

参考资料


docker compose 起步

昨天写完了 Dockerfile,使用 Docker run 将容器启动起来了。今天使用 docker compose 的方式将它启动起来。先前转载过一篇教程,也可以看一看:《Docker Compose工具快速入门

安装

Compose 目前支持 Linux、macOS、Windows 10 三大平台。 Compose 可以通过 Python 的包管理工具 pip 进行安装,也可以直接下载编译好的二进制文件使用,或在 Docker 容器中运行。

用 pip 安装比较简单:

 pip install -U docker-compose

即可开始使用。

卸载的话使用如下命令:

pip uninstall docker-compose

编写yaml模板文件

接上一篇的dockerfile

模板文件涉及的命令还蛮多的,不过大部分指令跟 docker run 相关参数的含义都是类似的。

来看我编写的 docker-compose.yml。

version: '3'    
services:
  nginx:	  # 镜像名称
    build: .  # 指定 `Dockerfile` 所在文件夹的路径,进行镜像编译。
    ports:    # 暴露端口信息。
     - "18080:80"
    volumes:  # 数据卷所挂载路径设置。
     - /var/local/nginx:/var/local/nginx/conf/vhost
     - /var/local/log/nginx:/var/local/log/nginx
  redis:      # 做个样子顺便写的
    image: "redis:alpine"   # 镜像

这里解释一下常用的这些命令:

  • build

    自动构建这个镜像,然后使用这个镜像

  • ports

    暴露端口信息。

    使用 (HOST:CONTAINER)格式,或者仅仅指定容器的端口(宿主将机端口)都可以。

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

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

    该指令中路径支持相对路径。例如

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

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

    例如:

      image: ubuntu
      image: orchardup/postgresql
      image: a4bc65fd
    
  • command

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

      command: echo "hello world"
    

指令列表如下:

更详细的指令解说可以查看这个文档https://yeasy.gitbooks.io/docker_practice/content/compose/yaml_file.html

运行 docker compose

写好 docker-compose.yml 后就是运行了。Compose 命令的基本的使用格式是

docker-compose [-f=<arg>...] [options] [COMMAND] [ARGS...]

命令选项

  • -f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。
  • -p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。
  • --x-networking 使用 Docker 的可拔插网络后端特性(需要 Docker 1.9 及以后版本)。
  • --x-network-driver DRIVER 指定网络后端的驱动,默认为 bridge(需要 Docker 1.9 及以后版本)。
  • --verbose 输出更多调试信息。
  • -v, --version 打印版本并退出。

也解释一下常用命令:

up

格式为 docker-compose up [options] [SERVICE...]

该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

链接的服务都将会被自动启动,除非已经处于运行状态。

可以说,大部分时候都可以直接通过该命令来启动一个项目。

默认情况,docker-compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。

当通过 Ctrl-C 停止命令时,所有容器将会停止。

如果使用 docker-compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。

默认情况,如果服务容器已经存在,docker-compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker-compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker-compose up --no-deps -d <SERVICE_NAME> 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。

选项:

  • -d 在后台运行服务容器。
  • --no-color 不使用颜色来区分不同的服务的控制台输出。
  • --no-deps 不启动服务所链接的容器。
  • --force-recreate 强制重新创建容器,不能与 --no-recreate 同时使用。
  • --no-recreate 如果容器已经存在了,则不重新创建,不能与 --force-recreate 同时使用。
  • --no-build 不自动构建缺失的服务镜像。
  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。

build

格式为 docker-compose build [options] [SERVICE...]

构建(重新构建)项目中的服务容器。

服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db。

可以随时在项目目录下运行 docker-compose build 来重新构建服务。

选项包括:

  • --force-rm 删除构建过程中的临时容器。
  • --no-cache 构建镜像过程中不使用 cache(这将加长构建过程)。
  • --pull 始终尝试通过 pull 来获取更新版本的镜像。

logs

格式为 docker-compose logs [options] [SERVICE...]

查看服务容器的输出。默认情况下,docker-compose 将对不同的服务输出使用不同的颜色来区分。可以通过 --no-color 来关闭颜色。

ps

格式为 docker-compose ps [options] [SERVICE...]

列出项目中目前的所有容器。

选项:

  • -q 只打印容器的 ID 信息。

更详细的指令解说可以查看这个文档https://yeasy.gitbooks.io/docker_practice/content/compose/commands.html


dockerfile 实践

看过了这么多的教程,我仍然写不好一个 Dockerfile 文件233333333。主要原因还是没有自己动手实践,把自己的项目一行一行敲出来做成一个镜像。

在这篇文档中,我将在我 GitHub 上的项目 —— KeluLinuxKit 作为基础,对照其中的命令移植到 Dockerfile 里来。

准备


先看一下我原生的脚本:

install_openresty(){
    cd $DOWNLOAD
    aptitude -y install libreadline-dev libpcre3-dev libssl-dev libpq-dev
    wget https://openresty.org/download/ngx_openresty-1.9.7.1.tar.gz
    tar -xzvf ngx_openresty-1.9.7.1.tar.gz
    cd ngx_openresty-1.9.7.1/
    ./configure --prefix=/usr/share/openresty --with-pcre-jit --with-http_postgres_module --with-http_iconv_module --with-http_stub_status_module
    make && make install

    mkdir /var/local/nginx
    mkdir /var/local/log/nginx
    cp -R $NGINX_HOME /var/local
    cd /var/local/nginx
    mkdir conf/vhost

    cp $RESOURCE/nginx/* /var/local/nginx/
}

这部分命令行主要做的是 openresty 的下载、编译然后拷贝现有的配置,其中涉及的命令行的知识就不做解读了。

以下这个部分会非常长,嫌麻烦的可以直接跳到最后看结果。

dockerfile 介绍


接触过容器的朋友应该都知道,Dockerfile是由一系列命令和参数构成的脚本,使用 Dockerfile 可以创建容器的镜像。

一个Dockerfile里面包含了构建一个容器镜像的完整命令。Docker通过docker build执行Dockerfile中的一系列命令自动构建image

首先,我使用一个简单的例子介绍一下 dockerfile。 一个标准最小化的 dockerfile 格式应该是这样子的:

FROM nginx
MAINTAINER kelvinblood <xxx@xxx.org>
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
ENTRYPOINT ["nginx", "-g", "daemon off;"]

它包含四个部分:

  • 基础镜像(父镜像)信息
  • 维护者信息
  • 镜像操作命令
  • 容器启动命令

完成之后,我们只需要简单的的使用 docker build . 命令,就可以将它制作出来了。

以下介绍 Dockerfile 的常用命令:

  • build
  • FROM
  • MAINTAINER
  • USER
  • ENV
  • ARG
  • RUN
  • ADD
  • COPY
  • LABLE
  • ONBUILD
  • STOPSIGNAL
  • HEALTHCHECK
  • SHELL
  • CMD
  • EXPOSE
  • ENTRYPOINT
  • VOLUME
  • WORKDIR

build


通过上面的介绍,我们对 dockerfile 已经有了一个大概的认识,现在先来看如何制作镜像。

docker build CONTENT

docker build命令从Dockerfilecontext中构建image。context可以使是PATHURLPATH是本地文件目录。 URL则是 Git 仓库的位置。

context以递归方式处理。所以PATH会包括目录下的所有子目录,URL会包括仓库和仓库的子模块。下面是一个使用当前目录作为context的构建镜像的命令:

$ docker build .
Sending build context to Docker daemon  6.51 MB
...

构建由Docker 守护程序运行,而不是由 Docker 命令行运行。在这里放上一个 Docker 的架构图帮助理解。关于Docker守护进程和命令行的内容在本文就不展开了。

构建过程所做的第一件事是将整个context递归地发送给守护进程。一个比较好的做法是将Dockerfile和所需文件复制到一个空的目录,再到这个目录进行构建。(尤其注意不要把根目录这种传过去)

以下是一些与 build 相关的小知识点:

  • 可以通过.dockerignore 来排除 content 中的文件和目录。这样能提高构建的性能。
  • 一般默认Dockerfile位于context的根目录下。也可以使用-f标志可指定Dockerfile的位置。

      $ docker build -f /path/to/a/Dockerfile .
    	
    
  • 用 -t 保存为新的镜像,也可以保存成多个镜像:

      $ docker build -t shykes/myapp .
      $ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
    
  • Docker守护程序会按顺序运行Dockerfile中的指令。
  • Docker 是根据 dockerfile 中的每一个指令进行镜像构建。在 dockerfile 中运行RUN cd /tmp对下一个指令不会有任何影响。Docker 每个指令都会产生中间镜像,以此来加速docker build的过程。

Dockerfile 格式


以下四个部分的命令分类,是我按照平时各个命令常见的位置来划分的,并没有实际意义。

第一部分 基础镜像(父镜像)信息


解析器指令

用来做一些解析的设置,目前只支持 escape。

有一些注意事项,例如必须置于 From 之前,不能重复,不能换行,未知指令视为注释。

  • escape

    用于在Dockerfile中转义字符的字符。如果未指定,则缺省转义字符为\

      # escape=\ (backslash)
    

      # escape=` (backtick)
    

FROM

FROM <image> 
FROM <image>:<tag>
FROM <image>@<digest>
  • 除了注释,from 必须作为第一个命令。
  • 可以出现多次。
  • tagdigest可以不填。如果省略其中任何一个,构建器将默认使用latest

.dockerignore

docker CLI 将 content 发送到 docker 守护程序之前,它会项目根目录中查找名为.dockerignore的文件。如果文件存在,CLI 将排除匹配的文件和目录后再将 content 发送给守护程序。

使用方法与 .gitignore 类似,这里就不展开说明了。

第二部分 维护者信息


MAINTAINER

MAINTAINER <name>

指镜像维护的作者信息。

USER

USER <user>[:<group>] 
USER <UID>[:<GID>]

USER指令设置运行image时使用的用户名或UID,用于运行 RUNCMDENTRYPOINT指令。

注意,如果所在的用户组没有 root 权限,那么镜像将会以 root 用户组运行。

第三部分 镜像操作命令


ENV

ENV <key> <value>
ENV <key>=<value> ...

ENV 主要用于设置变量,使用 $variable_name${variable_name} 进行访问。一般用后者,可以进行下面这种神奇的操作:

${foo}_bar

同时还支持几个bash修饰符

  • ${variable:-word}表示默认值是word,没有值则为空字符串。
  • ${variable:+word}表示默认值是空字符串,否则是word

除此之外 ENV 还可以用来处理转义。

注意:设置全局生效的 ENV 变量可能会导致意想不到的结果,比如设置 ENV DEBIAN_FRONTEND noninteractive 会导致其他用户在 apt-get 的时候因为跳过提示而不明所以。 只在一个命令中生效可以这么设置:RUN <key>=<value> <command>.

ARG

ARG <name>[=<default value>]

ARG中定义的变量,可以通过docker build --build-arg <varname> = <value>,在构建时将其传递给构建器。如果指定了一个未在Dockerfile中定义的,则会报错。

One or more build-args were not consumed, failing build.

ARG可以设置默认值。

警告:不建议使用 ARG 来传递诸如github密钥,用户凭证等密码。这样使用docker history命令对Docker的所有用户都可见。

ARGENV很像,都可以给变量赋值,不同的是 ENV可以将它们持久保存在最终image中。

Docker有一组预定义的ARG变量,您可以在Dockerfile中使用相应的ARG指令。

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

使用 ARG 变量会导致一些缓存未命中的问题,会降低编译的速度。详细内容可以参考官方文档https://docs.docker.com/engine/reference/builder/#impact-on-build-caching

RUN

RUN有2种形式:

  • RUN <command>shell形式)
  • RUN ["executable","param1","param2"]exec 形式)

RUN指令将在当前 image 之上的新层中执行命令,生成新的一层。这样可以从 image 历史中的任何点创建容器,就像代码控制一样简单。

  • shell

    shell形式在 Linux上为/bin/sh -c,Windows上为cmd /S/C。 常见的方式是在末尾添加反斜杠来换行处理:

      RUN /bin/bash -c 'source $HOME/.bashrc; \
      echo $HOME'
    
  • exec

    exec形式可以避免 shell 的字符串变化,并且可以指定特定的 shell 来运行RUN命令。例如:

      RUN ["/bin/bash", "-c", "echo $HOME"]
    

    当然使用 exec形式 也有弊端,执行命令的时候必须指定 shell,不能像平时使用 shell 一样使用exec形式。比如这个形式是错的: RUN [ "echo", "$HOME" ]。正确的写法应该是上面的形式。

    exec形式中使用json进行参数传递,所以不能使用单引号包裹字符串,必须使用双引号。

    所以这也涉及到了字符转义的问题,尤其是Windows下的字符转义问题,比如这个写法是错的: RUN ["c:\windows\system32\tasklist.exe"] 。正确的写法是: RUN ["c:\\windows\\system32\\tasklist.exe"]

为了加快编译速度, Docker 会对RUN apt-get dist-upgrade -y 这样的命令做缓存,加快下一次的镜像构建。如果我们希望不对它进行缓存,应该使用--no-cache 标志, 例如 docker build --no-cache.

ADD

  • ADD <src>... <dest>
  • ADD ["<src>",... "<dest>"] (对于包含空格的路径,此形式是必需的)

ADD指令从<src>复制新文件、目录或远程文件URL,并将它们添加到容器的文件系统,路径<dest>

<src>可以包含通配符,<dest>是绝对路径或相对于WORKDIR的路径。

如果 src 是文件或者目录那么:

  • 所有新文件和目录的UID和GID都是0
  • 如果文件名中包含有特殊字符,那么需要根据 Go 的转义规则转义这些字符串。例如我们添加这个文件 arr[0].txt

      ADD arr[[]0].txt /mydir/    # copy a file named "arr[0].txt" to /mydir/
    
  • 如果<src>是可以识别的压缩格式(identity,gzip,bzip2或xz),则 Docker 会将其解包为目录。Url 的资源不会解被压缩。如解压后有冲突,那么会将冲突的文件名改为”2.”并继续解压。注意:文件是否被识别为识别的压缩格式,基于文件的内容,而不是文件的名称。

如果 src 是 url 地址:

  • dsct 的文件权限将设置为 600.
  • 如果 HTTP 头部带有Last-Modified的时间戳,则 dest 的 mtime 就使用这个时间戳。Dockerfile 的其他的操作,比如ADD,则不会修改这个值。
  • 如果<src>是URL并且<dest>以尾部斜杠结尾,则 Docker 会从URL中推断文件名,并将文件下载到<dest>/<filename>。例如,ADD http://example.com/foobar /会创建文件/ foobar。网址必须有一个路径,以便在这种情况下可以发现一个适当的文件名(http://example.com 除外)。

关于 dest:

  • <src> 路径必须在构建的上下文中; 不能 ADD ../something /something,因为docker构建的第一步是发送上下文目录(和子目录)到docker守护进程。
  • 如果<dest>以尾部斜杠/结尾,它会被认为是一个目录,<src>的内容将被写在<dest>/base(<src>)
  • 如果使用通配符指定了多个<src>资源,则<dest>必须是目录,并且必须以斜杠/结尾。
  • 如果<dest>不以尾部斜杠结尾,它将被视为常规文件,<src>的内容将写在<dest>
  • 如果<dest>不存在,则会与其路径中的所有缺少的目录一起创建。

使用 ADD 还需要注意下面这些情况:

  • 如果 dockerfile 使用管道传输过来进行构建镜像,因为不存在构建的其它文件,那么 dockerfile 里就只能使用 ADD url 的操作了。
  • ADD 不提供权限操作,无法解决访问权限的问题。如果 Docker 没有读取 src 的权限,那么需要使用 wget curl 或者其他工具解决。

COPY

两种形式:

  • COPY <src>... <dest>
  • COPY ["<src>",... "<dest>"] (src有空格时使用)

基本和ADD类似,除了COPY<src>不能为URL。

LABLE

LABEL 指令向image添加元数据。LABEL是键值对。有空格的话要使用引号或者反斜杠。下面是一些例子:

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

image可以有多个label。因为每个LABEL都会产生一个新层,会导致镜像效率低下,所以建议一行将所有 Lable 都添加完全。

LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

使用docker inspect 查看image的labels:

"Labels": {
    "com.example.vendor": "ACME Incorporated"
    "com.example.label-with-value": "foo",
    "version": "1.0",
    "description": "This text illustrates that label-values can span multiple lines.",
    "multi.label1": "value1",
    "multi.label2": "value2",
    "other": "value3"
},

ONBUILD

ONBUILD [INSTRUCTION]

ONBUILD指令在当前镜像被用作其它镜像构建的基础时,添加要在以后执行的trigger指令,当前镜像内不执行。

这一块目前很少见到有使用,等以后需要了再学习。https://docs.docker.com/engine/reference/builder/#onbuild

STOPSIGNAL

STOPSIGNAL signal

STOPSIGNAL 接收系统的退出指令并退出容器。

HEALTHCHECK

两种形式:

  • HEALTHCHECK [OPTIONS] CMD command (通过在容器中运行命令来检查容器运行状况)
  • HEALTHCHECK NONE (禁用检查)

HEALTHCHECK指令告诉Docker如何测试容器以检查它是否仍在工作。

SHELL

SHELL ["executable", "parameters"]

SHELL 指令用于覆盖默认的shell。

Linux上的默认shell是["/bin/sh","-c"],在Windows上是["cmd","/S","/C"]

SHELL指令在Windows上特别有用,其中有两个常用的和完全不同的本机shell:cmdpowershell,以及sh的备用Shell。

SHELL指令可以多次出现。每个SHELL指令覆盖所有先前的SHELL指令,并影响所有后续指令。 例如:

以下示例是Windows上的常见模式,可以使用SHELL指令进行简化。也可以在Linux上使用,如zshcshtcsh等等。

SHELL功能是在Docker 1.12中添加的。

第四部分 容器启动命令


CMD

CMD指令有三种形式:

  • CMD ["executable","param1","param2"] (exec form, 首选形式)
  • CMD ["param1","param2"] (为 ENTRYPOINT 提供参数)
  • CMD command param1 param2 (shell form)

Dockerfile中只能有一个CMD指令。如果有多个则只有最后一个生效。

CMD的主要目的是为运行容器时的默认启动命令。

  • 注意:如果使用CMDENTRYPOINT指令提供默认参数,CMDENTRYPOINT指令都应该以 JSON 数组格式指定。
  • 注意:exec 形式作为JSON数组解析,使用双引号(”)而不是单引号(’)。
  • 如果用户指定docker run参数,那么它们将覆盖CMD中指定的默认值。

注意:不要将RUNCMD混淆。RUN实际上运行一个命令并提交结果;CMD在构建时不执行任何操作,目的是指定镜像运行时的默认命令。

EXPOSE

EXPOSE <port> [<port>...]

EXPOSE 主要用来标记 Docker 运行时侦听的网络端口。默认监听TCP,也可以设置为UDP。

EXPOSE并不是真正的启动一个对外的端口,它的作用主要是在镜像创建者和运行者之间类似开发文档一样的东西。要真正开启这个端口,需要在 Docker run 的时候使用 -p 命令开启特定端口或者 -P 命令监听所有端口。

ENTRYPOINT

两种形式:

  • ENTRYPOINT [“executable”, “param1”, “param2”] (exec 形式, 首选)
  • ENTRYPOINT command param1 param2 (shell 形式)

ENTRYPOINT时容器启动时运行的一个脚本,只有最后一个ENTRYPOINT指令会有效果。

如果我们为 entrypoint 写一个可执行脚本 entrypoint.sh,可以使用execgosu命令确保最终可执行文件接收到Unix信号:

#!/bin/bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"

理解 entrypoint 与 cmd 的关系:

  • Dockerfile应该至少指定一个CMDENTRYPOINT命令。
  • 当使用容器作为可执行文件时,应该定义ENTRYPOINT
  • CMD应该用作定义ENTRYPOINT命令的默认参数。
  • 当容器运行命令带有替代参数时,CMD会将被覆盖。

下面这个表格可以辅助我们理解 entrypoint 与 cmd 的协作:

无 ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
无 CMD /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”] p1_cmd p2_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

VOLUME

VOLUME ["/data"]

VOLUME 标记为从本机主机或其他容器保留外部挂载的卷。

VOLUME 可以是JSON数组,或具有多个参数的纯字符串:

  • VOLUME ["/var/log/"]
  • VOLUME /var/log
  • VOLUME /var/log /var/db

一些注意事项:

  • 如果使用JSON数组,注意要使用双引号(”)而不是单引号(’)
  • Windows下的挂载点,必须符合两个条件之一:
    • 一个不存在的或者空的文件夹
    • 一个C盘之外的硬盘
  • 如果在声明后更改了卷中的数据,那么这些更改将无效。所以我们一般把 VOLUME 放到比较靠后的位置。

WORKDIR

WORKDIR /path/to/workdir

WORKDIR指令为Dockerfile中的RUNCMDENTRYPOINTCOPYADD等指令设置工作目录。如果WORKDIR不存在会则自动被创建。

它可以在一个Dockerfile中多次使用。如果提供的是相对路径,它将相对于先前WORKDIR指令的路径。 例如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

在这个Dockerfile中的最终pwd命令的输出是/a/b/c

WORKDIR指令可以解析先前使用ENV显式设置设置的环境变量,例如:

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

pwd命令在该Dockerfile中输出的最后结果是/path/$DIRNAME

结果

根据 Dockerfile 的文档,将之前主机上运行的脚本改写为 Dockerfile 的结果如下:

FROM debian:jessie
MAINTAINER kelvinblood <admin@kelu.org>

# Docker Build Arguments
ENV RESTY_VERSION="1.9.7.1" \
    RESTY_CONFIG_OPTIONS=" \
        --prefix=/usr/share/openresty \
        --with-pcre-jit \
        --with-http_postgres_module  \
        --with-http_iconv_module  \
        --with-http_stub_status_module" \
    RESTY_DATA_DIR="/var/local/nginx/conf/vhost/" \
    RESTY_LOG_DIR="/var/local/log/nginx/"

COPY assets/sources.list /etc/apt/sources.list

RUN apt-get update \
 && DEBIAN_FRONTEND=noninteractive apt-get install -y zip vim locales curl wget net-tools\
        libperl4-corelibs-perl libreadline-dev libpcre3-dev libssl-dev libpq-dev gcc libc6-dev make\
 && update-locale LANG=C.UTF-8 LC_MESSAGES=POSIX \
 && locale-gen en_US.UTF-8 \
 && DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales \
 && cd /tmp \
    && wget https://openresty.org/download/ngx_openresty-${RESTY_VERSION}.tar.gz \
    && tar -xzvf ngx_openresty-${RESTY_VERSION}.tar.gz \
    && cd ngx_openresty-${RESTY_VERSION}/ \
    && ./configure ${RESTY_CONFIG_OPTIONS} \
    && make \
    && make install \
    && mkdir -p /var/local/log/nginx \
    && mkdir -p /var/local/nginx/fastcgi_cache/one_hour \
    && cp -R /usr/share/openresty/nginx /var/local \
    && mkdir -p /var/local/nginx/conf/vhost \
    && apt-get clean\
    && rm -rf /var/lib/apt/lists/* \
    && rm -rf /tmp/ngx_openresty-${RESTY_VERSION} \
    && rm /tmp/ngx_openresty-${RESTY_VERSION}.tar.gz

COPY assets/nginx/conf/nginx.conf /var/local/nginx/conf/nginx.conf
COPY assets/nginx/conf/vhost/www.conf /var/local/nginx/conf/vhost/www.conf

EXPOSE 80/tcp

VOLUME ["${RESTY_DATA_DIR}", "${RESTY_LOG_DIR}"]
# ENTRYPOINT /usr/share/openresty/nginx/sbin/nginx -c /var/local/nginx/conf/nginx.conf -g 'daemon off;'
CMD ["/usr/share/openresty/nginx/sbin/nginx","-c","/var/local/nginx/conf/nginx.conf","-g","daemon off;"]

将正常的脚本转换成 Dockerfile 基本的思路就是

  • 设定好这个 Dockerfile 和相关依赖包的版本号
  • 安装系统依赖
  • 清除多余软件包和文件
  • 拷贝必要的脚本或配置
  • 做好端口和挂载点声明
  • 写好并添加启动命令或脚本entrypoint.sh

这个脚本中我做了几个方便验证的东西:

  • 挂载 volume 的验证:

    在不挂载 RESTY_DATA_DIR 的情况下,,启用后我改成了另外一个配置,显示其他页面。

    运行命令: docker run --name 'daemon4' -d -p 18080:80 test 时,会显示默认的 nginx 页面。

    挂载 RESTY_DATA_DIR 文件夹后,就换成其它界面:

      docker run --name 'daemon' -d -p 18080:80 \
          --volume /var/local/nginx/:/var/local/nginx/conf/vhost \
          --volume /var/local/log/nginx/:/var/local/log/nginx/ test4
    

    挂载的nginx.conf的文件如下:

      server {
              listen 80;
              access_log /var/local/log/nginx/nginx.www.access.log;
              error_log /var/local/log/nginx/nginx.www.error.log;
    	
              location / {
                  default_type text/html;
                  content_by_lua '
                      ngx.say("<p>hello, kelu volume</p>")
                  ';
              }
          }
    
  • 最后两行 ENTRYPOINT 和 CMD 的作用是一样的。我多写了一个看看效果。

参考资料


Rancher 再学习(一)

前段时间转载了这篇《Rancher安装手册》文章, 也尝试着安装了一下,有了一些浅显的认识。今天从0开始又一次部署,算是对 Rancher 的再学习。 这一篇主要是将各种事先需要准备的一些东西。

使用的环境为 CentOS 7.4 ( cat /etc/redhat-release

事先准备几个命令,后边你可能会经常用到(但愿不需要用到)

docker kill $(docker ps -a -q) # 杀死所有正在运行的容器
docker rm $(docker ps -a -q)	# 删除所有已经停止的容器
docker rmi $(docker images -q)	# 删除所有镜像

重置 docker/rancher:

rm -rf /var/lib/docker
rm -rf /var/lib/rancher

Docker再安装

按照 Rancher 的官方推荐,使用 17.06 版本的 Docker。所以要先把本机的docker 卸载,再重新安装。

# 查看安装版本号、卸载
yum list installed | grep docker
yum -y remove docker-ce.x86_64

# 查看可用的版本
yum makecache fast
yum list docker-ce.x86_64 --showduplicates | sort -r

yum -y install docker-ce-17.06.0.ce

加速

安装成功后需要对 Docker 源进行加速。我试用 Docker 官方的加速办法:在 /etc/docker/daemon.json 文件并添加上 registry-mirrors 键值。

touch /etc/docker/daemon.json
vi /etc/docker/daemon.json
{  
	"registry-mirrors": ["https://registry.docker-cn.com"]
}

重启 Docker 后生效。

开机自启动

systemctl enable docker
# Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

Rancher 安装

参照我先前的文章《Rancher安装手册》进行安装。

注意要使用 Chrome 访问 Rancher。在 IE 下会出现无法添加主机的情况(嗯,我踩过的坑。)

防火墙firewall

容器之间通信需要借助防火墙。使用 Rancher 添加不同主机的时候,就需要打通各个主机的通信,主要是 Rancher 服务器的默认 8080 端口和 IPsec 的 500、4500端口(UDP协议)。这里列几个,其他的我会再开一篇文章记录一下。

关于 firewall,只要记住服务和端口两个概念即可。

iptables -F; # 清除 iptables,防止两个防火墙冲突。
iptables -L;
firewall-cmd --zone=public --list-ports  # 查看所有打开的端口
firewall-cmd --list-services			# 查看开启的服务。
firewall-cmd --zone=public --add-port=80/tcp --permanent    (--permanent永久生效,没有此参数重启后失效)							#  添加端口
firewall-cmd --permanent --add-service="ipsec"  # 添加服务
firewall-cmd --reload
systemctl start firewalld.server		# 重启 firewall

打了这么些命令,可以参考一下

检查远端端口是否可用

使用 nc 命令进行检测:

nc -v host port

出现 Connection refused 或者 Connected 都说明端口可用。如果出现 No route to host. 则说明端口未开放。例如:

参考资料


Rancher 中文文档 —— Rancher 安装

原文:http://rancher.com/docs/rancher/v1.6/en/installing-rancher/installing-server/

查看本系列翻译的目录

安装 Rancher 服务器


Rancher 由一系列的 Dcoekr 容器组成。 运行 Rancher 就像添加两个容器一样简单: 一个服务管理容器和一个作为客户端的节点容器。

注意:Rancher 服务器容器帮助选项docker run rancher/server --help

要求

  • 支持 Dcoekr 的 Linux 发行版。我们在 RancherOS Ubuntu 和 RHEL/CentOS 7 行会进行更多的测试(画外音:所以会更稳定一些)。。
    • 在 RHEL/CentOS 里,Docker 不支持默认的存储驱动器,比如devicemapper 使用的 loopback。所以请参考 Dcoker 的相关文档如何进行修改。
    • 对于RHEL / CentOS的,如果要启用SELinux的,需要安装附加的SELinux模块
  • 1GB RAM
  • MySQL服务器应该设置 MAX_CONNECTIONS > 150
    • 配置要求:
      • Option 1: Run with Antelope with default of COMPACT
      • Option 2: Run MySQL 5.7 with Barracuda where the default ROW_FORMAT is Dynamic
    • 推荐设置:
      • max_packet_size > = 32M
      • innodb_log_file_size > = 256M(如果你有一个现有的数据库,请确保适当的计划如何更改此设置。)
      • innodb_file_per_table=1
      • innodb_buffer_pool_size > = 1GB(对于内存较大的主机,建议4-8G池)

注:目前,Docker Mac 暂不支持 Rancher。

Rancher server 标签

Rancher server 有两个不同的标签。对于每一个主要版本的标签,我们将提供特定版本的文档。

  • rancher/server:latest标签将是我们最新开发版本。这些版本会已经通过我们的CI自动化框架验证。这些版本不意味着在它们可以在生产环境中部署。
  • rancher/server:stable标签将是我们最新的稳定版本。这个标签是我们推荐用于生产的版本。

请不要使用有任何带有rc{n}后缀的版本。这些rc是 Rancher 的测试版本

启动 Rancher 服务器 - 单容器(非HA)

在已安装了 docker 的 Linux 机器上,启动 Rancher 的单个实例很简单。

$ sudo docker run -d --restart=unless-stopped -p 8080:8080 rancher/server

Rancher 界面

Rancher 的界面和 API 可以通过 8080 端口访问。docker 镜像下载完成后,需要1到两分钟时间才可以使用。

进入网站: http://<SERVER_IP>:8080. <SERVER_IP>是 Rancher server 的公网地址.

当 Rancher 界面启动后, 我们可以添加主机或者从应用仓库里添加容器。 默认情况下 Rancher 使用 cattle 作为容器编排的环境。在主机添加吼,我们就可以开始添加服务或者在应用商店中通过模板添加服务了。

启动 Rancher 服务器 - 单容器 - 外部数据库

我们可以指定使用外部数据库来启动 Rancher。 命令行还是相同的,不过我们会添加额外的参数来设定如何使用外部数据库。

注意:数据库,名称和数据库的用户需要事先创建好,模式Schema不需要创建。Rancher 将自动创建所有与 Rancher 的模式Schema。

下面是创建一个数据库和用户的SQL命令的例子。

> CREATE DATABASE IF NOT EXISTS cattle COLLATE = 'utf8_general_ci' CHARACTER SET = 'utf8';
> GRANT ALL ON cattle.* TO 'cattle'@'%' IDENTIFIED BY 'cattle';
> GRANT ALL ON cattle.* TO 'cattle'@'localhost' IDENTIFIED BY 'cattle';

要启动 Rancher 连接到外部数据库,我们要将额外的信息作为命令的一部分传给容器:

$ sudo docker run -d --restart=unless-stopped -p 8080:8080 rancher/server \
    --db-host myhost.example.com --db-port 3306 --db-user username --db-pass password --db-name cattle

大多数的选项有默认值,不是必需的。只有 MySQL 服务器的位置是必需的。

--db-host               IP or hostname of MySQL server
--db-port               port of MySQL server (default: 3306)
--db-user               username for MySQL login (default: cattle)
--db-pass               password for MySQL login (default: cattle)
--db-name               MySQL database name to use (default: cattle)

注: Rancher server 的早期版本中,我们使用环境变量连接到外部数据库,这些环境变量也会继续生效,不过我们建议使用参数来运行 Rancher

启动 Rancher 服务器 - 单容器 - 绑定 Mysql 数据卷

如果你想将容器的数据库与主机商的存储设备绑定起来,那么应当这么启动 Rancher server:

$ sudo docker run -d -v <host_vol>:/var/lib/mysql --restart=unless-stopped -p 8080:8080 rancher/server

使用此命令,数据库将会把数据卷绑定到主机上。如果你已经有了 Rancher 数据库,也想绑定 MySQL 数据卷,参考我们的升级文件

启动 Rancher 服务器 - 全主动/主动HA

Rancher server 上跑 HA 和上文的跑外部数据库一样简单。通过一个额外的端口和参数,与外部的负载均衡器进行通信。

要求:

  • HA节点:
    • 支持 Dcoekr 的 Linux 发行版。我们在 RancherOS Ubuntu 和 RHEL/CentOS 7 行会进行更多的测试(画外音:所以会更稳定一些)。
      • 在 RHEL/CentOS 里,Docker 不支持默认的存储驱动器,比如devicemapper 使用的 loopback。所以请参考 Dcoker 的相关文档如何进行修改。
      • 对于RHEL / CentOS的,如果要启用SELinux的,需要安装附加的SELinux模块
    • 1GB RAM
    • 节点之间开通需要的端口:93458080
  • MySQL服务器应该设置 MAX_CONNECTIONS > 150
    • 配置要求:
      • Option 1: Run with Antelope with default of COMPACT
      • Option 2: Run MySQL 5.7 with Barracuda where the default ROW_FORMAT is Dynamic
  • 外部负载均衡器
    • 8080

注:目前,Docker Mac 暂不支持 Rancher。

对大规模部署的建议

  • 每个 Rancher server 节点应该有一个4 GB或8 GB 堆的大小,这需要具有至少8 GB或16 GB的RAM
  • MySQL数据库应该有快速磁盘
  • 对于真正的HA,建议MySQL数据库进行适当策略的备份。例如构建一个 Galera Cluster,提高MySQL服务的可用性和性能。
  1. 在每个要加入HA的节点,运行以下命令:

    # Launch on each node in your HA cluster
    $ docker run -d --restart=unless-stopped -p 8080:8080 -p 9345:9345 rancher/server \
         --db-host myhost.example.com --db-port 3306 --db-user username --db-pass password --db-name cattle \
         --advertise-address <IP_of_the_Node>
    

    对于每个节点,<IP_of_the_Node>都是唯一的,因为这将是正被加入到HA设置的每个特定节点的IP。 如果你需要更改-p 8080:8080,使HTTP端口暴露在主机上的不同端口上,将需要添加--advertise-http-port <host_port>的命令。

    注意:您可以通过运行得到帮助的命令docker run rancher/server --help

  2. 配置外部负载均衡器,平衡端口80443之间的流量,将流量指向运行 Rancher Server 的节点池。负载平衡器必须支持的WebSockets和headers转发。请参阅SSL设置页查看配置示例。

Option Example Description
IP address --advertise-address 192.168.100.100 Uses the give IP address
Interface --advertise-address eth0 Retrieves the IP of the given interface
awslocal --advertise-address awslocal Retrieves the IP from http://169.254.169.254/latest/meta-data/local-ipv4
ipify --advertise-address ipify Retrieves the IP from https://api.ipify.org

Rancher HA注意事项

如果你的 Rancher 主服务器节点更改IP,你的节点将不再是 Rancher HA 群集的一部分。您必须使用--advertise-address + 不正确的IP停止老的 Rancher server 容器 ,并使用 --advertise-address + 正确的IP 开始一个新的Rancher server 容器。

在 AWS 上运行 Rancher server 的 ELB

因为译者不用这一块,所以现在先不翻译了 2333333333333

在 AWS 的应用负载平衡器(ALB)

我们不再建议在AWS应用负载平衡器(ALB)在使用弹性/经典负载均衡器(ELB)。如果您仍然选择使用ALB,您需要将流量引导到节点上的HTTP端口,默认是8080端口。

启用ACTIVE DIRECTORY 或 OPENLDAP FOR TLS

要启用 Active Directory 或 OpenLDAP P for TLS, Rancher server 容器需要在启动时候加载由 LDAP 提供的证书。

我们需要通过绑定拥有证书的数据卷来启动 Rancher 容器。证书必须命名为ca.crt:

$ sudo docker run -d --restart=unless-stopped -p 8080:8080 \
  -v /some/dir/cert.crt:/var/lib/rancher/etc/ssl/ca.crt rancher/server

我们可以通过跟踪 Rancher server 地日志来检查 ca.crt是不是正确地传递给了 Rancher server:

$ docker logs <SERVER_CONTAINER_ID>

日志的开头,就会出现正确添加证书的信息。

Adding ca.crt to Certs.
Updating certificates in /etc/ssl/certs... 1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.
Certificate was added to keystore

启用 Rancher http 代理

为了启用 http 代理, docker daemon 需要修改代理的配置。在启动 Rancher server 之前进行配置:

sudo vi /etc/default/docker

在文件中,编辑#export http_proxy="http://127.0.0.1:3128" 指向您的代理。保存更改,然后重新启动 Docker。

重启 Docker 的方式在不同操作系统之间会有所差别。

注: 如果您使用 systemd 运行 Docker,请根据 Docker 的说明 配置HTTP代理。

配置好了 Docker,为了在Rancher 应用里也能加载,需要进行一些配置,并把代理的配置参数添加到 Rancher 的环境变量。

$ sudo docker run -d \
    -e http_proxy=<proxyURL> \
    -e https_proxy=<proxyURL> \
    -e no_proxy="localhost,127.0.0.1" \
    -e NO_PROXY="localhost,127.0.0.1" \
    --restart=unless-stopped -p 8080:8080 rancher/server

如果不使用 Rancher 应用,按照正常情况启动 Rancher server 即可。

主机添加到 Rancher 之后,后续就不需要再添加其他配置了。

与 MySQL 通过 SSL 通信

注:目前,Rancher 1.6.3+ 以上支持这个特性

重要提示

如果你使用了 LDAP/AD authentication 作为认证后端,由其它客户端生成CA证书(而不是MySQL生成的),那么接下里的操作不适合你,不会达到预期效果。

要求

  • 认证文件 或 MySQL 服务器的 CA 证书(PEM编码)

说明

  1. 将服务器的证书或CA证书复制到 Rancher server 的主机。当启动rancher/server容器时需要挂载认证文件到/lib/rancher/etc/ssl/ca.crt
  2. 替换服务器变量中的默认值,构建一个自定义的JDBC URL: jdbc:mysql://<DB_HOST>:<DB_PORT>/<DB_NAME>?useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&prepStmtCacheSize=517&cachePrepStmts=true&prepStmtCacheSqlLimit=4096&socketTimeout=60000&connectTimeout=60000&sslServerCert=/var/lib/rancher/etc/ssl/ca.crt&useSSL=true
  3. 根据这个 JDBC URL 设置容器中的环境变量,包括 CATTLE_DB_CATTLE_MYSQL_URLCATTLE_DB_LIQUIBASE_MYSQL_URL

  4. 在容器中设置 export CATTLE_DB_CATTLE_GO_PARAMS="tls=true"。如果服务器的证书的主题字段不匹配服务器的主机名,需要使用CATTLE_DB_CATTLE_GO_PARAMS="tls=skip-verify"代替。

例子


$ export JDBC_URL="jdbc:mysql://<DB_HOST>:<DB_PORT>/<DB_NAME>?useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&prepStmtCacheSize=517&cachePrepStmts=true&prepStmtCacheSqlLimit=4096&socketTimeout=60000&connectTimeout=60000&sslServerCert=/var/lib/rancher/etc/ssl/ca.crt&useSSL=true"

$ cat <<EOF > docker-compose.yml
version: '2'
  services:
    rancher-server:
      image: rancher/server:stable
      restart: unless-stopped
      command: --db-host <DB_HOST> --db-port <DB_PORT> --db-name <DB_NAME> --db-user <DB_USER> --db-pass <DB_PASS>
      environment:
        CATTLE_DB_LIQUIBASE_MYSQL_URL: $JDBC_URL
        CATTLE_DB_CATTLE_MYSQL_URL: $JDBC_URL
        CATTLE_DB_CATTLE_GO_PARAMS: "tls=true"
      volumes:
        - /path/to/mysql/ca.crt:/var/lib/rancher/etc/ssl/ca.crt
      ports:
        - "8080:8080"
EOF

$ docker-compose up -d

重要提示: 需要同时修改 JDBC URL 和 命令行中 --db-xxx 的变量!