《Docker技术入门与实战》读书笔记

2017-07-23

第1章 初识容器与docker

1.1 什么是docker

  • 基于Go实现的开源容器项目
  • Docker可理解为一种轻量级的沙盒。每个容器内运行着一个应用,不同的容器相互隔离,容器之间也可以通过网络互相通信。
  • 容器自身对系统资源的额外需求也十分有限,远低于传统虚拟机。

1.2 为什么要使用docker

开发和运维中的优势
  • 更快速的交付和部署
  • 更高效的资源利用
  • 更轻松的迁移和扩展
  • 更简单的更新管理
与传统虚拟机相比
  • Docker容器很快,启动和停止可在秒级实现。
  • Docker对系统资源需求少,一台主机可同时运行数千个Docker容器。
  • Docker通过类似git设计理念的操作来方便用户获取、分发、更新应用镜像,存储复用,增量更新。
  • Docker通过 Dockerfile 支持灵活的自动化创建和部署机制,提高工效,使流程标准化。

1.3 docker与虚拟化

  • 虚拟化核心为对资源的抽象,目标为在同一个主机上同时运行多个系统或应用,提高资源利用率、降低成本、方便管理、容错容灾。
  • 虚拟化分类
    • 硬件虚拟化(很少)
    • 软件虚拟化
      • 应用虚拟化
        • 模拟设备
        • wine等软件
      • 平台虚拟化
        • 完全虚拟化
          • 虚拟机模拟完整的底层硬件环境和特权指令执行过程,客户操作系统无须修改。如VMware Workstation、VirtualBox。
        • 硬件辅助虚拟化
          • 利用硬件(如CPU的虚拟化技术Intel-VT、AMD-V)处理敏感指令实现完全虚拟化功能,客户操作系统无须修改。如VMware Workstation、Xen、KVM。
        • 部分虚拟化
          • 部分硬件资源进行虚拟化,客户操作系统需进行修改。
        • 准虚拟化
          • 部分硬件接口以软件形式提供给客户机操作系统,客户操作系统需进行修改。如早期的 Xen。
        • 操作系统级虚拟化
  • Docker容器在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,更轻量级。

第2章 核心概念与安装配置

2.1 核心概念

  • Docker镜像
    • Docker镜像是一个只读的模板。
    • 镜像是创建Docker容器的基础。通过版本管理和增量文件系统,Docker提供了十分简单的机制来创建和更新现有镜像。
  • Docker容器

    • Docker利用容器来运行和隔离应用
    • 容器是从镜像创建的应用运行实例。可以将其启动、开始、停止、删除,容器间彼此隔离、互不可见。
    • 镜像自身是只读的。容器从镜像启动的时候,会在镜像的最上层创建一个可写层
  • Docker仓库

    • 类似于代码仓库,是Docker集中存放镜像文件的场所。
    • 仓库注册服务器是仓库的仓库,其上往往存放着多个仓库。
    • 公开仓库、私有仓库。

2.2 安装docker

  • Docker Platform
    • 支持在桌面系统或云平台安装Docker
  • DockerHub
    • 官方云托管服务,提供公有或私有镜像仓库
  • DockerCloud
    • 官方容器云服务,可完成容器部署与管理,可完整支持容器化项目,还有CI、CD功能。
  • Docker DataCenter
    • 提供企业级的简单安全弹性的容器集群编排和管理。

尽量使用Linux来运行Docker,Linux原生支持。

2.2.1 Ubuntu环境下安装docker
  • 系统要求:64位、内核>3.10
    • 查询linux版本:uname -a 或 cat /proc/version
    • 为让docker使用aufs存储,安装linux-image-extra软件包
      • sudo apt-get install -y linux-image-extra-$(uname -r)
    • ubuntu>14.04 LTS
  • 添加镜像源
    • 安装apt-transport-https以支持https协议的源
      • sudo apt-get install -y apt-transport-https
    • 添加源的gpg密钥
      • sudo apt-key adv –keyserver hkp://p80.pool.sks-keyservers.net:80 –recv-keys 58118E89F3A912897C070ADBF76221572C52609D
    • 获取当前操作系统代号
      • lsb_release -c
      • 14.04 LTS代号为trusty
    • 添加docker官方apt源,通过以下命令创建/etc/apt/sources.list.d/docker.list,并写入源的地址。
    • 添加成功后,更新apt软件包缓存
      • sudo apt-get update
  • 开始安装docker
    • sudo apt-get install -y docker-engine
    • 除手动添加软件源方式安装,也可使用官方脚本自动化安装
    • 安装成功后启动docker
      • sudo service docker start
2.2.2 Centos环境下安装docker
2.2.3 通过脚本安装
2.2.4 Mac os环境下安装docker
2.2.5 Windows环境下安装docker

2.3 配置docker服务

  • 将当前用户加入安装中自动创建的docker用户组(避免每次使用docker命令都要sudo)
    • sudo usermod -aG docker USER_NAME
    • 退出重新登录生效
  • docker默认配置文件:/etc/default/docker
    • 修改参数,如让docker服务通过本地2375端口接收来自外部的请求
      • DOCKER_OPTS=”$DOCKER_OPTS -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock”
    • 修改后重启docker服务:
      • sudo service docker restart
  • docker服务管理脚本:/etc/init.d/docker
    • 功能为将docker进程id写入/var/run/docker.pid,并通过ulimit调整系统资源限制。
  • 若通过upstart工具管理服务,则管理服务配置文件为/etc/init/docker.conf
  • CentOS、Redhat,服务可通过systemd管理
    • sudo systemctl start docker.service
  • 如服务异常,可查看docker服务日志
    • sudo tail /var/log/upstart/docker.log
  • 重启docker服务后,查看服务是否正常启动
    • docker version

2.4 推荐实践环境

  • Ubuntu 14.04.3 LTS

第3章 使用docker镜像

3.1 获取镜像

  • 从官方docker hub镜像源下载镜像:docker pull NAME[:TAG]
    • NAME为境像仓库名,TAG为镜像标签(通常为版本)
    • 获取ubuntu:docker pull ubuntu:14.04
    • 不显示指定TAG,默认选择latest标签
  • 从pull过程可知,镜像文件由若干层组成。当不同镜像包含相同的层时,本地仅存一份。
  • 从不同的仓库下载镜像,可能重名。严格的说,镜像仓库名中应添加仓库地址,默认为从docker hub下载,前缀可忽略。
    • docker pull registry.hub.docker.com/ubuntu:14.04 即 docker pull ubuntu:14.04
  • 下载镜像后,利用镜像创建容器,在容器中执行bash应用:docker run -it ubuntu:14.04 bash,bash中执行命令:ping localhost

3.2 查看镜像信息

  • 列出镜像:docker images
    • 镜像大小信息仅表示其逻辑大小,物理占用存储空间会小于各镜像逻辑体积之和。
    • images子命令选项
      • -a, –all=true|false:列出所有镜像文件,默认为否
      • –digests=true|false:列出镜像数字摘要值,默认为否
      • -f, –filter=[]:过滤列出的镜像,如dangling=true只显示未被使用的镜像;也可指定带有特定标注的镜像。
      • –format=”TEMPLATE”:控制输出格式,如.ID代表ID信息,.Repository代表仓库信息。
      • –no-trunc=true|false:对输出结果中太长部分是否截断,如镜像ID,默认为是。
      • -q, –quiet=true|false:仅输出ID,默认为否。
    • 更多子命令:man docker-images
  • 使用tag命令为本地镜像添加标签:docker tag ubuntu:latest myubuntu:latest
    • 添加标签后,镜像ID并没有改变,只是一个别名。
  • 查看镜像详细信息:docker inspect ubuntu:14.04
  • 查看镜像历史(镜像创建过程):docker history ubuntu:14.04

3.3 搜寻镜像

  • 搜索远程仓库镜像:docker search TERM
  • 子命令
    • –automated=true|false:仅显示自动创建的镜像,默认为否
    • –no-trunc=true|false:输出信息不截断显示,默认为否
    • -s, –stars=X:仅显示评价为指定星级以上的镜像,默认为0,即输出所有镜像
  • docker search --automated -s 3 nginx
  • 默认按星级评价排序

3.4 删除镜像

  • docker rmi IMAGE [IMAGE…]
    • IMAGE可以为标签或ID
    • docker rmi myubuntu:latest
    • 若镜像有多个标签,docker rmi只会删除1个标签,镜像本身还存在;但若镜像只有一个标签,则会删除镜像文件的所有层。
  • 使用镜像ID删除镜像
    • docker rmi ID,会先尝试删除镜像所有标签,再删除镜像文件本身。
    • 当有镜像创建的容器存在,镜像文件默认无法删除
    • 强行删除镜像(即使有容器存在):docker rmi -f ubuntu:14.04
    • 不推荐强行删除镜像,一般先删除镜像的所有容器,再删除镜像。
  • 查看本机上所有容器
    • docker ps -a

3.5 创建镜像(3种方式)

  • 基于已有镜像的容器创建

    • docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
      • -a, –author=””:作者
      • -c, –change=[]:提交时执行Dockerfile指令,包括CMD|ENTRYPOINT|ENV|EXPOSE|LABEL|ONBUILD|USER|VOLUME|WORKDIR等
      • -m, –message=””:提交消息
      • -p, –pause=true:提交时暂停容器运行。
    • 创建步骤
      • 启动容器:docker run -it ubuntu:14.04 /bin/bash
      • 记住容器ID
      • 在容器中进行修改
      • 以容器为基准创建新镜像
        • docker commit -m “修改的具体内容” -a “马面” 容器ID test:0.1
  • 基于本地模板导入

    • docker import [OPTIONS] file|URL|-[REPOSITORY[:TAG]]
    • OPENVZ模板下载
    • cat ubuntu-14.04-x86_64-minimal.tar.gz | docker import - ubuntu:14.04
  • 基于Dockerfile创建

3.6 存出和载入镜像

  • 存出镜像:导出镜像到本地文件
    • docker save -o ubuntu_14.04.tar ubuntu:14.04
  • 载入镜像
    • docker load –input ubuntu_14.04.tar 或 docker load < ubuntu_14.04.tar

3.7 上传镜像

  • docker push NAME[:TAG] | [REGISTRY_HOST[:REGISTRY_PORT]/]NAME[:TAG]
  • 用户在docker hub网站注册后即可上传自制镜像
    • docker tag test:latest user/test:latest
    • docker push user/test:latest
  • 第1次上传会提示输入登录信息。

3.8 本章小结


第4章 操作docker容器

  • 容器为镜像的运行实例,镜像为静态的只读文件,容器有可写文件层。

4.1 创建容器

新建容器
  • docker create -it ubuntu:latest
  • create后容器处于停止状态
  • create命令与容器运行模式相关的选项
选项 说明
-a, –attach=[] 是否绑定到标准输入、输出和错误
-d, – detach=true false 是否在后台运行容器,默认否
–detach-keys=”” 从attach模式退出的快捷键
–entrypoint=”” 镜像存在入口命令时,覆盖为新的命令
–expose=[] 指定容器暴露出来的端口或端口范围
–group-add=[] 运行容器的用户组
-i, –interactive=true false 保持标准输入打开,默认否
–ipc=”” 容器IPC命名空间,可以为其他容器或主机
–ioslation=”default” 容器使用的隔离机制
–log-driver=”json-file” 指定容器的日志驱动类型,可以为json-file、syslog、journald、gelf、fluentd、awslogs、splunk、etwlogs、gcplogs或none
–log-opt=[] 传递给日志驱动的选项
–net=”bridge” 指定容器网络模式,包括bridge、none、其他容器内网络、host的网络或某个现有网络等
–net-alias=[] 容器在网络中的别名
-P, –publish-all=true false 通过NAT机制将容器标记暴露的端口自动映射到本地主机的监时端口
-p, –publish=[] 指定如何映射到本地主机端口,如 -p 11234-12234:1234-2234
–pid=host 容器的pid命名空间
–userns=”” 启动userns-remap时配置用户命名空间的模式
–uts=host 容器的UTS命名空间
–restart=”no” 容器重启策略,包括no、on-failure[:max-retry]、always、uniess-stopped等
–rm=true false 容器退出后是否自动删除,不能跟-d同时使用
-t, –tty=true false 是否分配一个伪终端,默认否
–tmpfs=[] 挂载临时文件系统到容器
-v –volume[=[[HOST-DIR:] CONTAINER-DIR[:OPTIONS]]] 挂载主机上的文件卷到容器内
–volume-driver=”” 挂载文件卷的驱动类型
–volume-from=[] 从其他容器挂载卷
-w, –workdir=”” 容器内的默认工作目录
  • create命令与容器环境和配置相关的选项
