镜像的分层框架 image-layering framework

    最底端是引导文件系统 bootfs. 当一个容器启动后,它将被移到内存,而 bootfs 会被 unmount,以留出更多内存供 initrd 磁盘镜像使用

    第二层是根文件系统 rootfs

    • 传统 Linux 引导过程,rootfs 会首先只读方式加载、引导结束并完成完整性检查后切换为读写模式
    • Docker 中 rootfs 永远是只读状态,Docker 利用 union mount 技术会在 rootfs 上加载更多的 RO 文件系统

    联合加载 ( union mount ) - 一次同时加载多个文件系统,但在外面看起来只能看到一个文件系统;但各层文件会叠加、最终 FS 包含所有的文件和目录

    Docker 将这样的文件系统称为镜像。(基于镜像栈的关系,有 parent image / base image 概念)

    最终当从一个镜像启动容器时,Docker 会在镜像的最顶层加载一个读写文件系统,我们想在镜像中运行的程序、就是在这个 RW 层执行的。当文件系统变化时(如修改一个文件),会从下层的 RO 层复制到该 RW 层,即写时复制 ( copy on write )

    Dockerfile

    Dockerfile 所在目录为构建上下文 ( build context ),Docker 会在构建时将文件和目录上传到 Docker daemon,因此通过 .dockerignore 减少不必要的文件拷贝,另外 Dockerfile 中的 Copy 等命令无法访问 context 之外的文件。

    Dockerfile 中指令是自上而下执行,每条指令会创建一个新的镜像层 ( Layer ) 并提交(并返回一个 Image ID),这样每一层同时也被缓存。缓存特性会加速大部分情况下的镜像构建的,但对于类似 apt-get update 指令需要指定 --no-cache 以忽略

    修改模板中的 ENV 环境变量,后续指令会重置缓存

    FROM ubuntu 14:04
    EVN REFRESHED_AT 2014-07-01
    RUN apt-get -qq update
    

    EXPOSE

    告诉 Docker 该容器内的程序会使用容器的指定端口,但不会自动打开、需要 docker run 运行容器时 -P 公开所有通用 EXPOSE 指令公开的端口(或 -p 指定同宿主机的端口映射),通过 docker port <container> <port> 查看

    CMD

    容器启动时运行的命令(不同于 RUN 是容器构建时的命令),只能指定一条 CMD 指令;如果需要启动多进程或执行多指令,考虑使用 Supervisor 一类的服务管理工具;此外 docker run 命令会覆盖 CMD 指令

    CMD ["/bin/ls", "-l"]
    

    ENTRYPOINT

    比较容易同 CMD 混淆;docker run 命令行的参数会传递给 ENTRYPOINT 指令中指定的命令

    ENTRYPOINT ["/usr/sbin/nginx"]
    # ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
    # docker run -it <image> -g "daemon off;"
    

    组合使用 ENTRYPOINT 和 CMD,可以构建一个镜像,既可以运行默认指令、也支持通过 docker run 来覆盖

    ENTRYPOINT ["/usr/sbin/nginx"]
    CMD ["-h"]
    

    ENV

    构建过程的环境变量,可在后续指令中使用。也会持久化到创建的容器中,运行时环境变量可被 docker run -e 标志覆盖

    VOLUME

    向基于镜像创建的容器添加卷,一个卷是可以存在于一个或多个容器内的特定目录,这个目录可以绕过 Union FileSystem,并提供如下数据持久化功能:

    • 卷可以在容器间共享和重用(而不需要提交到镜像)
    • 对卷的修改立即生效
    • 对卷的修改不会对更新镜像产生影响

    通过 docker run -v 指令指定容器卷,docker run --volumes-from <container> 把指定容器的卷加入新创建的容器;容器文件复制使用 docker cp 命令;

    一个日志容器:

    docker run -d --name logstash \
        --volumes-from redis \
        --volumes-from nodeapp \
        logstash
    docker logs -f logstash
    

    其他指令

    • WORKDIR: 为 Dockerfile 中后续指令设置工作目录
    • USER: 指定启动容器的用户身份(如 USER user:group, USER uid:gid),默认为 root
    • COPY / ADD: 将构建上下文中的文件和目录复制到镜像,COPY 不会做文件解压,COPY 的目的位置必须是容器内部一个绝对路径,由 COPY 创建的文件及目录的 UID 和 GID 都会设置为 0
    • LABEL: 为镜像添加元数据(键值对),可以用 docker inspect 指令查看
    • ARG: 在 docker build 时传递给构建运行时的变量,可以设定默认值

    Docker 网络

    docker network create <network-name>
    docker network ls
    docker network inspect <network-name>
    docker network connect <network-name> <container>
    
    docker run --add-host=docker:10.0.0.1 --name app ...
    # 在 /etc/hosts 文件中添加名为 docker,地址为 10.0.0.1 的宿主机记录;容器内 env 查看环境变量
    

    镜像管理

    docker images
    docker history <image>
    
    # 安装 `dive` ( _brew info dive_ ) 查看镜像的层次结构
    dive <image>
    
    # 删除
    docker rmi <image>
    

    容器管理

    查看容器

    docker stats
    docker ps —no-trunc
    docker top <container>
    

    启动容器

    # 特权模式启动容器,对宿主机有 root 访问权限
    docker run --priviledged ...
    
    # 对于用完即抛的容器,--rm 自动删除
    docker run --rm ...
    

    连接容器,使用卷或网络接口而非通过 SSH

    docker exec -it <container> /bin/sh
    docker kill -s <signal> <container>
    

    Docker Machine

    Docker 在 Linux 操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制。Docker 利用 Linux 核心中的资源分离机制,例如 cgroups,以及 Linux 核心命名空间 ( namespace ),来建立独立的软件容器 ( containers ). Docker 以 C/S 模式执行,最初基于同一个 Binary 启动 client 及 daemon 的,新版本中已分离。

    Client 和 Daemon 建立请求的方式:

    • tcp://host:port
    • unix://path/to/sock
    • fd://socket_fd

    Docker Remote API


    容器编排

    服务发现是分布式应用程序之间管理相互关系的一种机制。服务发现允许某个组件在想要与其他组件交互时,自动找到对方。由于这些应用本身是分布式的,服务发现机制也需要是分布式的。而且,服务发现作为分布式应用不同组件之间的“胶水”,其本身还需要足够动态、可靠,适应性强,而且可以快速且一致地共享关于这些服务的数据。

    另外,Docker 主要关注分布式应用以及面向服务架构与微服务架构。这些关注点很适合与某个服务发现工具集成。每个 Docker 容器可以将其中运行的服务注册到服务发现工具里。注册的信息可以是 IP 地址或者端口,或两者都有,以便服务之间进行交互。

    Consul 是一个使用 Raft 一致性算法的特殊数据存储器;Consul 暴露了键值存储系统和服务分类系统,并提供高可用性、高容错能力,并保证强一致性。Consul 还提供了根据 API 进行服务分类,代替了大部分传统服务发现工具的键值对存储。