美文网首页
(三)Docker高级应用

(三)Docker高级应用

作者: 啊冬啊冬 | 来源:发表于2018-08-08 15:30 被阅读0次

    Docker中的文件和数据

    Docker镜像通过UnionFS进行分层存储(可以通过docker history查看),一个Docker镜像本质上是一组文件,默认存储在/var/lib/docker/<storage-driver>中。

    而Docker 容器其实是在镜像的最上层加了一层读写层,通常也称为容器层。在运行中的容器里所做的改动,如写新文件、修改已有文件、删除文件等操作其实都写到了容器层。容器层删除了,最上层的读写层跟着也删除了,改动自然也丢失了。

    若要持久化这些改动,一是通过 docker commit <containerId> [repository[:tag]]将当前容器保存成为一个新镜像。若想将持久化数据,或是多个容器间共享数据,需将数据存储主机的文件系统中。

    types-of-mounts.png

    数据卷(Volume) - 为什么是特殊的目录?

    • 处于UFS(Union File System)之外
    • 主机文件系统中的普通目录
    • 在卷上的I/O性能应与主机上的完全相同
    • 卷的内容不包含在Docker镜像中
    • 任何对卷内容的修改不是镜像的一部分
    • 可被多个容器共享和重用
    • 持久化数据(即使容器已被删除)

    如何初始化卷?

    volume-data-container.png
    • 从主机上挂载卷: 与容器共享主机目录

      • Docker卷允许在主机操作系统和容器之间共享任意目录/文件
      • 需要确保在主机操作系统上外部配置文件是可访问的
      • 动态伸缩的约束
    • 从其他容器挂载卷(数据容器):数据容器会作为一个接口, 向其他容器提供访问卷的途径

      • 容器可处理一组卷
      • 能限制访问权限,如只读或读写: docker run --volume-from some-container
      • 在多个容器间共享卷
      • 卷内容会保持同步
      • 底层使用相同的目录

    使用数据容器的场景?

    • 存储持久化数据库

      • 启动数据容器
      • 启动基于数据库镜像的数据库容器
      • 外部化数据目录 - 通过从数据容器挂载卷
      • 安排数据容器的备份计划
    • 配置文件

    • 数据文件

    实践操作

    1. 访问Docker Host虚拟机

    因为在MacOS上的Docker实际是运行在一个虚拟环境下的,所以/var/lib/docker路径无法在Mac上找到。我们需要连接到Docker Host虚拟机,才能查看到Docker的默认目录。

    $ screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty
    linuxkit-025000000001:~# ls /var/lib/docker/
    builder     containers  network     plugins     swarm       trust
    containerd  image       overlay2    runtimes    tmp         volumes
    linuxkit-025000000001:~# ls /var/lib/docker/volumes/
    metadata.db
    
    1. 在"主机"上挂载一个卷
    # 创建匿名卷
    $ docker volume create
    4997a11f62aff64766a2ce490debf1770a0c3f29dae57326579fefd0c3668673
    # 创建卷
    $ docker volume create my-volume
    my-volume
    

    这样创建的卷会默认在/var/lib/docker/volumes/下,可以使用docker volume进行统一管理

    1. 使用卷
    # 启动容器,挂载my-volume卷到容器的/data;
    $ docker run -dit --name c-my-volume -v my-volume:/data busybox
    a9ee3a9c98009a64865eb98da92f7bdd052634e44861f59d42051fe7015dbcd0
    # 查看是否挂载成功;
    $ docker exec c-my-volume ls | grep data
    data
    # 启动容器,自动生成匿名卷并挂载到容器的/data;
    $ docker run -dit --name c-anonymous -v :/data busybox
    ede60b7d7c514ef3b8d5f4d94cfa994b19da7680f13a5539ac64948fe65deb0e
    # 查看匿名卷对应的目录;
    $ docker inspect -f {{.Mounts}} c-anonymous
    
    1. 向卷中写入数据
    • 宿主机(Docker Host)向卷写入数据
    linuxkit-025000000001:/var/lib/docker/volumes/my-volume/_data# touch test
    linuxkit-025000000001:/var/lib/docker/volumes/my-volume/_data# echo "Hello" > test
    
    • 容器卷中检查修改文件
    $ docker exec c-my-volume cat /data/test
    Hello
    $ docker exec -it c-my-volume sh
    / # echo "Hi" > /data/test
    
    • 宿主机(Docker Host)检查
    linuxkit-025000000001:/var/lib/docker/volumes/my-volume/_data# cat test
    Hi
    
    1. 使用数据容器
    • Dockerfile
    FROM busybox
    VOLUME /config # 声明卷的挂载点
    CMD ls /config
    
    • Shell
    $ docker build -t data-container -f Dockerfile-2
    # 创建数据容器
    $ docker create --name data-container data-container
    610dc7f0df53adec4a8ee02e7f8f2a4856a29f5be3007c1fb69e9863d7960af1
    $ docker inspect -f {{.Mounts}} data-container
    [{volume e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa /var/lib/docker/volumes/e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa/_data /config local  true }]
    # 启动服务容器,引用数据容器
    $ docker run -dit --name service-container --volumes-from data-container busybox
    # 文件系统中修改
    linuxkit-025000000001:/var/lib/docker/volumes/e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa/_data# touch test
    # 查看服务容器
    $ docker exec service-container ls /config
    
    1. 运行时添加文件
    # 复制文件到docker容器中
    $ touch test-2
    $ docker cp test-2 service-container:/config/
    # 文件系统中查看
    linuxkit-025000000001:/var/lib/docker/volumes/e715b7a35b3f70b536a5eab136815d0deb4926b7b782b99168471c648ffc5ffa/_data# ls
    test    test-2
    

    Mount挂载

    文件挂载有多种方式,Volume卷只是提供了统一的管理方式。我们可以使用mount进行自定义配置,包括绑定任意文件目录,设置读写规则等。

    • --mount:由多个键值对组成,由逗号分隔,每一个由 <key>=<value> 元祖组成。键值对没有顺序。
      • type,可以是 bind,volume,tmpfs。
      • source,主机上的文件或目录的路径。可能用 src,source 指定。
      • destination,容器中的文件或目录的路径。可能用 destination,dst,target 指定。
      • readonly,如果存在,将更改 Propagation,可以是一个 rprivate。
      • consistency,如果存在,可以是 consistent,delegated 或 cached,只在 Mac 版有效。
      • --mount 标志不支持 z 或 Z 修改 selinux。
    • -v 绝对路径:容器内路径:默认使用bind模式挂载目录
    1. 直接挂载主机目录
    # 启动容器,挂载本机目录(绝对路径)到容器的/data;
    $ docker run -dit --name c-temp-path -v /temp:/data busybox
    a9ee3a9c98009a64865eb98da92f7bdd052634e44861f59d42051fe7015dbcd0
    # 查看是否挂载成功;
    $ docker exec c-temp-path ls /data
    Shared
    username
    $ docker inspect c-temp-path
    ...
    "Mounts": [
        {
            "Type": "bind",
            "Source": "/Users",
            "Destination": "/data",
            "Mode": "",
            "RW": true,
            "Propagation": "rprivate"
        }
    ],
    ...
    

    可以挂载任意路径,但是无法用volume进行管理。

    卷插件

    卷插件允许第三方数据管理解决方案接入
    在不修改应用的情况下,可以用另一个插件替换当前插件

    • Flocker
    • Convoy
    • Blockbridge
    • GlusterFS
    • Netshare
    • Openstorage

    卷的应用场景

    配置文件: 一次构建,多处部署

    • 将配置文件植入到容器
      • 在 Dockerfile 中使用 'COPY' 指令
      • 在镜像构建时使用 'RUN' 指令修改配置
      • 最简方案(静态配置的情况下)
      • 任何对配置文件的修改都需要重新构建镜像
    • 使用环境变量, 动态传入到容器
      • 当启动容器时传入环境变量: docker run -e USERNAME=zzz PASSWORD=...
      • 简单配置情况下运行良好
    • 查询键-值存储
      • 利用键-值存储获取配置信息
      • 很多可选项,如 consul, etcd, zookeeper
      • 使得配置更加动态化
      • 引入外部依赖,可用性更加重要
    • 从主机上挂载卷
    • 从其他容器挂载卷

    Tips

    • Volumes!=Persistance(卷并不意味着持久化)
    • 卷并不会被垃圾回收
    • 针对有状态的容器(如数据库)和相应的数据容器,可以使用相同的镜像
    • 为数据容器制定备份计划
    • 将带有敏感数据的容器放在的当前主机上
    • 尽可能缓存镜像,因为下载很耗时

    网络管理

    docker-networks.png

    Docker 为我们提供了四种不同的网络模式

    • bridge:未指定网络时, 容器默认在bridge网络下
    • container:CONTAINER_NAME:重用某容器的网络配置(ip/mac),等同于在同一容器内
    • host:(唯一)共享了宿主机的网络, 使用相同的ip
    • none:(唯一)关闭所有网络连接

    docker network ls中包含了默认的host、none和一个bridge网络,当启动的容器没有指定网络时,会默认加入到bridge网络中

    Bridge 网桥模式

    net-bridge.png
    • 启动Docker时会在主机上创建一个名为docker0的虚拟网桥接口
    • docker0 会为每一个容器分配一个新的 IP 地址并将 docker0 的 IP 地址设置为默认的网关
    • docker0 通过 iptables 中的配置与宿主机器上的网卡相连,所有符合条件的请求都会通过 iptables 转发到 docker0 并由网桥分发给对应的机器
    • 端口映射通过修改 iptables 再将对端口的访问重定向到 docker0,实现对容器的访问


      network-bridge-forward.png
    # 启动容器并映射端口
    $ docker run -dit --name bridge-container -p 5000 busybox
    cb52033e0b8f9c5a8e4c18e5a1fe0ac6d01809393575c1c85b3cdd5aacea437f
    # 查看容器服务的端口
    $ docker port bridge-container 
    5000/tcp -> 0.0.0.0:32771
    # 查看容器的IP地址
    $ docker inspect --format '{{ .NetworkSettings.IPAddress }}' bridge-container
    172.17.0.2
    # 查看默认网络配置和其中的容器
    $ docker network inspect bridge
    ...
    "IPAM": {
        ...
        "Config": [
            {
                "Subnet": "172.17.0.0/16",
                "Gateway": "172.17.0.1"
            }
        ]
    },
    "Containers": {
        "cb52033e0b8f9c5a8e4c18e5a1fe0ac6d01809393575c1c85b3cdd5aacea437f": {
            "Name": "bridge-container",
            "EndpointID": "b4af63f885a783563aeabdb70806983cb519a497835209686954a599fb81bc7a",
            "MacAddress": "02:42:ac:11:00:02",
            "IPv4Address": "172.17.0.2/16",
            "IPv6Address": ""
        }
    },
    ...
    

    网络和Links

    • docker run --link <target container> <image>
      • Links只能对同一主机上的容器生效
      • 容器在重新部署时会断开与其他容器的连接
      • 容器在创建后才能被相互Link

    容器网络模型 - 组件

    container-network-model.png
    • 沙箱
      • 包含容器网络堆栈配置信息:容器的接口、路由表和 DNS 设置
      • 可能包含多网络的多个端点:连接的每一个网络都有一个不同的端点
    • 网络
      • 一组端点之间能够相互直接交流
      • 实现可以是Linux网桥或重叠网(Overlay)
    • 容器
      • 容器能够作为任意一个或多个网络的一部分
      • 能够同时对接桥接网络和重叠网络
    • 在某个特定网络下的所有容器都能够自由地互相通信
    • 多个网络有助于分散容器之间的流量传输
    • 多个端点允许一个容器加入到多个网络中

    用户可创建的网络有2种类型:

    • bridge: 用于同一主机的不同容器进行连接
    • overlay: 跨主机网络连接

    网络实战

    1. 默认的bridge网络也是网桥网络,应用场景相同。
    1. 创建新的Bridge网络
    # 创建2个Bridge网络并创建容器连接网络
    $ docker network create my-net1
    $ docker run -itd --net=my-net1 --name service0 busybox
    $ docker run -itd --net=my-net1 --name service1 busybox
    $ docker network create my-net2
    $ docker run -itd --net=my-net2 --name service2 busybox
    # 查看网络, 获取容器ip地址等信息
    $ docker network ls
    $ docker network inspect my-net1
    $ docker network inspect my-net2
    
    1. 容器进行网络访问
    # 同网络容器Ping:成功
    $ docker exec service0 ping service1 # 172.18.0.2
    # 不同网络容器Ping:失败
    $ docker exec service1 ping service2
    $ docker exec service1 ping 172.19.0.2 # 无连接
    
    1. 为容器配置新的网络
    # 将容器连接到另一个网络
    $ docker network connect my-net2 service1
    $ docker network inspect my-net2
    # 此时service1在2个网络各自有一个ip
    $ docker exec service0 ping service1  # 172.18.0.3
    $ docker exec service1 ping service0  # 172.18.0.2
    $ docker exec service1 ping service2  # 172.19.0.2
    $ docker exec service2 ping service1  # 172.19.0.3
    

    此时,service1容器中的沙箱就具有2个Endpoints,对应不同的网络。访问时会根据网络配置决定应该走哪一个网络进行传输。

    1. 使用Host网络
    $ docker run -itd --net=host --name service-host busybox
    $ docker inspect service-host
    

    这时候容器就和本机运行的进程相同了,只要暴露了端口就能通过localhost:port进行访问。

    1. 使用None网络:即关闭所有网络连接

    网络插件

    • 允许第三方的容器网络方案连接到容器网络中
    • 降低了不同类型主机上容器通信的难度
    • 扩展由Docker提供的核心网络功能

    可用的网络插件来自于:

    • Weave
    • Project Calico
    • Nuage Networks
    • Cisco
    • VMware
    • Microsoft
    • Midokura

    容器安全

    在容器中的root权限对应宿主机的一个普通用户, 但当容器开启--privileged特权后, 就可以使用root对宿主机进行操作

    # 重要文件映射到容器中
    $ docker run -v /:/hostfs busybox cat /hostfs/etc/paths 
            # 部分文件不可见
    # 容器中修改主机重要文件
    $ docker run -it -v /:/hostfs busybox touch /hostfs/threat-on-the-way
            # 无法创建
    

    安全最佳实践

    • 主机:
      • 保持内核及时更新
      • 增强主机保护
      • 保持Docker及时更新
    • Docker守护进程:
      • 只允许受信用户控制Docker守护进程
      • 不使用不受信的镜像仓库
      • 必要时请为Docker守护进程应用TLS认证
      • 限制容器之间的网络通信
    • 镜像:
      • 在Dockerfile中为容器创建一个非root用户
      • 以非root用户运行容器进程
      • 只使用受信的基础镜像
      • 仅安装必要的包
      • 重新构建镜像时需要包含安全补丁
    • 容器运行时
      • 限制容器使用Linux内核能力
      • 不要使用privileged容器
      • 限制容器上的资源使用
      • 指定容器重启策略为on-failure
      • 使用AppArmor/SELinux保证额外的安全层
    • 其他
      • 为Docker挂载点创建单独的分区
      • 不要到产品环境中使用任何开发者工具(boot2docker, kinematic)
      • 建立本地仓库镜像
      • 使用供应商最支持的存储驱动程序
      • aufs是唯一的允许容器共享执行文件的存储驱动,但可能会导致严重的内核崩溃
      • 为Docker守护进程设置受限的控制资源权限(ulimit)
      • 由最小基础镜像开始(Busybox, Alpine)

    多主机部署和管理

    Docker本身只关注单主机(Docker Host),对镜像、容器进行管理。在多主机部署时,主要是利用其它组件、工具进行服务发现、服务注册、网络传输。只是由外部程序来接管多主机的协调工作,对于Docker来说是透明的。例如:

    • 将Docker容器放在Host网络/映射Docker容器的端口到Host上
    • 利用服务发现(Consul/Eureka)自动发布、获取IP:PORT
    • 利用Http通信

    相关文章

      网友评论

          本文标题:(三)Docker高级应用

          本文链接:https://www.haomeiwen.com/subject/xkpyvftx.html