选项 说明
–add-host=[] 在容器内添加一个主机名到IP地址的映射关系(通过/etc/hosts文件)
–device=[] 映射物理机上的设备到容器内
–dns-search=[] DNS搜索域
–dns-opt=[] 自定义的DNS选项
–dns=[] 自定义的DNS服务器
-e, –env=[] 指定容器内环境变量
–env-file=[] 从文件中读取环境变量到容器内
-h, –hostname=”” 指定容器内的主机名
–ip=”” 指定容器的IPv4地址
–ip6=”” 指定容器的IPv6地址
–link=[:alias] 链接到其他容器
–mac-address=”” 指定容器的mac地址
–name=”” 指定容器的别名
  • create命令与容器资源限制和安全保护相关的选项
选项 说明
–blkio-weight=10~100 容器读写块设备的I/O性能权重,默认为0
–blkio-weight-device=[DEVICE_NAME:WEIGHT] 指定各个块设备的I/O性能权重
–cpu-shares=0 允许容器使用cpu资源的相对权重,默认一个容器能用满一个核的cpu
–cap-add=[] 增加容器的Linux指定安全能力
–cap-drop=[] 移除容器的Linux指定安全能力
–cgroup-parent=”” 容器cgroups限制的创建路径
–cidfile=”” 指定容器的进程ID号写到文件
–cpu-period=0 限制容器在CFS调度器下的CPU占用时间片
–cpuset-cpus=”” 限制容器能使用哪些CPU核心
–cpuset-mems=”” NUMA架构下使用哪些核心的内存
–cpu-quota=0 限制容器在CFS调度器下的CPU配额
–device-read-bps=[] 挂载设备的读吞吐率(以bps为单位)限制
–device-write-bps=[] 挂载设备的写吞吐率(以bps为单位)限制
–device-read-iops=[] 挂载设备的读速率(以每秒i/o次数为单位)限制
–device-write-iops=[] 挂载设备的写速率(以每秒i/o次数为单位)限制
–kernel-memory=”” 限制容器使用内核的内存大小,单位可以是b、k、m或g
-m, –memory=”” 限制容器内应用使用的内存,单位可以是b、k、m或g
–memory-reservation=”” 当系统中内存过低时,容器会强制限制内存到给定值,默认情况下等于内存限制值
–memory-swap=”LIMIT” 限制容器使用内存和交换区的总大小
–oom-kill-disable=true false 内存耗尽时是否杀死容器
–pids-limit=”” 限制容器的pid个数
–privileged=true false 是否给容器以最高权限,这意味着容器内应用将不受权限下限制,一般不推荐
–read-only=true false 是否让容器内的文件系统只读
–security-opt=[] 指定一些安全参数,包括权限、安全能力、apparmor等
–stop-signal=SIGTERM 指定停止容器的系统信号
–shm-size=”” /dev/shm 的大小
–sig-proxy=true false 是否代理收到的信号给应用,默认为true,不能代理SIGCHLD、SIGSTOP和SIGKILL信号
–memory-swappiness=”0~100” 调整容器的内存交换区参数
-u, –user=”” 指定在容器内执行命令的用户信息
–ulimit=[] 能过ulimit来限制最大文件数、最大进程数等
  • 其他重要选项
    • -l, –label=[]:以键值对方式指定容器的标签信息
    • –label-file=[]:从文件中读取标签信息
启动容器
  • docker start af8f4f922daf
新建并启动容器
  • docker run ubuntu /bin/echo “Hello World!”
  • 等价于 docker create + docker run
  • 启动容器并进入终端
    • docker run -it ubuntu:latest /bin/bash
    • -t选项让docker分配一个伪终端并绑定到容器的标准输入上
    • -i选项让容器的标准输入保持打开
    • exit命令退出容器
    • exit退出后,容器自动处于退出状态
  • docker run常见错误码
    • 125:docker daemon执行出错,不支持的docker命令参数
    • 126:所指定命令无法执行,如权限出错
    • 127:容器内命令无法找到
  • 守护态执行
    • docker run -d ubuntu /bin/sh -c “while true; do echo hello world; sleep 1; done”
    • 获取容器的输出信息:docker logs CONTAINER_ID

4.2 终止容器

  • docker stop [-t|–time[=10]] [CONTAINER…]
    • 首先向容器发送SIGTERM信号,等待一段时间后再发送SIGKILL信号终止容器
  • docker kill命令会直接发送SIGKILL信号强行终止容器
  • docker容器中应用终结时,容器也会自动停止
  • 查看所有容器ID
    • docker ps -qa
  • 重启终止状态的容器
    • docker start [CONTAINER]
  • 重启容器
    • docker restart [CONTAINER]

4.3 进入容器

  • 使用-d参数,容器启动后进入后台,无法查看和操作,若需进入容器操作,可使用官方的attach、exec命令或三方nsenter工具。
attach命令
  • docker attach [–detach-keys[=[]]] [–no-stdin] [–sig-proxy[=true]] CONTAINER
    • docker attach CONTAINER
  • –detach-keys[=[]]:退出attach模式的快捷键,默认CTRL-p、CTRL-q。
  • –no-stdin=true|false:是否关闭标准输入,默认打开。
  • –sig-proxy=true|false:是否代理收到的系统信号给应用进程,默认true。
  • 缺点:多个窗口同时使用attach连到同一容器,所有窗口同步显示。
exec命令
  • docker exec [-d|–detach] [–detach-keys[=[]]] [-i|–interactive] [–privileged] [-t|–tty] [-u|–user[=USER]] CONTAINER COMMAND [ARG…]
    • docker exec -it CONTAINER_NAME /bin/bash
    • it参数保持标准输入打开,并分配一个伪终端
  • -i, –interactive=true|false:打开标准输入接受用户输入命令,默认false
  • –privileged=true|false:是否给执行命令以最高权限,默认false
  • -t, –tty=true|false:分配伪终端,默认false
  • -u, –user=””:执行命令的用户名或ID
nsenter工具

4.4 删除容器

  • docker rm [-f|–force] [-l|–link] [-v|–volumes] CONTAINER [CONTAINER…]
    • -f, –force=false:是否强行终止并删除运行中的容器
    • -l, –link=false:删除容器连接,但保留容器
    • -v, –volumes=false:删除容器挂载的数据卷
  • docker rm 默认只删除终止或退出状态容器
  • docker rm -f 直接删除运行中容器,会先发送SIGKILL信号给容器,终止应用,之后强行删除。

4.5 导入和导出容器

导出容器
  • 导出已创建好的容器到文件,不管容器是否处于运行状态
  • docker export [-o|–output[=””]] CONTAINER
    • -o 指定导出的tar文件,也可通过重定向来实现
      导入容器
  • 导出的文件通过docker import命令导入变成镜像
  • docker import [-c|–change[=[]]] [-m|–message[=MESSAGE]] file|URL|-[REPOSITORY[:TAG]]
    • docker import XXX.tar - test/ubuntu:v1.0
  • -c, –change=[]:在导入同时执行对容器进行修改的Dockerfile指令
  • docker load(镜像存储文件) 与 docker import(容器快照) 相似
    • 容器快照将丢弃所有历史记录和元数据信息;镜像存储文件将保存完整记录。从容器快照文件导入时可重新指定标签等元数据信息。

4.6 本章小结

  • 生产环境,使用容器时在一组容器前引入HA机制,如HAProxy工具来代理容器访问,这样容器故障时,可快速切换到功能正常的容器。
  • 指定合适的容器重启策略,来自动重启退出的容器。

第5章 访问docker仓库

  • 仓库:存放镜像
  • 注册服务器:存放仓库的具体服务器
  • 1个注册服务器有多个仓库,1个仓库有多个镜像
  • 仓库地址mydocker.com/ubuntu,mydocker.com为注册服务器地址,ubuntu为仓库名

5.1 docker hub公共镜像市场

登录
  • docker login
  • 注册成功后本地生成 .dockercfg 保存用户认证信息。
  • 登录成功后可上传镜像
基本操作
  • docker search
  • docker pull
自动创建
  • 镜像内程序需频繁升级,可通过Docker Hub指定跟踪一个目标网站,一旦项目发生新的提交,则自动创建。
  • 自动创建步骤
    • 登录 Docker Hub ,在目标网站中连接账户到 Docker Hub
    • 在 Docker Hub 中配置一个“自动创建”
    • 选取一个目标网站中的项目(需含Dockerfile)和分支
    • 指定Dockerfile位置,并提交创建

5.2 时速云镜像市场

5.3 搭建本地私有仓库

使用registry镜像创建私有仓库
  • 通过官方registry镜像来搭建本地私有仓库
    • docker run -d -p 5000:5000 registry
  • 默认仓库创建在容器的/tmp/registry下。存放到某目录
    • docker run -d -p 5000:5000 -v /mypath/registry:/tmp/registry / registry
管理私有仓库
  • 在远程机器a(10.0.2.2:5000)上搭建私有仓库
  • 在本地机器上,对某镜像打tag
    • docker tag ubuntu:14.04 10.0.2.2:5000/test
    • docker tag IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
  • 上传镜像
    • docker push 10.0.2.2:5000/test
  • 下载镜像
    • docker pull 10.0.2.2:5000/test

第6章 docker数据管理

  • 容器管理数据方式
    • 数据卷:容器内数据直接映射到本地主机环境
    • 数据卷容器:使用特定容器维护数据卷

6.1 数据卷

  • 将主机操作系统目录映射进容器,类似linux中的mount操作。
  • 优势
    • 容器间共享和重用、数据传递
    • 对数据卷内数据修改会立马生效
    • 对数据卷更新不会影响镜像,解耦应用与数据
    • 卷一直存在,直到没有容器使用,可安全缷载
容器内创建数据卷
  • 使用training/webapp镜像创建web容器,并创建数据卷挂载到容器的/webapp目录:
    • docker run -d -p –name web -v /webapp training/webapp python app.py
挂载一个主机目录作为数据卷
  • 加载主机的/src/webapp到容器的/opt/webapp
    • docker run -d -p –name web -v /src/webapp:/opt/webapp training/webapp python app.py
  • docker挂载数据卷的默认权限为rw,可通过ro指定为只读
    • docker run -d -p –name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py
挂载一个本地主机文件作为数据卷(不推荐)
  • docker run –rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

6.2 数据卷容器

  • 多容器间共享持续更新的数据,使用数据卷容器较方便。其也是容器,专用来提供数据卷供其他容器挂载。
  • 创建数据卷容器dbdata,并在其中创建一个数据卷挂载到/dbdata
    • docker run -it -v /dbdata –name dbdata ubuntu
    • 新建了一个名为dbdata的容器,容器中有个目录/dbdata,创建了数据卷挂载到容器dbdata的/dbdata目录下。
  • 其他容器(如db1、db2)挂载容器dbdata中的数据卷
    • docker run -it –volumes-from dbdata –name db1 ubuntu
    • docker run -it –volumes-from dbdata –name db2 ubuntu
    • 此时db1、db2都挂载同一个数据卷到相同的/dbdata目录。三个容器任何一方在该目录下写,其他容器均可见。
  • 多次使用–volumes-from从多个容器挂载多个数据卷。还可从其他已经挂载了容器卷的容器来挂载数据卷
    • docker run -d –name db3 –volumes-from db1 training/postgres
  • 使用–volumes-from所挂载数据卷的容器自身并不需要保持在运行状态。
  • 如删除挂载的容器,数据卷不会自动删除。如需删除数据卷,必须在删除最后一个还挂载着它的容器时显式使用docker rm -v命令来指定同时删除关联的容器。

6.3 利用数据卷容器来迁移数据

  • 可利用数据卷容器对其中的数据进行备份、恢复,以实现数据迁移。
备份
  • 备份dbdata数据卷容器内的数据卷
    • docker run –volumes-from dbdata -v $(pwd):/backup –name worker ubuntu tar cvf /backup/backup.tar /dbdata
    • 首先用ubuntu镜像创建了容器worker
    • 使用 –volumes-from dbdata 让worker容器挂载dbdata容器的数据卷(即dbdata数据卷)
    • 使用 -v $(pwd):/backup 来挂载本地的当前目录到worker容器的/backup目录
    • worker启动后,使用 tar cvf /backup/backup.tar /dbdata 将/dbdata下内容备份为容器内的 /backup/backup.tar,即宿主机当前目录下的backup.tar
恢复
  • 创建一个带数据卷的容器dbdata2
    • docker run -v /dbdata –name dbdata2 ubuntu /bin/bash
  • 创建另一个新容器,挂载dbdata2容器,并使用untar解压备份文件到所挂载的容器卷中
    • docker run –volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar

第7章 端口映射与容器互联

  • 多容器间协作
    • 网络
    • 映射容器内应用的服务端口到本地宿主主机
    • 互联机制实现多个容器间通过容器名来快速访问

7.1 端口映射实现访问容器

从外部访问容器应用
  • 默认情况,容器外无法通过网络访问容器内应用与服务。
  • -P 随机映射一个49000~49900端口到内部容器开放的网络端口
    • docker run -d -P training/webapp python app.py
    • 查看应用日志:docker logs -f CONTAINER_NAME
  • -p 指定映射端口,一个指定端口仅可绑定一个容器
    • IP:HostPort:ContainerPort
    • IP::ContainerPort
    • HostPort:ContainerPort
映射所有接口地址
  • HostPort:ContainerPort将本地5000端口映射到容器5000端口
    • docker run -d -p 5000:5000 training/webapp python app.py
    • 默认绑定本地所有接口上的所有地址
  • docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py
映射到指定地址的指定端口
  • IP:HostPort:ContainerPort指定映射使用一个特定地址
    • docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
映射到指定地址的任意端口
  • IP::ContainerPort绑定localhost任意端口到容器的5000端口,本地主机会自动分配一个端口
    • docker run -d -p 127.0.0.1::5000 training/webapp python app.py
  • 使用udp指定udp端口
    • docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
查看映射端口配置
  • docker port CONTAINER 5000
  • 容器有自己的内部网络和ip,查看容器具体信息
    • docker inspect CONTAINER_ID

7.2 互联机制实现便捷互动

  • 容器互联:多容器中应用快速交互。容器间建立连接,通过容器名快速访问,无需指定具体IP。
自定义容器命名
  • 创建容器时会有默认名称,但自定义名称:
    • 好记
    • 使用方便:连接其他容器时(即便重启)可使用容器名
  • docker run -d -P --name web training/webapp python app.py
  • 查看容器名
    • docker inspect -f “{ { .Name } }” CONTAINER_ID
  • 容器名唯一,如已命名web容器,当再次使用web名称时,需先docker rm删除之前的同名容器
  • 容器终止时立即删除容器
    • docker run … --rm
    • –rm与-d不可同时使用
容器互联
  • 现有容器db:docker run -d –name db training/postgres
  • 创建新容器web,并连接到db容器
    • docker run -d -P –name web --link db:db training/webapp python app.py
  • –link格式:–link name:alias
    • name:要连接的容器
    • alias:这个连接的别名
  • 此时存在2个容器:db,web。容器db的2个name:db、web/db。
  • docker 相当于在2个互联的容器间创建虚拟通道,无需映射容器端口到宿主机。启动db时并没有使用-p,避免暴露数据库服务端口到外部网络。
  • docker公开容器连接信息方式
    • 更新环境变量
    • 更新 /etc/hosts 文件
  • 查看web容器环境变量
    • docker run –rm –name web2 –link db:db training/webapp env
    • 环境变量中以 DB_开头(即以大写的连接别名开头)的环境变量为 web容器连接db容器使用的。
  • 除环境变量,docker还添加host信息到父容器(web)的 /etc/hosts 文件
    • docker run -t -i –rm –link db:db training/webapp /bin/bash
    • 进入父容器后执行:cat /etc/hosts
    • 由hosts文件可知:第1个为web容器自己,第2个为db容器的ip和主机名。
  • 可连接多个子容器到父容器,比如可连接多个web到同一个db容器。

7.3 本章小结

  • 生产环境更复杂,包括跨主机或跨数据中心通信,需引入额外机制,如SDN(软件定义网络)、NFV(网络功能虚拟化)
  • docker使用libnetwork实现跨主机通信

第8章 使用dockerfile创建镜像

8.1 基本结构

  1. 基础镜像信息
  2. 维护者信息
  3. 镜像操作指令
  4. 容器启动时执行指令

8.2 指令说明

指令 说明
FROM 指定所创建的镜像的基础镜像
MAINTAINER 指定维护者信息
RUN 运行命令
CMD 指定启动容器时默认执行的命令
LABEL 指定生成镜像的元数据标签信息
EXPOSE 声明镜像内服务所监听的端口
ENV 指定环境变量
ADD 复制指定的路径下的内容到容器的路径下,可以为URL;如果为tar文件,会自动解压到路径下
COPY 复制本地主机的路径下的内容到镜像中的路径下;一般情况下推荐使用COPY,而不是ADD
ENTRYPOINT 指定镜像的默认入口
VOLUME 创建数据卷挂载点
USER 指定运行容器时的用户名或UID
WORKDIR 配置工作目录
ARG 指定镜像内使用的参数(例如版本号信息等)
ONBUILD 配置当所创建的镜像作为其他镜像的基础镜像时,所执行的创建操作指令
STOPSIGNAL 容器退出的信号值
HEALTHCHECK 如何进行健康检查
SHELL 指定使用shell时的默认shell类型
FROM
  • 所创建镜像的基础镜像
  • 格式: FROM、FROM:、FROM@
  • dockerfile第一条指令必为FROM。同一dockerfile中创建多个镜像,可使用多个FROM(每个镜像一次)
MAINTAINER
  • 指定维护者信息
  • 格式:MAINTAINER
    • MAINTAINER image_creator@docker.com
  • 被写入生成镜像的Author属性域中。
RUN
  • 运行指定命令
  • 格式:RUN、RUN [“executable”,”param1”,”param2”]
    • RUN [“executable”,”param1”,”param2”]会被解析为Json数组,必须用双引号;使用exec执行,不会启动shell
    • RUN默认在shell中运行,即 /bin/sh -c
  • 每条Run指令在当前镜像基础上执行,并提交为新镜像
CMD
  • 启动容器时默认执行的命令
  • 格式
    • CMD [“executable”,”param1”,”param2”]:使用exec执行,推荐使用
    • CMD command param1 param2:在/bin/sh中执行,提供给需要交互的应用
    • CMD [“param1”,”param2”]:提供给ENTRYPOINT的默认参数
  • 每个dockerfile只能有一条CMD,若有多条只有最后一条生效
  • 用户启动容器时手动指定了运行命令(run参数),则覆盖CMD。
LABEL
  • 指定生成镜像的元数据标签信息
  • 格式:LABEL < key>=< value> < key>=< value> < key>=< value>…

    LABLE version="1.0"
    LABEL description="XXX"
    
EXPOSE
  • 镜像内服务所监听端口
  • 格式:EXPOST […]
    • EXPOST 22 80 8443
  • 仅声明,并不会自动完成端口映射
  • 启动容器时需使用 -P,docker主机会自动分配宿主机的临时端口转发到指定端口;-p可具体指定宿主机的哪个本地端口映射。
ENV
  • 指定环境变量,镜像生成过程中会被RUN指令使用,在镜像启动的容器中也会存在。
  • 格式:ENV < key> < value>、ENV < key>=< value>…

    ENV PG_MAJOR 9.3
    RUN XXX
    ENV PATH /usr/local/XXX:$PATH
    
  • 运行时可被覆盖,如docker run –env XXX build_image
ADD
  • 复制路径下内容到容器的
  • 格式:ADD
    • src可以是dockerfile所在目录的一个相对路径;也可为URL;或tar文件(tar文件会自动解压到dest下)
    • dest可为镜像内绝对路径,或相对于工作目录(WORKDIR)的相对路径。
    • ADD *.c /code
COPY
  • 复制本地主机(为dockerfile所在目录的相对路径、文件或目录)下内容到镜像中的下。目标路径不存在时自动创建
  • 格式:COPY
  • 使用本地目录为源目录时,推荐使用COPY
ENTRYPOINT
  • 指定镜像的默认入口命令,在启动容器时作为根命令执行,所有传入值作为该命令的参数。
  • 格式
    • ENTRYPOINT [“executable”,”param1”,”param2”]:exec调用执行
    • ENTRYPOINT command param1 param2:shell中执行
  • CMD指令的指定值将作为根命令参数
  • 每个dockerfile中只能有一个ENTRYPOINT,指定多个时仅最后一个生效。
  • 运行时可被–entrypoint覆盖
VOLUME
  • 创建数据卷挂载点
  • 格式:VOLUME [“/data”]
  • 可从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保存的数据
USER
  • 指定运行容器时的用户名或UID,后续RUN指令也会使用此用户。
  • 格式:USER daemon
  • 服务无需管理员权限时,可通过此命令指定运行用户,并可在之前创建所需用户
    • RUN groupadd -r postgres && useradd -r -g postgres postgres
  • 要临时获取管理员权限可使用gosu或sudo
WORKDIR
  • 为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。可多个WORKDIR,后续命令如为相对路径,则会基于之前指定的WORKDIR。
  • 格式:WORKDIR /path/XXX

    WORKDIR /a
    WORKDIR b
    WORKDIR c
    
    • 则最终路径为/a/b/c
ARG
  • 指定镜像内使用的参数,这些参数在执行docker build命令时才以–build-arg< varname>=< value>格式传入。
  • 格式:ARG< name>[=< default value>]
  • 可用 docker build –build-arg< name>=< value> 来指定参数值。
ONBUILD
  • 当创建的镜像作为其他镜像的基础镜像时,所执行的创建操作指令。
  • 格式:ONBUILD [INSTRUCTION]
  • 如image-A镜像的dockerfile包含

    ONBUILD ADD . /app/src
    ONBUILD RUN /usr/local/bin/python-build --dir /app/src
    
    • 则基于image-A创建新镜像时,新dockerfile中使用FROM image-A,会自动执行如上ONBUILD指令。
  • 使用ONBUILD指令的镜像,推荐在标签中注册,如ruby:1.9-onbuild

STOPSIGNAL
  • 指定所创建镜像启动的容器,接收退出的信号值
  • 格式:STOPSIGNAL signal
HEALTHCHECK
  • 配置所启动容器如何进行健康检查
  • 格式
    • HEALTHCHECK [OPTIONS] CMD command:根据所执行命令返回值是否为0来判断
    • HEALTHCHECK NONE:禁止基础镜像中的健康检查
  • OPTION支持
    • –interval=DURATION(默认30s):多久检查1次
    • –timeout=DURATION(默认30s):每次检查等待结果的超时
    • –retries=N(默认3):如失败,重试几次才确定失败
SHELL
  • 指定其他命令使用shell时的默认shell类型。
  • 格式:SHELL [“executable”,”parameters”]
    • 默认:[“/bin/bash”,”-c”]

8.3 创建镜像

  • 编写完dockerfile后通过 docker build 来创建镜像。
  • 指定dockerfile路径为/tmp/docker_builder/,且生成镜像标签为build_repo/first_image
    • docker build -t build_repo/first_image /tmp/docker_builder/
  • 一般建议放置dockerfile的目录为空目录。docker build将读取指定路径下的dockerfile,并将此路径下所有内容发送给docker服务端。

8.4 使用.dockerignore文件

  • 使用.dockerignore来让docker忽略匹配模式路径下的目录和文件。

8.5 最佳实践

  1. 精减镜像用途:避免大而复杂、多功能的镜像
  2. 选用合适的基础镜像:大镜像太臃肿,推荐小巧的debian镜像
  3. 提供足够清晰的命令注释和维护者信息:dockerfile文件清晰
  4. 正确使用版本号:使用明确版本号而不是latest
  5. 减少镜像层数:尽量合并指令(多个RUN合并为一条)可减少层数
  6. 及时删除临时文件和缓存文件:执行apt-get后/var/cache/apt下会缓存安装包
  7. 提高生成速度:合理用缓存,减少内容目录下文件;或使用.dockerignore
  8. 调整合理的指令顺序:开启缓存时,内容不变的指令尽量放前面,可尽量复用
  9. 减少外部源的干扰:使用外部资源需指定持久地址并带有版本信息

第9章 操作系统

9.1 BusyBox

  • 集成常用linux指令,只有几M大小(2M)。
  • docker pull busybox:latest
  • docker run -it busybox

9.2 Alpine

  • 面向安全的轻型linux发行版
  • 体积小:5M左右
  • 采用了 musl libc 和 busybox
使用官方镜像
  • docker run alpine echo ‘123’
迁移至Alpine基础镜像
  • 大部分docker官方镜像已支持alpine作为基础镜像,很容易进行迁移。
    • ubuntu/debian > alpine
    • python:2.7 > python:2.7-alpine
    • ruby:2.3 > ruby:2.3-alpine
  • 使用alpine替换ubuntu作基础镜像,需使用apk替换apt工具安装软件。
    • apk add –no-cache

9.3 Debian/Ubuntu

  • docker清除了ubuntu的apt仓库信息
    • apt-get update
    • apt-get install XXX

9.4 CentOS/Fedora

9.5 本章小结

  • 出于安全考虑,几乎所有官方镜像均无SSH服务,无法使用用户名、密码直接登录。

第10章 为镜像添加SSH服务

  • 使用attach、exec可进入容器内进行操作,但无法远程操作容器。远程需SSH

10.1 基于commit命令创建

  • 基于容器创建镜像:docker commit CONTAINER [REPOSITORY[:TAG]]
准备
  • docker run -it ubuntu:14.04 /bin/bash
  • 进入容器后执行命令:apt-get update
安装和配置SSH服务
  • 容器内执行命令:apt-get install openssh-server -y
    • /var/run/sshd 必须存在,不存在则手动创建。
  • 容器内启动SSH服务
    • mkdir -p /var/run/sshd
    • /usr/sbin/sshd -D &
  • 查看容器的22端口(SSH服务默认监听的端口)
    • netstat -tunlp
  • 修改SSH服务的安全登录配置,取消 pam 登录限制
    • sed -ri ‘s/session required pam_loginuid.so/#session required pam_loginuid.so/g’ /etc/pam.d/sshd
  • root用户目录下创建.ssh目录,复制需要登录的公钥信息(一般为本地主机用户目录下的.ssh/id_rsa.pub文件,可由ssh-keygen -t rsa命令生成)到authorized_keys文件中
    • mkdir root/.ssh
    • vi /root/.ssh/authorized_keys
  • 创建自动启动SSH服务的可执行文件run.sh,并添加可执行权限

    • vi /run.sh
    • chmod -x run.sh
    • run.sh内容如下

      #!/bin/bash
      /usr/sbin/sshd -D
      
  • 退出容器
    • exit
保存镜像
  • 将所退出的容器用docker commit保存为一个新的sshd:ubuntu镜像
    • docker commit fc1XXXX sshd:ubuntu
使用镜像
  • 启动容器,添加端口映射10022>22。10022为宿主主机端口,22为容器的SSH服务监听端口
    • docker run -p 10022:22 -d sshd:ubuntu /run.sh
  • 其他主机上使用ssh访问10022端口来登录容器
    • ssh 192.168.1.200 -p 10022

10.2 使用dockerfile创建

创建工作目录
  • 创建sshd_ubuntu工作目录
    • mkdir sshd_ubuntu
  • 创建dockerfile和run.sh
    • cd sshd_ubuntu
    • touch Dockerfile run.sh
编写 run.sh 脚本和 authorized_keys 文件
  • run.sh内容

    #!/bin/bash
    /usr/sbin/sshd -D
    
  • 宿主机上生成SSH密钥对,创建authorized_keys
    • ssh-keygen -t rsa
    • cat ~/.ssh/id_rsa.pub > authorized_keys
编写dockerfile
# 设置继承镜像
FROM ubuntu:14.04

# 提供一些作者的信息
MAINTAINER docker_user (user@docker.com)

# 下面开始运行更新命令
RUN apt-get update

# 安装ssh服务
RUN apt-get install -y openssh-server
RUN mkdir -p /var/run/sshd
RUN mkdir -p /root/.ssh
# 取消pam限制
RUN sed -ri 's/session required pam_loginuid.so/#session required pam_loginuid.so/g' /etc/pam.d/sshd

# 复制配置文件到相应位置,并赋予脚本可执行权限
ADD authorized_keys /root/.ssh/authorized_keys
ADD run.sh /run.sh
RUN chmod 755 /run.sh

# 开放端口
EXPOSE 22

# 设置自启动命令
CMD ["/run.sh"]
创建镜像
  • 在sshd_ubuntu目录下,使用docker build创建镜像。命令中的.表示使用当前目录中的dockerfile
    • cd sshd_ubuntu
    • docker build -t sshd:dockerfile .
  • 使用dockerfile创建自定义镜像,docker会删除中间临时创建的层。
测试镜像,运行容器
  • docker run -d -p 10122:22 sshd:dockerfile
  • ssh 192.168.1.200 -p 10122

第11章 Web服务与应用

  • 操作简单的镜像使用dockerfile创建;复杂应用(如weblogic)使用commit方式创建。

11.1 Apache

使用官方镜像
  • 编写dockerfile

    FROM httpd:2.4
    COPY ./public-html /usr/local/apache2/htdocs/
    
  • 创建public-html目录,创建index.html
  • 构建镜像
    • docker build -t apache2-image .
  • 运行镜像
    • docker run -it –rm –name apache-container -p 80:80 apacha2-image
  • 不创建镜像,通过映射目录方式运行apache
    • docker run -it –rm –name my-apache-app -p 80:80 -v “$PWD”:/usr/local/apache2/htdocs/ httpd:2.4
自定义镜像

11.2 Nginx

  • 支持协议:http、https、smtp、pop3、imap
  • 官方镜像:docker run -d -p 80:80 –name webserver nginx
  • 自定义web页面
    • docker run –name nginx-container -p 80:80 -v index.html:/usr/share/nginx/html:ro -d nginx
  • dockerfile构建镜像

    FROM nginx
    COPY ./index.html /usr/share/nginx/html
    
    • docker build -t my-nginx .
    • docker run –name nginx-container -d my-nginx

11.3 Tomcat

准备
  • 创建dockerfile、run.sh
    • mkdir tomcat7_jdk6
    • cd tomcat7_jdk6
    • touch Dockerfile run.sh
    • 下载tomcat并解压到tomcat7_jdk6
    • Dockerfile、run.sh、apache-tomcat-7.0.56 jdk
dockerfile文件、脚本文件
  • Dockerfile

    FROM sshd:dockerfile
    # 设置继承自用户创建的sshd镜像
    MAINTAINER docker_user (user@docker.com)
    # 下面是一些创建者的基本信息
    
    # 设置环境变量,所有操作都是非交互式的
    ENV DEBIAN_FRONTEND noninteractive
    
    # 注意这里要更改系统的时区设置
    RUN echo "Asia/Shanghai" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata
    
    # 安装跟 tomcat 用户认证相关的软件
    RUN apt-get install -yq --no-install-recommends wget pwgen ca-certificates && apt-get clean && rm -rf /var/lib/apt/lists/*
    
    # 设置 tomcat 的环境变量,若读者有其他环境变量需要设置,也可在此添加
    ENV CATALINA_HOME /tomcat
    ENV JAVA_HOME /jdk
    
    # 复制 tomcat 和 jdk 文件到镜像中
    ADD apache-tomcat-7.0.56 /tomcat
    ADD jdk /jdk
    
    ADD create_tomcat_admin_user.sh /create_tomcat_admin_user.sh
    ADD run.sh /run.sh
    RUN chmod +x /*.sh
    RUN chmod +x /tomcat/bin/*.sh
    
    EXPOSE 8080
    CMD ["/run.sh"]
    
  • 创建 tomcat 用户和密码脚本文件 create_tomcat_admin_user.sh

    #!/bin/bash
    
    if [ -f /.tomcat_admin_created ]; then
        echo "Tomcat 'admin' user already created"
        exit 0
    fi
    
    #generate password
    PASS=${TOMCAT_PASS:-$(pwgen -s 12 1)}
    _word=$( [ ${TOMCAT_PASS}] && echo "preset" || echo "random" )
    
    echo "=> Creating and admin user with a ${_word} password in Tomcat"
    sed -i -r 's/<\/tomcat-users>//' ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '<role rolename="manager-gui"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '<role rolename="manager-script"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '<role rolename="manager-jmx"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '<role rolename="admin-gui"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '<role rolename="admin"-script"/>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo "<user username=\"admin\" password=\"${PASS}\" roles=\"manager-gui,manager-script,manager-jmx,admin-gui,admin-script\"/>" >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo '</tomcat-users>' >> ${CATALINA_HOME}/conf/tomcat-users.xml
    echo "=> Done!"
    touch /.tomcat_admin_created
    
    echo "======================"
    echo "You can now configure to this Tomcat server using:"
    echo ""
    echo "   admin:${PASS}"
    echo ""
    echo "======================"
    
  • run.sh

    #!/bin/bash
    
    if [ ! -f /.tomcat_admin_created ]; then
        /create_tomcat_admin_user.sh
    fi
    /usr/sbin/sshd -D & exec ${CATALINA_HOME}/bin/catalina.sh run
    
创建、测试镜像
  • docker build -t tomcat7:jdk6
  • docker run -d -P tomcat7:jdk6
  • 获取 tomcat 密码:docker logs CONTAINER_ID
  • docker ps
  • 可用-v来挂载Tomcat日志文件、程序所在目录、tomcat相关配置。

11.4 Jetty

  • docker run -d jetty
    • docker run -d -p 80:8080 -p 443:8443 jetty
  • container-ip:8080

11.5 LAMP

linode/lamp镜像
  • docker run -p 80:80 -t -i linode/lamp /bin/bash
  • 进入容器启动apache、mysql
    • service apache2 start
    • service mysql start
tutum/lamp镜像
  • docker run -d -p 80:80 -p 3306:3306 tutum/lamp

11.6 CMS

11.6.1 WordPress
  • 基于 PHP 和 Mysql
  • 使用官方镜像
    • 下载:docker pull wordpress
    • 创建wordpress容器,连接mysql容器
      • docker run –name some-wordpress –link some-mysql:mysql -p 8080:80 -d wordpress
  • 使用Compose一键搭建Wordpress应用

    • 创建docker-compose.yml

      wordpress:
          image: wordpress
          links:
              - db:mysql
          ports:
              - 8080:80
      
      db:
          -image: mariadb
          environment:
              MYSQL_ROOT_PASSWORD: example
      
    • 执行:docker-compose up

    • 如无docker-compose命令,可通过pip install docker-compose在线安装
    • 通过80端口打开wordpress配置页。
11.6.2 Ghost
  • 开源博客平台,Javascript编写。
  • docker run –name ghost-container -d ghost
    • 默认监听2368端口
    • docker run –name ghost-container -p 8080:2368 -d ghost
  • 挂载已有的内容到Ghost容器内
    • docker run –name some-ghost -v /path/to/ghost/blog:/var/lib/ghost ghost

11.7 持续开发与管理

  • 持续集成CI,持续交付CD
11.7.1 Jenkins
  • jenkins官方提供了Docker镜像
  • 宿主机:mkdir jenkins_data
  • 数据持久化
    • 数据卷机制
      • docker run -p 8080:8080 -p 50000:50000 -v /var/jenkins_data:/var/jenkins_home jenkins
    • 数据卷容器
      • docker run –name myjenkins -p 8080:8080 -p 50000:50000 -v /var/jenkins_home jenkins
11.7.2 Gitlab
  • gitlab官方提供DockerHub镜像
  • 命令

    docker run --detach \
        --hostname ip:port \
        --publish 443:443 --publish 80:80 --publish 23:23 \
        --name gitlab \
        --restart always \
        --volume /srv/gitlab/config:/etc/gitlab \
        --volume /srv/gitlab/logs:/var/log/gitlab \
        --volume /srv/gitlab/data:/var/opt/gitlab \
        gitlab/gitlab-ce:latest
    
    • hostname指外网ip或域名,git项目地址即为http://ip:port/projectname
    • 本地目录/srv/gitlab/*提前创建好
    • 几分钟后gitlab启动成功

本章小结

  • 对于程序代码、程序资源目录、日志、数据库文件等需实时更新的数据,一定要通过-v映射到宿主主机的目录,避免数据丢失。

第12章 数据库应用

12.1 MySQL

  • 官方镜像
    • docker run –name hi-mysql -e MYSQL_ROOT_PASSWORD=123 -d mysql:latest
    • 以上容器名为hi-mysql,密码为123
  • 将应用容器连接到mysql容器

    • docker run –name some-app –link some-mysql:mysql -d application-that-uses-mysql
    • 修改mysql配置
      • docker run -it –link some-mysql:mysql –rm mysql sh -c ‘exec mysql -h”$MYSQL_PORT_3306_TCP_ADDR” -P”$MYSQL_PORT_3306_TCP_PORT” -uroot -p”$MYSQL_ENV_MYSQL_ROOT_PASSWORD”‘
  • mysql容器作为客户端连接非docker或远程mysql实例

    • docker run -it –rm mysql mysql -hsome.mysql.host -usome-mysql-user -p
系统与日志访问
  • 使用docker exec指令调用内部系统bash shell,以访问容器内部系统
    • docker exec -it some-mysql bash
  • 查看mysql日志
    • docker logs some-mysql
使用自定义配置文件
  • 创建cnf配置文件(如/mypath/config-file.cnf),并挂载至容器的/etc/mysql/conf.d目录
    • docker run –name some-mysql -v /mypath:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
  • 新容器启动后会结合使用/etc/mysql/my.cnf和/mypath/config-file.cnf两个配置文件。
脱离cnf文件进行配置
  • 很多配置项可通过标签传给mysqld进程,这样用户可脱离cnf对容器进行弹性的定制。
  • 改变表编码为uft8mb4
    • docker run –name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag –character-set-server=utf8mb4 –collation-server=utf8mb4_unicode_ci
  • 查看命令
    • docker run -it –rm mysql:tag –verbose –help

12.2 MongoDB

12.2.1 使用官方镜像
  • docker run –name mongo-container -d mongo
  • 进入mongo容器
    • docker exec -it CONTAINER_ID sh
    • 启动mongo:mongo
    • show dbs
    • db.status()
  • 查看环境变量:容器内执行 env 命令
  • 默认端口 27017

  • 连接已创建的 mongodb 容器

    • docker run -it –link mongo-container:db alpine sh
    • ping db
  • 直接使用 mongo cli 指令
    • 在宿主机上直接使用 mongodb 镜像,可在 docker run 后加入 entrypoint 指令,即可直接进入 mongo cli 了。
    • docker run -it –link mongo-container:db –entrypoint mongo mongo –host db
    • 可设置存储引擎:docker run –name mongo-container -d mongo –storageEngine wiredTiger
12.2.2 使用自定义Dockerfile
  • 创建dockerfile

    # 设置从用户之前创建的 sshd 镜像继承
    FROM sshd
    MAINTAINER docker_user (user@docker.com)
    
    RUN apt-get update && apt-get install -y mongodb pwgen && apt-get clean && rm -rf /var/lib/apt/lists/*
    
    # 创建 mongodb 存放数据文件的文件夹
    RUN mkdir -p /data/db
    VOLUME /data/db
    
    ENV AUTH yes
    
    # 添加脚本
    ADD run.sh /run.sh
    ADD set_mongodb_password.sh /set_mongodb_password.sh
    RUN chmod 755 ./*.sh
    
    EXPOSE 27017
    EXPOSE 28017
    
    CMD ["/run.sh"]
    
  • set_mongodb_password.sh:配置数据库的用户名和密码

    #!/bin/bash
    # 这个脚本主要是用来设置数据库的用户名和密码
    
    # 判断是否已经设置过密码
    if [ -f /.mongodb_password_set ]; then
        echo "MongoDB password already set!"
        exit0
    fi
    
    /usr/bin/mongod --smallfiles --nojournal &
    PASS=${MONGODB_PASS:-$(pwgen -s 12 1)}
    _word=$( [ ${MONGODB_PASS}] && echo "preset" || echo "random" )
    
    RET=1
    while [[ RET -ne 0]]; do
        echo "=> Waiting for confirmation of MongoDB service startup"
        sleep 5
        mongo admin --eval "help" >/dev/null 2>&1
        RET=$?
    done
    
    # 通过 docker logs + id 可看到下面的输出
    echo "=> Creating an admin user with a ${_word} password in MongoDB"
    mongo admin --eval "db.addUser({user: 'admin', pwd: '$PASS', roles: [ 'userAdminAnyDatabase', 'dbAdminAnyDatabase']});"
    mongo admin --eval "db.shutdownServer();"
    
    echo "=> Done!"
    touch /.mongodb_password_set
    
    echo "==========="
    echo "You can now connect to this MongoDB server using:"
    echo ""
    echo "  mongo admin -u admin -p $PASS --host <host> --port <port>"
    echo ""
    echo "Please remember to change the above password as soon as possible!"
    echo "==========="
    
  • run.sh:mongodb启动脚本

    #!/bin/bash
    if [ ! -f /.mongodb_password_set ]; then
        /set_mongodb_password.sh
    fi
    
    if [ "$AUTH" == "yes"]; then
    # 这里读者可以自己设定 Mongodb 的启动参数
        export mongodb='/usr/bin/mongod --nojournal --auth --httpinterface --rest'
    else
        export mongodb='/usr/bin/mongod --nojournal --httpinterface --rest'
    fi
    
    if [ ! -f /data/db/mongod.lock ];then
        eval $mongodb
    else
        export mongodb=$mongodb' --dbpath /data/db'
        rm /data/db/mongod.lock
        mongod --dbpath /data/db --repair && eval $mongodb
    fi
    
  • 构建镜像
    • docker build -t mongodb-image .
  • 启动容器,并分别映射 27017、28017 端口到本地
    • docker run -d -p 27017:27017 -p 28017:28017 mongodb
  • 查看默认的admin密码
    • docker logs CONTAINER_ID
    • 容器启动时指定密码:docker run -d -p 27017:27017 -p 28017:28017 -e MONGODB_PASS=”mypass” mongodb
    • 不设定密码:docker run -d -p 27017:27017 -p 28017:28017 -e AUTH=no mongodb

12.3 Redis

  • docker run –name redis-container -d redis
  • 容器中启动bash:docker exec -it CONTAINER_ID bash
连接redis容器
  • docker run -it –link redis-container:db alpine sh
  • 进入容器后
    • ping db
    • 检测redis服务可用性:nc db 6379
  • 官方镜像自事了redis客户端
    • docker run -it –link redis-container:db –entrypoint redis-cli redis -h db
使用自定义配置
  • docker run -v /myredis/conf/redis.conf:/usr/local/etc/redis/redis.conf –name myredis redis redis-server /usr/local/etc/redis/redis.conf

12.4 Memcached

  • docker run –name memcached-container -d memcached
  • 设定 memcached server 内存大小
    • docker run –name memcached-container -d memcached memcached -m64
  • 宿主机测试
    • telnet 192.168.99.100 11211

12.5 CouchDB

  • 面向文档的 nosql 数据库,json格式存数据。兼容 ACID
  • CouchDB侧重AP(可用性和分区容忍度);MongoDB侧重CP(一致性和分区容忍度),Neo4j则提供特有的面向图形的结构。
  • docker run -d –name couchdb-container couchdb
    • 默认端口:5984
  • 容器链接:docker run –name couchdb-app –link couchdb-container:couch couchdb

12.6 Cassandra

  • 实现容错及无单点故障
  • 引入p2p技术,具备大规模可分区行存储能力。支持 Spart、Storm、Hadoop集成,类似工具有 HBase。
  • docker run –name my-cassandra -d cassandra:latest
  • 将另一容器的应用与Cassandra容器连接,应用容器需暴露Cassandra需使用的端口(Cassandra默认服务端口为rpc_host:9160;CQL默认本地服务端口为native_transport_host:9042)
    • docker run –name my-app –link my-cassandra:cassandra -d app-this-uses-cassandra
搭建 cassandra 集群
  • 单机模式(所有实例集中到一台机器)、多机模式(实例分布在多台机器)
  • 如需启动更多实例,需在指令中配置首个实例信息
    • docker run –name my-cassandra2 -d -e CASSANDRA_SEEDS=”$(docker inspect –format=’{ { .NetworkSettings.IPAddress } }’ my-cassandra)” cassasndra:latest
    • my-cassandra即为首个 Cassandra Server 的实例名。docker inspect 用以获取首个实例ip地址。
  • 连接2个cassandra实例
    • docker run –name my-cassandra2 -d –link my-cassandra:cassandra cassasndra:latest
  • 多机模式,需通过环境变量配置 Cassandra Server 容器的IP广播地址(-e标签)。如容器1的ip为10.22.22.22,容器2 的ip为10.23.23.23,Gossip端口为 7000
    • 启动容器1的Cassandra实例
      • docker run –name my-cassandra -d -e CASSANDRA_BROADCAST_ADDRESS=10.22.22.22 -p 7000:7000 cassandra:latest
    • 启动容器2的Cassandra实例,同样需暴露 Gossip 端口,并通过环境变量声明容器1的 IP
      • docker run –name my-cassandra -d -e CASSANDRA_BROADCAST_ADDRESS=10.23.23.23 -p 7000:7000 -e CASSANDRA_SEEDS=10.22.22.22 cassandra:latest

12.7 本章小结

  • 数据库文件映射到宿主主机:一方面减少容器文件系统带来的性能损耗,另一方面实现数据持久化。

第13章 分布式处理与大数据平台

13.1 RabbitMQ

  • 支持AMQP的开源消息队列实现,Erlang编写。
  • 客户端:java、python、php、.net、ruby、javascript。
  • 消息发送者与接收者间无须知道彼此的存在。
  • 重要组件
    • 服务端:Broker包括Exchange、Queue)
    • 客户端:Producer、Consumer
    • Producer 产生消息,发送给 Exchange,经由 Queue,最终由 Consumer 接收。
  • RabbitMQ 将数据存储在 Node 中,默认为 hostname。运行容器时指定每个 rabbitmq daemon 运行的主机名,以便管理和维护数据。
    • docker run -d –hostname my-rabbit –name some-rabbit rabbitmq:3
  • 使用 rabbitmqctl 工具进行远程管理(或跨容器管理),需设置持久化的 cookie。
    • docker run -d –hostname my-rabbit –name some-rabbit -e RABBITMQ_ERLANG_COOKIE=’secret cookie here’ rabbitmq:3
  • 使用 cookie 连接至1个独立的实例
    • docker run -it –rm –link some-rabbit:my-rabbit -e RABBITMQ_ERLANG_COOKIE=’secret cookie here’ rabbitmq:3 bash
    • rabbitmyctl -n rabbit@my-rabbit list_users
    • 使用 RABBITMQ_NODENAME 简化指令
      • docker run -it –rm –link some-rabbit:my-rabbit -e RABBITMQ_ERLANG_COOKIE=’secret cookie here’ -e RABBITMQ_NODENAME=rabbit@my-rabbit rabbitmq:3 bash
  • 默认rabbitmq会启动一些管控插件,如 rabbitmq:3-management,通过默认用户名密码及标准管控端口 15672 访问插件。
    • docker run -d –hostname my-rabbit –name some-rabbit rabbitmq:3-management
    • http://container-ip:15672
    • 如需从宿主机外访问,可使用 8080 端口
      • docker run -d –hostname my-rabbit –name some-rabbit -p 8080:15672 rabbitmq:3-management
  • 修改默认用户名密码
    • docker run -d –hostname my-rabbit –name some-rabbit -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password rabbitmq:3-management
  • 修改默认 vhost
    • docker run -d –hostname my-rabbit –name some-rabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost rabbitmq:3-management
  • 连接到 daemon
    • docker run –name some-app –link some-rabbit:rabbit -d application-that-uses-rabbitmq

13.2 Celery

  • 分布式任务处理系统,专注实时处理的任务队列管理,同时支持任务调度。
  • 基于python实现,可集成 Django、Pyramid、Pylons、Flask、Tornado 等web框架。
  • Celery 可单机运行,也可多机或跨数据中心运行。
使用官方镜像
  • 启动1个 celery worker,即 RabbitMQ Broker
    • docker run –link some-rabbit:rabbit –name some-celery -d celery:latest
    • 检查集群状态
      • docker run –link some-rabbit:rabbit –rm celery celery status
  • 启动1个 celery worker,即 Redis Broker
    • docker run –link some-redis:redis -e CELERY_BROKER_URL=redis://redis –name some-celery -d celery
    • 检查集群状态
      • docker run –link some-redis:redis -e CELERY_BROKER_URL=redis://redis –rm celery celery status
使用 Celery 库
  • 如用户的web框架已有 Celery 库,则更方便。Python 调用 Celery 示例:

    from celery import Celery
    app = Celery('hello', broker='amqp://guest@localhost//')
    
    @app.task
    def hello();
        return 'hello world'
    

13.3 Hadoop

  • HDFS:文件系统;YARN:资源管理系统(相当于Linux的进程调度和内存分配模块);MapReduce:运行在YARN上的应用,负责分布式处理管理。
使用官方镜像
  • docker run -it sequenceiq/hadoop-docker:2.7.0 /etc/bootstrap.sh -bash
  • 查看namenode日志
    • cat /usr/local/hadoop/logs/hadoop-root-namenode-d4e1e9d8f24f.out
安装验证(验证 hadoop 是否安装成功)
  • 打开容器bash
    • cd /usr/local/hadoop
    • bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.0.jar grep input output ‘dfs[a-z.]+’
    • 检查输出结果:bin/hdfs dfs -cat output/*

13.4 Spark

  • 速度、易用性、复杂分析;基于 Scala。
  • 与 Hadoop 和 Storm 及 MapReduce 技术相比,Spark 支持更灵活的函数定义,处理速度提升1到2个数量级。更多的实用工具:SQL查询、流处理、机器学习、图处理。
  • 三大组件:数据存储(HDFS)、API(Java,Scala,Python)、管理框架(Mesos,YARN)。
13.4.1 使用官方镜像
  • docker pull sequenceiq/spark:1.6.0
    • docker build –rm -t sequenceiq/spark:1.6.0
  • docker run -it -p 8088:8088 -p 8042:8042 -h sanbox sequenceiq/spark:1.6.0 bash
    • cat /usr/local/hadoop/logs/hadoop-root-namenode-sanbox.out
13.4.2 验证
  • YARN 客户端模式

    • 客户端模式,SparkContext(驱动程序)运行在客户端进程中,应用的master仅处理来自 YARN 的资源管理请求

      # 运行spark shell
      spark-shell --master yarn-client --driver-memory 1g --executor-memory 1g --executor-cores 1
      
      # 执行以下命令,若返回1000则符合预期
      scala> sc.parallelize(1 to 1000).count()
      
  • YARN 集群模式

    • 集群模式,Spark driver驱动运行于应用 master 的进程中,即由 YARN 从集群层面进行管理。
  • 容器外访问 Spark

    • 设置 YARN_CONF_DIR 环境变量,yarn-remote-client 文件夹内置远程访问的配置信息:export YARN_CONF_DOR=”pwd/yarn-remote-client”
    • 只能使用 root 用户访问 Docker 的 HDFS 环境。当用户从容器集群外使用非 root 用户访问 Spark 环境时,需配置 HADOOP_USER_NAME 环境变量:export HADOOP_USER_NAME=root

13.5 Storm

  • 实时流计算框架,Twitter开源,基于 Clojure 语言开发
  • Storm与Hadoop类似,Hadoop 运行 MapReduce,Storm 运行 topolopy。MapReduce 任务完成即结束,topolopy 则永远等待消息并处理。
使用 Compose 搭建 Storm 集群
  • 利用 Docker Compose 模板,可在本地单机 Docker 环境快速搭建 Storm 集群,进行开发测试。
  • 包含的容器
    • Zookeeper:Apache Zookeeper 三节点部署。
    • Nimbus:Storm Nimbus
    • Ui:Storm UI
    • Supervisor:Storm Supervisor(一个或多个)
    • Topology:Topolopy 部署工具。
  • 本地开发测试
    • git clone https://github.com/denverdino/docker-storm.git
    • cd docker-swarm/local
    • docker-compose build
    • 一键部署一个 Storm 应用:docker-compose up -d
    • 容器启动后,访问容器的 8080 端口打开操作界面。
    • 伸缩 supervisor 数量:docker-compose scale supervisor=3
    • web 界面中无运行中的topolopy,因为 Docker Compose 只能保证容器启动顺序,无法确保所依赖容器中的应用已完全启动并可被正常访问。再次启动 topolopy 服务来提交更新的拓扑
      • docker-compose start topolopy

13.6 ElasticSearch

  • 实时分布式数据存储与查询分析,易扩展到上百台服务器,支持 PB 级结构化或非结构化数据。配合 Logstash、Kibana 组件,可快速构建对日志消息的分析平台。
  • docker run -d elasticsearch
    • docker run -d elasticsearch elasticsearch -Des.node.name=”TestNode”
  • 镜像内包含默认配置文件,如使用自定义配置,可使用数据卷,挂载到/usr/share/elasticsearch/config
    • docker run -d -v “$PWD/config”:/usr/share/elasticsearch/config elasticsearch
  • 数据持久化
    • docker run -d -v “$PWD/esdata”:/usr/share/elasticsearch/data elasticsearch
  • 镜像暴露 9200 9300 端口,9200对外提供API,9300 用于内部通信(心跳、集群内信息同步)

第14章 编程开发

14.1 C/C++

  • 三款 C/C++ 开发工具:GCC、LLVM、Clang
14.1.1 GCC
  • GNU 开发的编译器
  • GCC 可处理 C/C++、Fortran、Pascal、Objective-C、Java、Ada
  • 将编译指令写入 Dockerfile,再使用此 Dockerfile 构建自定义镜像,最后直接运行此镜像,即可启动程序。
  • Dockerfile

    FROM gcc:4.9
    COPY . /usr/src/myapp
    WORKDIR /usr/src/myapp
    RUN gcc -o myapp main.c
    CMD ["./myapp"]
    
  • main.c

    #include<stdio.h>
    
    int main(){
        printf("Hello World!");
        return 0;
    }
    
  • docker build -t gcc-image .
  • docker run -it –rm –name gcc-container gcc-image
  • 如只需编译不需运行
    • docker run –rm -v “$(pwd)”:/usr/src/myapp -w /usr/src/myapp gcc gcc -o myapp main.c
    • 此命令将当前目录 “$(pwd)” 挂载到容器的 /usr/src/myapp 目录,并执行 gcc -o myapp myapp.c,GCC 将编译 myapp.c 并将生成的可执行文件输出至 /usr/src/myapp 文件夹。
14.1.2 LLVM
  • docker pull imiell/llvm
14.1.3 Clang
  • Apple 用 C++,基于 LLVM 实现
  • docker pull bowery/clang

14.2 Java

新建目录,创建 Dockerfile
FROM java:7
COPY . /usr/src/javaapp
WORKDIR /usr/src/javaapp
RUN javac HelloWorld.java
CMD ["java", "HelloWorld"]
构建镜像
  • docker build -t java-image .
运行镜像
  • docker run -it –rm –name java-container java-image
  • 如只需在容器中编译Java程序,不需运行
    • docker run –rm -v “$(pwd)”:/usr/src/javaapp -w /usr/src/javaapp java:7 javac HelloWorld.java

14.3 Python

  • 解释型动态语言,面向对象
14.3.1 使用官方的Python镜像
  • 创建 Dockerfile

    FROM python:3-onbuild
    CMD [ "python3.5", "./py3-sample.py" ]
    
  • py3-sample.py 为一段 python 代码
  • touch requirements.txt
  • 创建镜像:docker build -t py3-image .
  • docker run -it –rm –name py3-container py3-image
    • 只运行单个 python 脚本,无需用 Dockerfile 构建镜像,可直接用命令运行官方镜像
      • docker run -it –rm –name my-running-script -v “$(pwd)”:/usr/src/myapp -w /usr/src/myapp python:3 python your-daemon-or-script.py
14.3.2 使用PyPy
  • Python 实现的解释器和即时编译工具,通过 JIT 技术可使 Python 运行速度提高近十倍

14.4 JavaScript

创建 Dockerfile
FROM node:4-onbuild
EXPOSE 8888
新建 server.js
'use strict';

var connect = require('connect');
var serveStatic = require('serve-static');

var app = connect();
app.use('/', serveStatic('.',{'index':['index.html']}));
app.listen(8080);

console.log('MyApp is ready at http://localhost:8080');
npm init 命令新建 node 项目必须的 package.json
docker build -t node-image .
docker run -it -P node-image
  • 若只需运行单个 node 脚本的容器,无需 Dockerfile 构建镜像
    • docker run -it –rm –name my-running-script -v “$(pwd)”:/usr/src/myapp -w /usr/src/myapp node:0.10 node your-daemon-or-script.js

14.5 Go

  • 拥有 C/C++ 的运行和编译效率。
  • 提供轻量级协程,支持大规模并发。
14.5.1 搭建并运行Go容器
  • 官方镜像:docker run -it golang /bin/bash
  • 自定义镜像,创建 Dockerfile
  • Go 项目容器化
14.5.2 Beego
  • Go 应用程序开源框架
  • 使用方法

    • go get github.com/astaxie/beego
    • 创建 hello.go

      package main
      
      import "github.com/astaxie/beego"
      
      func main() {
          beego.Run()
      }
      
    • 编译:go build -o hello hello.go
    • 运行:./hello
14.5.3 Gogs:基于Go的Git服务
  • docker run –rm –name gogs gogs/gogs
  • 停止:docker stop gogs
  • 删除:docker rm gogs
  • 数据持久化:先创建数据文件夹,将其作为数据卷挂载至gogs容器中
    • mkdir -p /srv/lxc/gogs/data
    • docker run -d –name gogs -p 8300:3000 -p 8322:22 -v /srv/lxc/gogs/data:/data gogs/gogs

14.6 PHP

14.7 Ruby

14.7.1 使用Ruby官方镜像
14.7.2 JRuby
14.7.3 Ruby on Rails

14.8 Perl

14.9 R

14.10 Erlang

14.11 本章小结


第15章 容器与云服务

15.1 公有云容器服务

15.1.1 AWS
15.1.2 Google Cloud Platform
15.1.3 Azure
15.1.4 腾讯云
15.1.5 阿里云
15.1.6 华为云
15.1.7 UCloud

15.2 容器云服务

15.2.1 基本要素与关键特性
15.2.2 网易蜂巢
15.2.3 时速云
15.2.4 Daocloud
15.2.5 灵雀云
15.2.6 数人云

15.3 阿里云容器服务

15.4 时速云容器平台

15.5 本章小结


第16章 容器实战思考

16.1 docker为什么会成功

16.2 研发人员该如何看容器

16.3 容器化开发模式

16.4 容器与生产环境

16.5 本章小结


第17章 docker核心实现技术

  • Linux 操作系统命名空间、控制组、联合文件系统、Linux 网络虚拟化支持

17.1 基本架构

  • C/S 架构,客户端和服务端既可运行在一个机器上,也可运行在不同机器上通过 socket 或 restful api 来进行通信。
服务端
  • Docker Daemon 一般运行在宿主主机后台,作为服务端接受客户请求(创建、运行、分发容器)。
  • Docker Daemon 模块化设计,通过专门的 Engine 模块来分发管理客户端任务。
  • Docker 服务端默认监听本地 unix:///var/run/docker.sock 套接字,只允许root或docker用户组成员访问。
    • 修改监听方式,如监听tcp的1234端口:docker daemon -H 0.0.0.0:1234
  • Docker 还支持通过 https 认证方式来验证访问。
客户端
  • 客户端默认通过本地 unix:///var/run/docker.sock 套接字向服务端发送命令。若服务端不是监听在默认地址,需客户端执行命令时显示指定服务端地址。
    • docker -H tcp://127.0.0.1:1234 version
新的架构设计
  • 原架构必须保证 Docker daemon 正常运行,既要管理容器的运行时,也要提供对外api响应。一旦 Docker daemon 不正常,则运行在 Docker 上的容器也无法使用。
  • 新架构将容器运行任务放到一个单独的组件 containerd 中来管理。原对客户端 api 的支持仍在 Docker daemon 。

17.2 命名空间

  • 命名空间为linux内核特性,每个容器有自己单独的命名空间。保证容器间互不影响。
  • 操作系统中,内核、文件系统、网络、PID、UID、IPC、内存、硬盘、CPU 均为应用进程直接共享。让进程在彼此隔离的命名空间中运行,彼此不可见。
进程命名空间
  • linux 通过命名空间管理进程号,同一进程在不同命名空间中,看到的进程号不相同。
  • 进程命名空间是父子关系结构,子空间中的进程对父空间可见。新 fork 的进程在父命名空间和子命名空间分别有一个进程号。
网络命名空间
  • 通过网络命名空间实现网络隔离。
  • 网络命名空间为进程提供了完全独立的网络协议栈视图,这样每个容器的网络可隔离开
    • 网络设备接口
    • IPv4 和 IPv6 协议栈
    • IP 路由表
    • 防火墙规则
    • sockets
  • Docker 采用虚拟网络设备的方式,将不同命名空间的网络设备连接在一起。默认,容器中虚拟网卡将同本地主机上的 docker0 网桥连接在一起
  • 桥接到宿主机 docker0 网桥上的虚拟网口:brctl show
IPC命名空间
  • 容器中进程交互采用 linux 常见的进程间交互方法:信号量、消息队列、共享内存。
  • PIC 命名空间和 IPC 命名空间可组合使用,同一个 IPC 命名空间内的进程彼此可见;不同空间的进程无法交互。
挂载命名空间
  • 类似 chroot,将一个进程放到一个特定的目录执行。
  • 挂载命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间中的进程看到的文件目录彼此隔离。
UTS命名空间
  • unix time-sharing system 命名空间允许每个容器拥有独立主机名和域名。
  • 默认 docker 容器的主机名为容器id。
用户命名空间
  • 每个容器可有不同的用户和组 id,可在容器内使用特定的内部用户执行程序,而非本地系统上存在的用户。
  • 每个容器内都可有 root 账号,但与宿主机主机不在一个命名空间。
  • 使用隔离的用户命名空间可提高安全性,避免容器内进程获取额外权限。

17.3 控制组

  • 控制组为linux内核特性,用于对共享资源进行隔离、限制、审计。
  • 提控容器对内存、CPU、磁盘IO等资源进行限制和计费管理。
  • 控制组提供
    • 资源限制:将组设置为不超过设定的内存限制。如内存子系统可为进程组设置内存上限,一旦进程组达到上限后再申请内存将 Out of Memory 警告。
    • 优先级:通过优先级让一些组优先得到更多的cpu等资源。
    • 资源审计:统计使用多少资源,如可用cpuacct子系统记录某个进程组使用的cpu时间。
    • 隔离:为组隔离命名空间,一个组不会看到另一个组的进程、网络连接、文件系统。
    • 控制:挂起、恢复、重启
  • 查看Docker组应用的各种限制项
    • cd /sys/fs/cgroup/memory/docker
    • ls
    • 可修改文件值来控制Docker应用资源
  • 进入对应的容器文件夹,可看到对应容器的状态
    • cd 42352bb6c
    • ls
    • 开发容器工具时,往往需要一些容器运行状态数据,可从这里获取

17.4 联合文件系统

  • 联合文件系统:轻量级高性能分层文件系统,它支持将文件系统中的修改信息作为一次提交,层层叠加,同时可将不同目录挂载到同一个虚拟文件系统下,应用看到的是挂载的最终结果。
  • 联合文件系统是实现 Docker 镜像的技术基础。Docker 镜像可通过分层来进行继承。
Docker 存储
  • 查看镜像由哪些层组成
    • docker history image_name
  • Docker 镜像层不可修改、只读。使用 Docker 镜像启动容器时,将在镜像文件系统的最顶端再挂载一个新的可读写的层给容器。容器中的内容更新将发生在可读写层。
    • 当所操作对象位于较深的层时,需要先复制到最上层的可读写层。
    • 当数据对象较大时,往往意味着IO性能较差。因此,一般推荐将容器修改的数据通过 volume 方式挂载,而不是直接修改镜像内数据。
    • 对频繁启停Docker容器的情况下,文件系统的 IO 性能也十分关键。
  • Docker 所有的存储都在 Docker 目录下,Ubuntu 默认路径为/var/lib/docker
    • 此目录下存储Docker镜像和容器相关的文件和目录,包括:aufs、containers、graph、image、init、linkgraph.db、network、repositories-aufs、swarm、tmp、trust、volumes等。
      • aufs保存Docker镜像相关数据和信息。包括 layers、diff 和 mnt 三个子目录。
      • layers 包含层属性文件,用来保存各个镜像层的元数据
      • diff 包含层内容子目录,用来保存所有镜像层的内容数据
      • mnt 下子目录是各容器最终挂载点,所有相关的 AUFS 层在这里挂载到一起,形成最终效果。一个运行中容器的根文件系统就挂载在这下面的子目录上。
多种文件系统比较
  • Docker 支持的联合文件系统有:AUFS、OverlayFS、btrfs、vfs、zfs、Device Mapper
  • AUFS、Device Mapper 应用更广泛。

17.5 Linux网络虚拟化

  • Docker 本地网络实现利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 veth pair)。
基本原理
  • 要实现网络通信,机器至少需要一个网络接口(物理或虚拟),收发数据包。不同子网间通信,还需要额外的路由机制。
  • Docker 中网络接口默认为虚拟接口,最大优势是转发效率高。Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发。即发送接口的发送缓存中的数据包直接复制到接收接口的接收缓存中。
  • Docker 容器网络在本地主机和容器内分别创建一个虚拟接口,彼此连通,称为 veth pair。
网络创建过程
  • 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中
  • 本地主机端的虚拟接口,连接到默认的 docker0 网桥或指定网桥上,且有一个以 veth 开头的唯一名字,如 veth1234。
  • 容器端的虚拟接口放在新创建的容器中,修改名字为 eth0。此接口仅在容器的命名空间可见。
  • 从网桥可用地址段中获取一个空闲地址分配给容器的 eth0,并配置默认路由网关为 docker0 网卡的内部接口 docker0 的 ip 地址。
  • docker run 启动容器时,可通过 –net 参数指定容器的网络配置
    • –net=bridge:默认,在 Docker 网桥 docker0 上为容器创建新的网络栈。
    • –net=none:让 Docker 将新容器放到隔离的网络栈中,但不进行网络配置。
    • –net=container:NAME_or_ID:新容器与某已存在的容器共享 IP 地址和端口等网络资源,2者进程可直接通过lo环回接口通信。
    • –net=host:容器使用本地主机网络,此时容器拥有完全的本地主机接口访问权限。容器进程可跟主机其他root进程一样打开低范围的端口,可访问本地网络服务(如D-bus)。甚至可配置主机的网络栈,此选项要特别小心
    • –net=user_defined_network:用户自行用network命令创建一个网络,此方式将容器连接到指定的已创建网络上去。
手动配置网络
  • 使用–net=none后,Docker不对容器网络进行配置,需手动完成配置。
    • 启动容器,设定–net=none
      • docker run -it –rm –net=none base /bin/bash
    • 本地主机中查找容器进程id,并为它创建网络命名空间
      • docker inspect -f ‘{ {.State.Pid} }’ CONTAINER_ID
      • pid=XXX
      • sudo mkdir -p /var/run/netns
      • sudo ln -s /proc/$pid/ns/net /var/run/netns/$pid
    • 检查桥接网卡的IP和子网掩码
      • ip addr show docker0
    • 创建一对“veth pair”接口A和B,绑定A接口到网桥docker0,并启用
      • sudo ip link add A type veth peer name B
      • sudo brctl addif docker0 A
      • sudo ip link set A up
    • 将B接口放到容器的网络命名空间,命名为etho,启动它并配置一个可用ip(桥接网段)和默认网关
      • sudo ip link set B netns $pid
      • sudo ip netns exec $pid ip link set dev B name eth0
      • sudo ip netns exec $pid ip link set eth0 up
      • sudo ip netns exec $pid ip addr add 172.17.42.99/16 dev eth0
      • sudo ip netns exec $pid ip route add default via 172.17.42.1
  • 容器终止后,Docker清空容器,容器内网络接口会随网络命名空间一起被清除,A接口也会自动从docker0缷载并清除。

17.6 本章小结


第18章 配置私有仓库

18.1 安装Docker Registry

18.2 配置TLS证书

18.3 管理访问权限

18.4 配置Registry

18.4.1 示例配置
18.4.2 选项

18.5 批量管理镜像

18.6 使用通知系统

18.6.1 相关配置
18.6.2 Notification的使用场景

18.7 本章小结


第19章 安全防护与配置

  • Docker 安全性依赖于 Linux 系统自身
    • Linux 内核的命名空间机制提供的容器隔离安全
    • Linux 控制组机制对容器资源的控制能力安全
    • Linux 内核的能力机制所带来的操作权限安全
    • Docker程序本身的抗攻击性
    • 其他安全增强机制(AppArmor、SELinux)对容器安全性的影响
    • 第三方工具(Docker Bench)对 Docker 环境的安全性进行评估

19.1 命名空间隔离的安全

  • docker run 启动容器,Docker 在后台为容器创建一个独立的命名空间。
  • 命名空间提供了直接的隔离,容器中进程不会被本地主机进程影响。
  • 所有容器实际上通过本地主机的网桥接口(docker0)通信。
  • 与虚拟机相比,命名空间实现的隔离并不绝对。容器中应用可直接访问系统内核和部分系统文件。用户必须保证容器中应用是安全可信的,否则本地系统会受威胁。即必须保证镜像来源和自身可靠。

19.2 控制组资源控制的安全

  • 控制组实现资源的审计和限制。
  • docker run 启动容器,Docker 通过 Linux 相关的调用,在后台为容器创建一个独立的控制组策略集合,该集合将限制容器内应用对资源的消耗。
  • 控制组作用
    • 各容器可公平分享主机的CPU、内存、磁盘IO等
    • 限制容器对资源占用,当某容器对资源消耗过大,不会影响本地主机系统和其他容器
  • 控制组虽不负责隔离容器间相互访问、处理数据和进程,但防止恶意攻击(特别是DDOS)方面十分有效。

19.3 内核能力机制

  • 能力机制为Linux内核特性,可提供细粒度权限访问控制。即可作用于进程,也可作用于文件。
    • 如某web进程仅需绑定一个小于1024端口的权限,不需root权限。那么只需被授权 net_bing_service 能力即可。
  • 默认,Docker启动的容器被限制只允许使用内核的一部分能力,包括chown、dac_override、fowner、kill、setgid、setuid、setpcap、net_bind_service、net_raw、sys_chroot、mknod、setfcap、audit_write等
  • 通常,服务器上会运行一些特权进程,如ssh、cron、syslogd、硬件管理工具、网络配置工具等。容器与这些进程不同,因为几乎所有特权进程都由容器外的支持系统来进行管理。
  • 攻击者在容器中获取root,也不能获得本地主机较高权限,破坏有限。
  • 不恰当分配内核能力,会导致容器内应用获取破坏本地系统的权限。
  • 默认,Docker采用白名单机制,禁用了必需的一些能力之外的其他权限。
    • 支持CAP_CHOWN、CAP_DAC_OVERRIDE、CAP_FSETID、CAP_FOWNER、CAP_MKNOD、CAP_NET_RAW、CAP_SETGID、CAP_SETUID、CAP_SETFCAP、CAP_SETPCAP、CAP_NET_BIND_SERVICE、CAP_SYS_CHROOT、CAP_KILL、CAP_AUDIT_WRITE等
    • 用户也可根据需要为 Docker 容器启用额外权限。

19.4 Docker服务端的防护

  • Docker核心为Docker服务端,目前Docker服务的运行需root权限支持。

19.5 更多安全特性的使用

19.6 使用第三方检测工具

19.6.1 Docker Bench
19.6.2 clair
  • 对容器的文件层进行扫描发现漏洞

19.7 本章小结


第20章 高级网络功能

20.1 网络启动与配置参数

  • Docker 启动时自动在主机上创建 docker0 虚拟网桥,实际为 Linux 网桥,类似于软件交换机,会在挂载其上的接口间进行转发。
  • Docker 随机分配一个本地未占用的私有网段中的一个地址给 docker0 接口。
  • 创建一个Docker容器时,同时创建一对 veth pair 接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即eth0;另一端在本地并被挂载到 docker0 网桥,名称以 veth 开头。通过这种方式,主机可跟容器通信,容器间也可相互通信。如此,Docker 创建了主机和所有容器间一个虚拟共享网络。
  • Docker 网络命令参数,仅 Docker 服务启动时使用:
    • -b BRIDGE or –bridge=BRIDGE:指定容器挂载的网桥
    • –bip=CIDR:定制 docker0 的掩码
    • -H SOCKET … or –host=SOCKET … :Docker服务端接收命令的通道
    • –icc=true|false:是否支持容器间通信
    • –ip-forward=true|false:启用 net.ipv4.ip_forward,即打开转发功能
    • –iptables=true|false:禁止Docker添加iptables规则
    • –mtu=BYTES:容器网络中的MTU
  • Docker 网络命令,服务启动及Docker容器启动均可用的命令:
    • –dns=IP_ADDRESS:使用指定的DNS服务器
    • –dns-search=DOMAIN:指定DNS搜索域
  • Docker 网络命令,仅docker run时可用:
    • -h HOSTNAME or –hostname=HOSTNAME:配置容器主机名
    • –link=CONTAINER_NAME:ALIAS:添加到另一个容器的连接
    • –net=bridge|none|container:NAME_or_ID|host|user_defined_netword:配置容器的桥接模式
      • –net=bridge:默认配置,为容器创建独立网络命名空间,分配网卡、IP地址等网络配置,并通过veth接口对将容器挂载到一个虚拟网桥(默认docker0)上
      • –net=none:为容器创建独立网络命名空间,但不进行网络配置,即容器内没有创建网卡、IP地址
      • –net=container:NAME_or_ID:新创建的容器共享指定的已存在容器的网络命名空间,2个容器内的网络配置共享,但其他资源(进程空间、文件系统)还是相互隔离的
      • –net=host:不为容器创建独立命名空间,容器内看到的网络配置(网卡、路由表、Iptables规则)均与主机保持一致。注意其他资源还是与主机隔离的
      • –net=user_defined_network:用户自行用network命令创建网络,同一网络内容器彼此可见,可采用更多类型的网络插件。
    • -p SPEC or –publish=SPEC:映射容器端口到宿主主机
    • -P or –publish-all=true|false:映射容器所有端口到宿主主机

20.2 配置容器DNS和主机名

  • 20.3 容器访问控制

    20.4 映射容器端口到宿主主机的实现

    20.5 配置docker0网桥

    20.6 自定义网桥

    20.7 使用OpenvSwitch网桥

    20.8 创建一个点到点连接

    20.9 本章小结


第21章 libnetwork插件化网络功能

21.1 容器网络模型

21.2 docker网络相关命令

21.3 构建跨主机容器网络

21.4 本章小结


第22章 Etcd–高可用的键值数据库

22.1 简介

22.2 安装和使用Etcd

22.3 使用etcdctl客户端

22.3.1 数据类操作
22.3.2 非数据类操作

22.4 Etcd集群管理

22.4.1 构建集群
22.4.2 集群参数配置

22.5 本章小结


第23章 Docker三剑客之Docker Machine

23.1 简介

  • 对 Docker 主机本身进行管理,由 go 编写,用户可在本地任意指定被 Machine 管理的 Docker 主机,并对其进行操作。
  • Machine 定位:在本地或云环境中创建 Docker 主机。
    • 在指定节点上安装 Docker 引擎,配置其为 Docker 主机
    • 集中管理所有 Docker 主机。
  • Machine 连接不同类型的节点是通过不同驱动实现的。

23.2 安装Machine

Linux 平台上的安装
Mac OS系统上的安装
Windows 系统上的安装
  • 安装 git-bash
    if [[ ! -d "$HOME/bin"]]; then mkdir -p "$HOME/bin"; fi
    curl -L https://github.com/docker/machine/releases/download/v0.8.0/docker-machine_windows-amd64.zip > machine.zip
    unzip machine.zip
    rm machine.zip
    mv -f docker-machine* "$HOME/bin"
    

23.3 使用Machine

  • Docker Machine 通过多种后端驱动来管理不同资源,包括虚拟机、本地主机、云平台
虚拟化平台
  • 可通过virtualbox驱动支持本地启动一个虚拟机并配置为 Docker 主机
    • docker-machine create –driver=virtualbox vbox-instance
    • 将启动一个全新虚拟机,并安装Docker引擎
  • 还支持 Microsoft Hyper-V 虚拟化平台
本地主机
  • 此种驱动适合主机操作系统和ssh服务已装好,需对其安装 Docker 引擎
  • 确保本地主机可通过 user 账号的 key 直接通过ssh连到目标主机。使用 generic 类型驱动,注册一台 Docker 主机,命名为 test
    • docker-machine create -d generic –generic-ip-address=10.0.100.102 –generic-ssh-user=user test
    • Machine通过 ssh 连到指定节点并在上面安装了docker引擎
  • 创建主机成功后,通过 docker-machine ls 命令来查看注册到本地管理列表中的 docker 主机
    • docker-machine ls
  • inspect命令查看指定 docker 主机具体信
云平台驱动
  • 配置 AWS 上的虚拟机为 Docker 主机,需指定 AccessKeyId、SecretAccessKey、VpcId
    • docker-machine create –driver amazonec2 –amazonec2-access-key AKIXXX –amazonec2-secret-key 8T93CXXX –amazonec2-vpc-id vpc-XXX aws_instance

23.4 Machine命令

  • 查看用法:docker-machine -h
命令 说明
active 查看当前激活状态的Docker主机
config 查看到激活Docker主机的连接信息
create 创建一个Docker主机
env 显示连接到某个主机需要的环境变量
inspect 以json格式输出指定Docker主机的详细信息
ip 获取指定Docker主机地址
kill 直接杀死指定的Docker主机
ls 列出所有管理的主机
regenerate-certs 为某个主机重新生成TLS认证信息
restart 重启指定Docker主机
rm 删除某台Docker主机。对应虚拟机会被删除
scp 在Docker主机间及Docker主机与本地间通过scp命令来远程复制文件
ssh 通过ssh连到主机上,执行命令
start 启动一个指定的docker主机。如对象是虚拟机,该虚拟机将被启用
status 获取指定Docker主机状态,包括Running、Paused、Saved、Stopped、Stopping、Starting、Error等
stop 停止一个Docker主机
upgrade 将指定主机的Docker版本更新为最新
url 获取指定Docker主机的监听URL
help,h 输出帮助信息
active
  • docker-machine active [arg…]
  • 查看当前激活状态的Docker主机。激活状态意味着当前的DOCKER_HOST环境变量指向该主机。
config
  • create
    env
    inspect
    ip
    kill
    ls

23.5 本章小结


第24章 Docker三剑客之Docker Compose

24.1简介

24.2 安装与卸载

24.3 Compose命令说明

24.4 Compose环境变量

24.5 Compose模板文件

24.6 Compose应用案例一:Web负载均衡

24.7 Compose应用案例二:大数据Spark集群

24.8 本章小结


第25章 Docker三剑客之Docker Swarm

25.1 简介

25.2 安装Swarm

25.3 使用Swarm

25.4 使用其他服务发现后端

25.5 Swarm中的调度器

25.6 Swarm中的过滤器

25.7 本章小结


第26章 Mesos–优秀的集群资源调度平台

26.1 简介

26.2 Mesos安装与使用

26.3 原理与架构

26.3.1 架构
26.3.2 基本单元
26.3.3 调度
26.3.4 高可用性

26.4 Mesos配置项解析

26.4.1 通用项
26.4.2 master专属项
26.4.3 slave专属项

26.5 日志与监控

26.6 常见应用框架

26.7 本章小结


第27章 Kubernetes–生产级容器集群平台

27.1 简介

27.2 核心概念

27.2.1 集群组件
27.2.2 资源抽象
27.2.3 辅助概念

27.3 快速体验

27.4 安装部署

27.5 重要组件

27.5.1 Etcd
27.5.2 kube-apiserver
27.5.3 kube-scheduler
27.5.4 kube-controller-manager
27.5.5 kubelet
27.5.6 kube-proxy

27.6 使用kubectl

27.6.1 获取kubectl
27.6.2 命令格式
27.6.3 全局参数
27.6.4 子命令

27.7 网络设计

27.8 本章小结


第28章 其他相关项目

28.1 平台即服务方案

28.1.1 Deis
28.1.2 Flynn

28.2 持续集成平台 Drone

28.3 容器管理

28.3.1 Citadel
28.3.2 Shipyard
28.3.3 DockerUI
28.3.4 Panamax
28.3.5 Seagull
28.3.6 Dockerboard

28.4 编程开发

28.5 网络支持

28.5.1 pipework
28.5.2 Flannel
28.5.3 Weave Net
28.5.4 Calico

28.6 日志处理

28.6.1 Docker-Fluentd
28.6.2 Logspout
28.6.3 Sematext-agent-docker

28.7 服务代理工具

28.7.1 Traefik
28.7.2 Muguet
28.7.3 nginx-proxy

28.8 标准与规范

28.9 其他项目

28.9.1 CoreOS
28.9.2 OpenStack支持
28.9.3 dockerize
28.9.4 Unikernel
28.9.5 容器化的虚拟机

28.10 本章小结


附录

附录A 常见问题总结

附录B Docker命令查询

附录C 参考资源链接


Kommentare: