docker

docker

背景

环境配置的难题

  • 软件开发最大的麻烦事之一,就是环境配置.用户计算机的环境都不相同
  • 如果某些老旧的模块与当前环境不兼容,那就麻烦了
  • 环境配置如此麻烦,换一台机器,就要重来一次,旷日费时
  • 能不能从根本上解决问题,软件可以带环境安装?

虚拟机

  • 虚拟机(virtual machine)就是带环境安装的一种解决方案

  • 它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统

  • 用户可以通过虚拟机还原软件的原始环境.但是,这个方案有几个缺点

  • 缺点

    • 资源占用多

      • 虚拟机会独占一部分系统资源,它运行的时候,其他程序就不能使用这些资源了
    • 冗余步骤多

      • 虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录等, 操作繁琐
    • 启动慢

      • 启动操作系统需要多久,启动虚拟机就需要多久. 可能要等几分钟,应用程序才能真正运行

Docker出现的背景

  • 每个集群都需要搭建很多虚拟机,再加上备份,本地磁盘很快被一大堆虚拟机镜像占满
  • 虚拟机共用,不同项目的应用不兼容,互相干扰影响
  • 环境不同导致代码不能正常运行,为了兼容特殊环境需要对代码进行定制化修改
  • 升级或迁移项目操作繁琐,更换环境需要重新测试,费时费力

docker简介

Docker 是什么

  • Docker 是一种应用容器引擎

容器

Container

  • 一个用户空间独立且限定了资源的对象,这样的对象称为容器

  • Namespace

    • Linux提供的实现环境隔离的方法,使一个进程和其子进程的运行空间与Linux的超级父进程相隔离
  • Cgroup

    • Linux系统提供的实现资源限制的方法,控制一个进程组群可使用的资源(如CPU、内存、磁盘IO等)
  • LXC

    • LXC(Linux Container)是Linux系统提供的容器化技术,它结合Namespace和Cgroup技术为用户提供了更易用的接口来实现容器化
    • LXC仅为一种轻量级的容器化技术,只能对部分资源进行限制,无法做到网络限制、磁盘空间占用限制等

Docker

  • DotCloud公司结合LXC和以下列出的技术实现了Docker容器引擎,相比于LXC,Docker具备更加全面的资源控制能力,是一种应用级别的容器引擎

  • 技术

    • Namespace、Cgroup

    • Chroot

      • 在容器里构造完整的Linux文件系统。Chroot可以增进系统的安全性,限制使用者能做的事
    • Veth

      • 在主机上虚拟出一张网卡与容器里的eth0网卡进行桥接,实现容器与主机、容器之间的网络通信
    • UnionFS

      • 联合文件系统,Docker利用该技术“Copy on Write”的特点实现容器的快速启动和极少的资源占用
    • Iptables/netfilter

      • 控制container网络访问策略
    • Tc

      • 该技术主要用来做流量隔离,限制带宽(Tc 命令用于Linux内核的流量控制)
    • Quota

      • 该技术用来限制磁盘读写空间的大小
    • Setrlimit

      • 该技术用来限制容器中打开的进程数,限制打开的文件个数等
  • 因为Docker依赖Linux内核的技术,3.8及以上内核版本才能运行Docker容器,官方推荐至少3.10

  • Docker本质是宿主机上的一个进程,Docker通过Namespace实现环境隔离,通过Cgroup实现资源限制

Docker与openstack

Docker与虚拟机

  • 架构图

  • Hypervisor

    • 传统的虚拟化技术在虚拟机VM和硬件之间加了一个软件层Hypervisor(虚拟机管理程序)

    • 运行方式

      • 直接运行在物理硬件之上。如基于内核的KVM虚拟机,需要CPU支持虚拟化技术
      • 运行在另一个操作系统。如VMWare和VitrualBox等虚拟机
    • GuestOS通过Hypervisor分享硬件,Guest OS发出的指令需要被Hypervisor捕获,然后翻译为物理硬件或宿主机操作系统能够识别的指令

    • 故像 VMWare和VirtualBox等虚拟机在性能方面远不如裸机,但基于硬件虚拟化的KVM约能发挥裸机80%的性能

  • Docker Engine

    • Docker引擎运行在操作系统上,是基于内核的LXC、Chroot等技术实现容器的环境隔离和资源控制
    • 在容器启动后,容器里的进程直接与内核交互,无需经过Docker引擎中转,因此几乎没有性能损耗,能发挥出裸机的全部性能
    • 由于Docker是基于Linux内核技术实现容器化,因此容器只能运行在Linux内核的操作系统上
    • 目前在Window上安装的Docker引擎其实是利用了Window自带的Hyper-V虚拟化工具创建了一个Linux系统,容器内的操作实际上是使用这个虚拟系统实现的
  • 比较

    • 占用资源

      • Docker的架构可以共用一个内核与共享应用程序库,所占内存极小,对系统的利用率非常高
    • 创建、启动时间

      • Docker属于秒级别,虚拟机通常需要几分钟
    • 性能损耗

      • Docker容器和内核交互,几乎没有性能损耗,虚拟机需要通过Hypervisor转发,性能损耗较大
    • 交付、部署

      • Docker在Dockerfile中记录了容器构建过程,可在集群中实现快速分发和快速部署
      • 虚拟机可以通过镜像实现环境交付的一致性,但镜像分发无法体系化
    • 高可用和可恢复性

      • Docker对业务的高可用支持是通过快速重新部署实现的
      • 拟化具备负载均衡、高可用、容错、迁移和数据保护等成熟保障机制
    • 隔离性

      • Docker属于进程之间的隔离,虚拟机可实现系统级别隔离
    • 安全性

      • Docker的租户root和宿主机root等同,虚拟机租户root权限和宿主机的root权限是分离的
    • 可管理性

      • Docker的集中化管理工具还不算成熟。各种虚拟化技术都有成熟的管理工具

使用场景

  • 使用系统镜像创建容器,当作虚拟机来使用,可以启动大量的操作系统容器,方便进行各种测试
  • 作为云主机使用结合Kubernetes这样的容器管理系统,可以在大量服务器上动态分配和管理容器
  • 应用服务打包,能很方便的升级服务和控制版本
  • 容器云服务CaaS(容器即服务)
  • CI/CD自动化,线上线下环境统一,线上服务版本与Git/SVN发布分支实现统一
  • 解决微服务架构的实施难题
  • 构建临时的运行环境,执行完任务后关闭容器即可,方便快捷
  • 多租户环境为不同的租户提供独占的容器,实现简单而且成本较低

Docker安装

版本说明

  • 开源的商业产品,从1.13.x开始版本分为企业版EE(Enterprise Edition)和社区版CE(Community Edition),版本号改为按照时间线,比如17.03就是2017年3月
  • 社区版分为stable和edge两种发布方式,stable(稳定版)是季度发布,比如17.03, 17.06, 17.09,edge是月份发布,比如17.03, 17.04

服务架构

  • C/S结构。Docker客户端与Docker服务器进行交互,Docker服务端负责构建,运行和分发Docker镜像
  • Docker客户端和服务端可以运行在一台机器上,也可以通过RESTful、stock或网络接口与远程Docker服务端进行通信

安装

  • docker源

    • curl https://gitee.com/leedon21/k8s/raw/master/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo

    • yum list docker-ce --showduplicates

      • 查看版本
    • yum install -y docker-ce

      • 安装最新版
    • yum install -y docker-ce-19.03.15

      • 安装指定版本,常用19.03
  • rpm包

    • 官方下载地址

      • https://download.docker.com/linux/centos/7/x86_64/stable/Packages/
    • yum -y install docker-ce-19.03.9-3.el7.x86_64.rpm

启动

  • systemctl start docker;systemctl enable docker

镜像加速器

  • 方法1

    • vim /etc/docker/daemon.json

{
“registry-mirrors”: [“https://jvfknb9j.mirror.aliyuncs.com”,“https://pf5f57i3.mirror.aliyuncs.com”]
}
- systemctl restart docker

  • 方法2

    • vim /usr/lib/systemd/system/docker.service

ExecStart=/usr/bin/dockerd --registry-mirror=https://pf5f57i3.mirror.aliyuncs.com
- systemctl daemon-reload;systemctl restart docker

  • 可以登录阿里云控制台, 搜索容器镜像服务, 来获取自己的镜像加速地址

查看

  • docker version
  • docker info

远程连接

  • vim /usr/lib/systemd/system/docker.service

ExecStart=/usr/bin/dockerd -H fd:// -H tcp://1.1.1.11:2375 --containerd=/run/containerd/containerd.sock

  • systemctl daemon-reload;systemctl restart docker

  • docker -H 1.1.1.11 version

    • 客户端连接
  • 默认只能使用本机客户端连接,不建议开启远程连接,没有任何加密认证过程

  • 请务必要开启需要安全认证的tcp端口https://blog.csdn.net/qq_21187515/article/details/90262324

Docker基本概念

Docker镜像(image)

  • Docker把应用程序及其依赖,打包在image里面.只有通过镜像,才能生成Docker容器
  • Image可以看作是容器的模板. 一个image可以生成多个同时运行的容器实例
  • 实际开发中, 一个image往往通过继承另一个image, 加上一些个性化设置而生成,比如在centos镜像中加入apache形成新的apache镜像
  • Image是通用的, 一台机器的image拷贝到另一台机器, 照样可以使用
  • 为了方便共享,image制作完成后,可以上传到网上的仓库.Docker的官方仓库Docker Hub(hub.docker.com)是最重要、最常用的image仓库

Docker容器(container)

  • Docker利用容器来运行应用,容器是从镜像创建的运行实例,它可以被启动、开始、停止、 删除。每个容器都是相互隔离的、保证安全的平台
  • 可以把容器看做是一个简易版的Linux环境(包括root用户权限,进程空间,用户空间和网络空间等)和运行在其中的应用程序
  • 镜像是只读的,容器在启动的时候创建一层可写层作为最上层
  • docker容器启动后,会生成一个容器ID号,一旦容器内前台运行的程序结束后,容器就会关闭
  • 如果不希望容器自动结束,我们需要在容器启动时给它一个前台运行的、不会自动结束的进程

Docker仓库

  • 仓库是集中存放镜像的场所,分为公开仓库(Public)和私有仓库(Private) 。有时候会把仓库和仓库注册服务器(Registry)混为一谈,并不严格区分
  • 最大的公开仓库是Docker Hub(hub.docker.com),存放了数量庞大的镜像供用户下载。 国内的公开仓库包括阿里云, 腾讯云, 网易蜂巢, daocloud等
  • 用户可以在本地网络内创建私有仓库,可以将自己的镜像使用push命令上传到公有或者私有仓库, 使用时只需从仓库上pull下来
  • Docker仓库的概念跟Git类似,注册服务器可以理解为GitHub这样的托管服务
  • 仓库注册服务器( Registry)存放仓库,仓库存放镜像,镜像有不同的标签(tag)

Docker引擎

  • 创建和管理容器的工具,通过读取镜像来生成容器,并负责从仓库拉取镜像或提交镜像到仓库中

Dockerfile

  • Dockerfile构建出Docker镜像,通过Docker镜像运行Docker容器

关系图

docker基础系统镜像

busybox

  • 一个极简版的Linux系统,集成了100多种常用Linux命令,大小不到2MB,被称为“Linux系统的瑞士军刀”,适用于简单测试场景

alpine

  • 一个面向安全的轻型Linux发行版系统,比BusyBox功能更完善,大小不到5MB,是官网推荐的基础镜像,由于其包含了足够的基础功能和体积较小,在生产环境中最常用

debian/ubuntu

  • Debian系列操作系统,功能完善,大小约170MB,适合研发环境

centos/fedora

  • 都是基于Redhat的Linux发行版,企业级服务器常用操作系统,稳定性高,大小约200MB,适合生产环境使用

docker基本操作

Docker镜像

  • 镜像索引

    • NAME:TAG或IMAGE ID

      • 默认TAG为latest,可省略
    • NAME组成

      • 注册服务器/用户/仓库

      • 注册服务器

        • 官方注册服务器为docker.io,默认省略
      • 用户

        • 官方仓库没有用户,非官方仓库有用户
      • 仓库

        • 仓库名称,仓库存放在注册服务器中
  • 查看镜像

    • 查看本机的镜像

      • docker image ls
      • docker images
    • 镜像详细信息

      • docker inspect 镜像
  • 查找镜像

    • docker search centos

      • 在官方注册服务器查找centos仓库,OFFICIAL为[OK]表示是官方仓库
    • https://hub.docker.com

      • 使用镜像前先看描述信息Description,找使用方法
  • 下载镜像

    • docker pull NAME[:TAG]

      • 从官方注册服务器拉取镜像
  • 上传镜像

    • docker push NAME[:TAG]

      • 将镜像推送到NAME中的注册服务器的仓库中
  • 删除镜像

    • docker image rm 镜像
    • docker rmi 镜像

Docker容器

  • 容器索引

    • CONTAINER ID或NAMES
  • 查看容器

    • docker ps [-a]

      • 查看正在运行的[所有的]容器

      • 状态

        • running、exited、created、paused
    • docker ps -q [-a]

      • 查看正在运行的[所有的]容器ID
    • docker top 容器

      • 查看容器的进程信息
    • docker inspect 容器

      • 以json格式显示出容器的具体信息
    • docker logs 容器

      • 查看容器日志
  • 创建并运行容器

    • 语法

      • docker run [OPTIONS] IMAGE [COMMAND]
      • [COMMAND]的输出保存在容器日志中
      • 若本地没有该镜像,run会自动先下载
    • 选项

      • -i

        • 打开STDIN,用于控制台交互
      • -t

        • 分配tty设备,可以支持终端登录
      • -d

        • 在后台运行容器,返回容器ID
      • -h

        • 指定容器的主机名,如果不指定,会随机生成一个
      • –rm

        • 容器停止后删除掉,默认不会删除
        • docker run --rm -it alpine sh #用于测试用运行一次性容器
      • –name

        • 指定容器的名称
      • –network

        • 指定容器的网络连接方式, 默认为bridge
      • –ip

        • 指定容器的IP地址,只有自定义网络类型能指定IP
      • -p <宿主端口>:<容器端口>

        • 将容器指定端口映射到宿主机的指定端口. 可以用多个-p选项指定多个端口映射
      • -p <容器端口>

        • 将容器的指定端口映射到宿主机的随机端口
      • -P

        • 将容器的所有端口映射到宿主机的随机端口
      • -v <宿主目录>:<容器目录>

        • 将容器的指定目录映射到宿主机的指定目录(容器数据持久化)
      • -v <容器目录>

        • 将容器的指定目录映射到宿主机的匿名目录
      • –restart=always

        • 重启docker后自动启动容器,否则重启docker所有容器会停止
      • –privileged

        • 需要修改某些特定的参数需要加上此选项, 正常运行一个容器不建议开放
    • 示例

      • docker run -itd --name centos centos:7
      • docker run -d --name nginx nginx
      • docker run -d --name nginx1 -p 8080:80 -v /www:/usr/share/nginx/html nginx
  • 连接容器

    • 对于正在运行的容器,我们可以在宿主机上连接容器

    • docker exec -it centos {/bin/bash,bash,sh}

      • 可用exit命令退出,不影响容器
    • docker attach centos

      • 通过attach连接容器,使用exit退出后容器会关闭,当多个窗口同时使用该命令进入该容器时,所有的窗口都会同步显示.如果有一个窗口阻塞了,那么其他窗口也无法再进行操作.若不想退出后停止容器,可通过快捷键ctrl+pq退出
  • 容器运行

    • docker create [选项] 镜像

      • 创建一个容器但不运行,选项基本和run命令相同
    • docker stop|start|restart|kill 容器

      • 启停容器
    • docker pause|unpause 容器

      • 暂停|恢复容器
  • 删除容器

    • docker rm 容器

      • 删除指定的未运行容器
    • docker rm -f 容器

      • 强制删除指定容器
    • docker rm docker ps -a -q

      • 删除所有未运行的容器
  • 复制文件

    • docker cp container:src_file dst_path

      • 将容器中的文件复制到宿主机上
    • docker cp src_file container:dst_path

      • 将宿主机上的文件复制到容器中
  • 容器提交

    • 对容器所作的修改保存在容器中, 一旦容器被删除了, 修改也没有了。为了永久保存, 可以将容器打包成镜像
    • docker commit -m “描述信息” 容器 镜像名[:tag]

docker容器的运行过程

  • 创建和运行

    • create

      • None—create—created
    • start

      • created—start—running
    • run

      • None—created—start—running
  • 运行和停止

    • start

      • stopped—start—running
    • stop

      • running—die—stop—exited
    • kill

      • running—die—kill—exited
  • 重启

    • restart

      • running—die—start—running
  • 暂停

    • pause

      • running—pause—paused
    • unpause

      • paused—unpause—running

docker导入导出

基于镜像

  • 导出

    • docker save centos:latest > /bak/docker-centos_latest.tar
  • 导入

    • docker load < /bak/docker-centos_latest.tar

基于容器

  • 导出

    • docker export centos7 > /bak/docker-centos7.tar
  • 导入

    • docker import /bak/centos7.tar centos7
    • 导入了镜像,对镜像有一定的改变,run后面要接命令才能运行

不建议直接导入导出,建议使用仓库

docker仓库

普通仓库

  • 安装

    • docker run -d -p 5000:5000 -v /docker/images/:/var/lib/registry --name registry --restart=always registry
    • 通过运行registry注册服务器镜像,仓库存放在注册服务器中(registry容器的/var/lib/registry目录下)
    • 为了便于使用,将容器的服务端口(5000)映射到宿主机,将仓库目录映射到宿主机目录(自定义)
  • 配置

    • Registry为了安全性考虑,默认需要https证书支持,可以通过修改启动文件添加允许的’不安全的’注册服务器
    • vim /usr/lib/systemd/system/docker.service

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry=1.1.1.11:5000
- systemctl daemon-reload;systemctl restart docker

  • 使用

    • 重命名镜像,使之与registry匹配

      • docker tag centos:7 1.1.1.11:5000/centos7:v1
      • 普通仓库不需要用户
    • 上传到私有仓库

      • docker push 1.1.1.11:5000/centos7:v1
    • 查看私有仓库

      • curl 1.1.1.11:5000/v2/_catalog

        • 查看所有仓库
      • curl 1.1.1…11:5000/v2/centos7/tags/list

        • 查看仓库的所有标签(镜像)
    • 从私有仓库中拉取镜像

      • docker pull 1.1.1.11:5000/centos7:v1
  • WebUI

    • 目的

      • docker命令行工具对registry操作不能直观地查看资源,可以借助UI工具管理镜像
    • 工具

      • docker-registry-web

        • https://hub.docker.com/r/hyper/docker-registry-web/
    • 运行

      • docker run -d -p 8080:8080 --name registry-web --link registry -e REGISTRY_URL=http://1.1.1.11:5000/v2 -e REGISTRY_NAME=1.1.1.11:5000 hyper/docker-registry-web

      • –link=[]

        • 指定容器间的关联,使用其他容器的IP、env等信息
      • -e

        • 指定环境变量
    • 访问web界面

      • 1.1.1.11:8080

企业级仓库-Harbor

  • Harbor简介

    • 官网

      • https://goharbor.io/
    • 由VMware公司中国团队为企业用户设计的Registry server开源项目

    • Harbor是构建企业级私有docker镜像的仓库的开源解决方案,它是Docker Registry的更高级封装

    • 支持安装在多个 Registry 节点的镜像资源复制,镜像全部保存在私有 Registry 中

    • 提供了高级的安全特性,诸如用户管理,访问控制和活动审计等

  • Harbor架构

    • proxy

      • 前端代理,主要是分发前端页面ui访问和镜像上传和下载流量
    • ui

      • web管理页面,包括一个前端页面和后端API,底层使用mysql数据库
    • registry

      • 镜像仓库,负责存储镜像文件,当镜像上传完毕后通过hook通知ui创建repository,registry的token认证也是通过ui组件完成
    • adminserver

      • 系统的配置管理中心附带检查存储用量,ui和jobserver启动时候需要加载adminserver的配置
    • jobsevice

      • 负责镜像复制工作的,他和registry通信,从一个registry pull镜像然后push到另一个registry,并记录job_log
    • log

      • 日志汇总组件,通过docker的log-driver把日志汇总到一起
  • 准备工作

    • 配置好epel源,删除前面自己配置的registry
  • 配置过程

    • 安装docker-compose

      • docker提供的一个命令行工具,用来定义和运行由多个容器组成的应用
      • 可以通过YAML文件声明式的定义应用程序的各个服务,并由单个命令完成应用的创建和启动
      • yum install -y docker-compose
    • 下载harbor

      • wget https://storage.googleapis.com/harbor-releases/release-1.9.0/harbor-offline-installer-v1.9.1.tgz
      • tar xf harbor-offline-installer-v1.9.1.tgz -C /usr/local
    • 配置并安装

      • cd /usr/local/harbor/

      • [root@docker harbor]# vim harbor.yml
        hostname: 1.1.1.11

        • 密码在配置文件中
      • [root@docker harbor]# ./prepare

      • [root@docker harbor]# ./install.sh

    • 添加http注册服务器

    • 进入web界面

      • 1.1.1.11

        • 用户名: admin
        • 密码: 见配置文件Harbor12345
    • 测试上传镜像

      • 登录harbor的Web界面,创建一个项目sdc

      • 打标签

        • docker tag nginx 1.1.1.11/sdc/nginx:v1
      • 登录镜像库

        • docker login 1.1.1.11

          • Username:admin
          • password:Harbor12345
      • 上传镜像

        • docker push 1.1.1.11/sdc/nginx:v1
  • harbor启停

    • 启动

      • [root@docker ~]# cd /usr/local/harbor/
        [root@docker harbor]# docker-compose up -d
    • 关闭

      • [root@docker ~]# cd /usr/local/harbor/
        [root@docker harbor]# docker-compose stop
  • 配置HTTPS

    • 服务端

      • 申请证书
      • 将证书和私钥文件放到相应的位置, 并修改配置文件
        https:
        port: 443
        certificate: /your/certificate/path
        private_key: /your/private/key/path
      • 重新在harbor目录中执行./install.sh
    • 客户端

      • 创建证书目录

        • mkdir -p /etc/docker/certs.d
      • 将证书放到证书目录中

  • 权限管理

    • 官方文档

      • https://goharbor.io/docs/1.10/administration/managing-users/
    • 系统级别

      • 管理员

        • 能操作所有项目
      • 普通用户

        • 只能看到公开项目, 可成为项目级别的角色
    • 项目级别

      • 项目管理员

        • 当用户创建一个项目后,此用户为该项目的项目管理员, 项目管理员拥有该项目的一切权限
      • 项目维护者

        • 拥有比“开发人员”更高的权限,包括查找镜像、查看复制任务、删除镜像的能力
      • 开发者

        • 对指定项目有读写权限
      • 访客

        • 对指定项目具有只读权限。他们可以拉出和重新标记镜像,但不能推

公有仓库-阿里云

  • 登录阿里云控制台

  • 搜索 容器镜像服务 并进入

  • 创建个人实例

    • 实例列表 -> 个人实例 -> + 创建个人实例 -> 进入个人实例
  • 创建命名空间

    • 命名空间->创建命名空间->自定义命名空间->确定
    • 全局唯一,类似于harbor中的项目,用来对应一个公司、组织或个人用户
  • 创建镜像仓库

    • 镜像仓库->创建镜像仓库->选择命名空间->自定义仓库->选择代码源(本地仓库)->创建镜像仓库

docker数据持久

介绍

  • 容器的生命周期是有限的,一旦一个容器被删除,那么容器中的数据随之而删除了
  • 如果想要永久保存容器中的数据,则需要将数据保存在宿主机的目录中,这就是Docker的数据持久化
  • Docker的数据持久化主要有两种方式:bind mount、volume

bind mount

  • 用法

    • -v 宿主目录:容器目录[:挂载属性]

      • 将宿主机目录挂载到容器中
    • docker run -d -v /www:/usr/share/nginx/html nginx

  • 注意

    • host机器的目录路径必须为绝对路径(准确的说需要以/或~/开始的路径)
    • 如果目录(不论哪边)不存在,docker会自动创建该目录
    • host上的目录的内容会覆盖掉容器目录的内容
  • 缺陷

    • 移植性差,如果两个系统的目录结构不同(如windows与linux)则不可移植

volume

  • 用法

    • -v [VOLUME_NAME:]容器目录[:挂载属性]

      • 将宿主机volume挂载到容器中
    • docker run -d -v nginx:/usr/share/nginx/html nginx

    • docker run -d --mount ‘src=nginx,dst=/usr/share/nginx/html’ nginx

  • 查看挂载信息

    • docker inspect nginx -f {{.Mounts}}
  • volume

    • 由docker管理的数据卷,在/var/lib/docker/volumes目录下

    • 管理命令

      • docker volume COMMAND

      • COMMANDS

        • create

          • Create a volume
        • inspect

          • Display detailed information on one or more volumes
        • ls

          • List volumes
        • prune

          • Remove all unused local volumes
        • rm

          • Remove one or more volumes
  • 注意

    • volume可以先创建再挂载,若未创建挂载时会自动创建
    • 若运行容器时不指定volume, docker将为容器创建一个匿名卷挂载
    • 如果volume是空的而container中的目录有内容,那么docker会将container目录中的内容拷贝到volume中
    • 如果volume中已经有内容,则会将container中的目录覆盖
  • 优势

    • 可移植性强,使用名称这种形式在任何操作系统都适用

docker网络

查看网络模式

  • docker network ls
  • Docker安装后,会创建三种网络模式:bridge,host,none

网络模式

  • bridge

    • bridge模式是容器的默认网络模式,相当于vmware中的NAT

    • Docker安装时在宿主机创建虚拟网桥docker0,每开启一个容器,按照顺序从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关,容器连接到虚拟网桥docker0

    • 虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中

    • 在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在容器中,命名为eth0(容器的网卡),另一端放在主机中,命名为vethxxx,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看

    • 结构图

  • host

    • 运行容器时使用–network=host指定,容器和宿主机使用同一个网络空间
    • 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口
    • 例如,在容器中运行一个Web服务,监听8080端口,则主机的8080端口就会自动映射到容器中
  • none

    • 运行容器时使用–network=none指定,容器将没有网络空间
  • container

    • 运行容器时使用–net container:NAMES指定,新容器和指定的容器使用同一个网络空间
    • docker run -itd --name apache3 --net container:apache1 centos
    • container网络不能和 -p|-h 一起使用
  • 自定义

    • 创建网桥

      • docker network create --subnet 192.168.20.0/24 net-20
    • 指定容器IP

      • docker run -itd --network net-20 --ip 192.168.20.10 centos
    • 只有自定义网络支持指定IP,不指定时仍按顺序分配

容器跨主机通信

  • 直接路由

    • 通过在Docker主机上添加静态路由实现跨宿主机通信
  • pipework

    • 一个网络配置脚本,通过ip、brctl、ovs-vsctl等命令来为Docker容器配置自定义的网桥、网卡、路由等
    • 使用新建的br0网桥代替缺省的docker0网桥,br0和主机eth0之间是veth pair
    • https://github.com/jpetazzo/pipework/blob/master/pipework
  • flannel

    • 每个主机上安装并运行etcd和flannel,在etcd中规划配置所有主机的docker0子网范围
    • 每个主机上的flanneld根据etcd中的配置,为本主机的docker0分配子网,保证所有主机上的docker0网段不重复,并将结果存入etcd库中,这样etcd库中就保存了所有主机上的docker子网信息和本主机IP的对应关系
    • 当需要与其他主机上的容器进行通信时,查找etcd数据库,找到目的容器的子网所对应的outip(目的宿主机的IP)
    • 将原始数据包封装在VXLAN或UDP数据包中,IP层以outip为目的IP进行封装
    • 由于目的IP是宿主机IP,因此路由是可达的
    • VXLAN或UDP数据包到达目的宿主机解封装,解出原始数据包,最终到达目的容器

dockerfile

介绍

  • 官方文档

    • https://docs.docker.com/engine/reference/builder/#from
  • Dockerfile是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像

  • 从FROM命令开始,紧接着跟随者各种方法,命令和参数,其产出为一个新的可以用于创建容器的镜像

FROM

  • 语法

    • FROM 
  • 所有Dockerfile都必须以FROM命令开始。 FROM命令会指定镜像基于哪个基础镜像创建

MAINTAINER

  • 语法

    • MAINTAINER 
  • 设置该镜像的作者

RUN

  • 语法

    • RUN command arg1 arg2 …

      • 由shell启动,类似于直接在命令行输入命令. Linux默认为 /bin/sh -c
    • RUN [“命令”,“arg1”,“arg2”]

      • 类似于函数调用的形式
  • 每一条RUN都会新建立一层(开启一个容器),在其上执行这些命令,执行结束后,commit这一层的修改,构成新的镜像

  • 多条命令用命令连接符连接以减少RUN的使用,尽量避免产生不必要的层

  • 每一层只保留真正需要的东西,类似于安装包、暂存目录、yum缓存等不必要的东西都建议清理掉

COPY

  • 语法

    • COPY <源路径>… <目标路径>
    • COPY [“<源路径1>”,… “<目标路径>”]
  • 将构建上下文目录中<源路径>的文件或目录复制到新的一层的镜像内的<目标路径>位置

  • <源路径>可以是多个,支持通配符

  • <目标路径>可以是绝对路径或相对于工作目录(WORKDIR指令指定)的相对路径,不需提前创建

  • 源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等

ADD

  • 语法

    • ADD   
    • ADD指令和COPY的格式和性质基本一致
    • 相对于构建上下文目录的一个文件或目录,也可以是一个远程的url

    • 远程url

      • 下载后的文件权限自动设置为600,若需要调整权限或解压需要额外的一层RUN来处理
    • tar压缩文件

      • 压缩格式为gzip,bzip2,xz 的情况下,会自动解压缩这个压缩文件到<目标路径>去
    • 目标容器中的绝对路径
  • 注意

    • ADD指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢
    • 选择建议:所有的文件复制均使用COPY指令,仅在需要自动解压缩的场合使用ADD

CMD

  • 语法

    • CMD command arg1 arg2

      • sh -c执行shell内部命令
    • CMD [“executable”,“arg1”,“arg2”]

      • 执行可执行文件,优先
    • CMD [“arg1”,“arg2”]

      • 参数给ENTRYPOINT调用
  • 用于指定默认的容器主进程的启动命令,运行容器时执行

  • CMD命令可被docker run中的命令覆盖,如果有多个CMD命令,则最后一条生效

  • 对于容器而言,其启动程序就是容器应用进程. 容器仅为前台主进程而存在,前台主进程退出,容器就失去了存在的意义,从而退出

  • 前台启动

    • apache

      • httpd -D FOREGROUND
    • nginx

      • nginx -g “daemon off;”

ENTRYPOINT

  • 语法

    • ENTRYPOINT application “arg1”, “arg2”, …
    • ENTRYPOINT [“application”,“arg1”, “arg2”]
  • 用于指定默认的容器主进程的启动命令,运行容器时执行

  • ENTRYPOINT命令也可以被覆盖,但docker run需要指定–entrypoint,多个ENTRYPOINT只有最后一个生效

  • 结合CMD和ENTRYPOINT,可以从CMD中移除“application”而仅仅保留参数,参数将传递给ENTRYPOINT,也可以从命令行中捕获执行时需要的参数

EXPOSE

  • 语法

    • EXPOSE port1 [port2]
  • 声明运行时容器提供服务端口,这只是一个声明,不影响容器实际开启的服务端口

  • docker run -P 时,会自动随机映射到EXPOSE的端口

ENV

  • 语法

    • ENV
    • ENV = [= …]
  • 设置环境变量。无论是后面的其它指令还是运行时的应用,都可以直接使用这里定义的环境变量

VOLUME

  • 语法

    • VOLUME
    • VOLUME [“dir1”,“dir2”]
  • 挂载匿名卷,可被运行时设置的挂载所覆盖

  • 可通过 docker inspect -f {{.Mount}} container查询所挂载的目录

WORKDIR

  • 语法

    • WORKDIR
  • 指定工作目录(当前目录),如该目录不存在,WORKDIR会自动创建目录

USER

  • 语法

    • USER user/uid
  • 改变之后层的执行RUN,CMD以及ENTRYPOINT 这类命令的身份,用户必须提前创建

HEALTHCHECK

  • 语法

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

    • interval=<间隔>

      • 两次健康检查的间隔,默认为 30 秒
    • timeout=<时长>

      • 健康检查命令运行超时时间,超过则本次被视为失败,默认 30 秒
    • retries=<次数>

      • 当连续失败指定次数后,则将容器状态视为 unhealthy ,默认3次
  • 告诉Docker如何判断容器的状态是否正常,1.12 引入,引入前只能通过容器内主进程是否退出来判断

  • 如果程序进入死锁或死循环状态,应用进程并不退出但是该容器已经无法提供服务了,1.12前就无法检测到

  • 启动容器时的初始状态为starting ,检查成功后变为 healthy ,如果连续一定次数失败,则会变为 unhealthy

  • HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效

练习

  • 1

    • 基于centos:7镜像构建一个镜像centos7:yum,修改官方yum源为国内镜像源
    • [root@docker2 centos-yum]# vim Dockerfile
      FROM centos:7
      RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo ;
      curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
      [root@docker2 centos-yum]# docker build -t centos7:yum ./
  • 2

    • 基于centos7:yum镜像构建一个已安装好apache服务的镜像,要求用此镜像启动容器时,若接-P选项其80端口会映射到宿主机的随机端口,且能正常提供http服务(要有网页), 网页的内容是 Apache is working. Ipaddress is XXX.
    • [root@docker2 apache]# vim Dockerfile
      FROM centos7:yum
      RUN yum install -y httpd && yum clean all
      EXPOSE 80
      COPY [“entrypoint.sh”,“/usr/local/bin/”]
      CMD [“httpd”,“-DFOREGROUND”]
      ENTRYPOINT [“entrypoint.sh”]
      [root@docker2 apache]# cat entrypoint.sh
      #!/bin/bash
      echo “Apache is working. Ipaddress is hostname -I.” > /var/www/html/index.html
      exec “$@”
      [root@docker2 apache]# chmod +x entrypoint.sh
      [root@docker2 apache]# docker build -t apache:v1 ./
  • 3

    • 基于centos7:yum镜像构建mariadb数据库镜像,要求启动镜像可提供数据库服务,且要求数据能够持久保存在磁盘中
    • [root@docker2 mariadb]# vim Dockerfile
      FROM centos7:yum
      RUN yum -y install mariadb-server && yum clean all
      COPY [“entrypoint.sh”,“/usr/local/bin”]
      VOLUME [“/var/lib/mysql/”]
      CMD [“mysqld_safe”]
      ENTRYPOINT [“entrypoint.sh”]
      [root@docker2 mariadb]# vim entrypoint.sh
      #!/bin/bash
      [ -d “/var/lib/mysql/mysql” ] || mysql_install_db --basedir=/usr --datadir=/var/lib/mysql --user=mysql
      mysqld_safe &
      while :;do
      mysql -h localhost -e “grant all on . to ‘ M Y S Q L U S E R ′ @ ′ MYSQL_USER'@' MYSQLUSER@MYSQL_HOST’ identified by 'KaTeX parse error: Expected 'EOF', got '&' at position 24: …ER_PASSWORD';" &̲& pkill mysql …@”
      [root@docker2 mariadb]# chmod +x entrypoint.sh
      [root@docker2 mariadb]# docker build -t mariadb:v1 ./
      [root@docker2 mariadb]# docker run -d --name mariadb -e MYSQL_USER=user1 -e MYSQL_HOST=172.17.0.1 -e MYSQL_USER_PASSWORD=123 mariadb:v1
      [root@docker2 mariadb]# mysql -u user1 -p123 -h 172.17.0.4
  • 4

    • 基于Alpine镜像构建一个nginx镜像 https://zhuanlan.zhihu.com/p/116084967
    • [root@docker2 nginx]# vim Dockerfile
      FROM alpine
      ADD [“nginx-1.14.0.tar.gz”,“/tmp/”]
      RUN echo “https://mirrors.aliyun.com/alpine/v3.6/main/” > /etc/apk/repositories &&
      echo “https://mirrors.aliyun.com/alpine/v3.6/community/” >> /etc/apk/repositories &&
      apk add gcc g++ pcre-dev make &&
      addgroup -S nginx &&
      adduser -DSH -s /sbin/nologin -G nginx nginx &&
      cd /tmp/nginx-1.14.0 &&
      ./configure --user=nginx --without-http_gzip_module --sbin-path=/usr/sbin/ &&
      make && make install &&
      rm -rf /tmp/nginx-1.14.0
      EXPOSE 80
      CMD [“nginx”,“-g”,“daemon off;”]
      [root@docker2 nginx]# docker build -t nginx:v1 ./

多阶段构建

  • 介绍

    • Docker 17.05版本以后,支持了多阶段构建,即允许一个Dockerfile 中出现多个 FROM 指令
    • Docker的镜像内容中,并非只是一个文件,而是有依赖关系的层级结构,后面以前一层为基础, Dockerfile 中的大多数指令都会生成一个层
    • 多个 FROM 指令时,最后生成的镜像,以最后一条 FROM 为准,之前的 FROM 会被抛弃
    • 在后面的 FROM 指令中, 能够将前面阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。最大的使用场景是将编译环境和运行环境分离
  • 多阶段构建redis

    • FROM centos7:yum
      RUN yum install -y gcc gcc-c++ make
      ADD [“redis-4.0.10.tar.gz”,“/”]
      RUN cd /redis-4.0.10 && make && sed -i ‘/^bind/s/127.0.0.1/0.0.0.0/’ redis.conf

FROM ubuntu
COPY --from=0 [“/redis-4.0.10/src/redis-server”,“/redis-4.0.10/redis.conf”,“/redis/”]
CMD [“/redis/redis-server”,“/redis/redis.conf”]
- redis服务,只需要启动脚本redis-server和配置文件redis.conf即可运行

  • COPY指令的–from参数

    • 表示从前面指定的阶段中拷贝文件到当前阶段中,可以使用数字(0表示第一个FROM)、名称来指定,还可以直接从一个已经存在的镜像中拷贝

    • 名称

      • FROM centos:7 as builder

FROM ubuntu
COPY --from=builder …

- 从镜像

	- FROM ubuntu

COPY --from=quay.io/coreos/etcd:v3.3.9 [“/usr/local/bin/etcd”,“/usr/local/bin/”]
- 直接将官方编译好的程序文件拿过来使用

    • 使用多阶段构建一个redis6-alpine镜像的Dockerfile
    • FROM alpine
      ENV pkg=redis-6.2.7.tar.gz
      ENV dl_url=https://download.redis.io/releases/redis-6.2.7.tar.gz
      ENV src_dir=redis-6.2.7

RUN sed -i ‘s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/’ /etc/apk/repositories;
apk update;
apk add -t .build-deps gcc libc-dev make linux-headers wget;
wget $dl_url;
tar xf $pkg;
cd $src_dir;
sed -i ‘/^bind/s/127.0.0.1/0.0.0.0/’ redis.conf;
make

FROM alpine
ENV src_dir=redis-6.2.7
COPY --from=0 [“/ s r c d i r / s r c / r e d i s − s e r v e r " , " / src_dir/src/redis-server","/ srcdir/src/redisserver","/src_dir/src/redis-cli”,“/$src_dir/redis.conf”,“/redis/”]
CMD [“/redis/redis-server”,“/redis/redis.conf”]

docker监控

Docker容器的监控

  • 容器的基本信息

    • 包括容器的数量、ID、名称、镜像、启动命令、端口等信息
  • 容器的运行状态

    • 统计各状态的容器的数量,包括运行中、暂停、停止及异常退出
  • 容器的用量信息

    • 统计容器的CPU使用率、内存使用量、块设备I/O使用量、网络使用情况等资源的使用情况

容器的监控方案

  • 单台主机

    • docker stats [–no-stream]

      • 显示所有容器的资源使用情况
      • 简单方便,但只显示当前值看不到变化趋势
    • CAdvisor

      • docker run -d -v /:/rootfs:ro -v /var/run:/var/run:rw -v /sys/:/sys/:ro -v /var/lib/docker/:/var/lib/docker/:ro -p 8080:8080 --name cadvisor google/cadvisor

      • 览器访问8080端口,CPU的使用率、内存使用率、网络吞吐量以及磁盘空间利用率

      • 优点

        • 部署、维护简单:通过容器形式部署,“开箱即用”,无需配置即可使用
        • 监控容器和宿主机
        • 通过图展示,比较美观
        • 支持多种基于事件序列的数据库,写入和读取快
      • 缺点

        • 只能看到2-3分钟的监控数据
        • 消耗的内存较高,通常几百M
        • 页面加载较慢
  • 跨主机容器监控

docker原理

Namespace

  • 在Linux Kernel3.8以后,Linux支持6种namespace

  • UTS

    • 隔离主机名与域名
  • IPC

    • 隔离进程间通信:信号量,消息列队及共享内存
  • PID

    • 隔离进程编号
  • NS(MOUNT)

    • 隔离挂载点(文件系统)
  • NET

    • 隔离网络设备、网络栈、端口等
  • USER

    • 隔离用户和用户组
  • 可以使用 unshare 命令感受一下namespace

    • [root@docker2 ~]# hostname
      docker2
      [root@docker2 ~]# unshare -u bash
      [root@docker2 ~]# hostname aaa
      [root@docker2 ~]# hostname
      aaa
      [root@docker2 ~]# exit
      exit
      [root@docker2 ~]# hostname
      docker2

Cgroups

  • 对于应用进程来说,namespace实质上只是限制了‘视线’,它使用的资源(cpu、内存等)还是同其他所有进程平等竞争

  • Control Group,最主要作用是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。此外还能够对进程进行优先级设置、审计,以及将进程挂起和恢复等操作

  • 在Linux中,Cgroups给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在/sys/fs/cgroup路径下

  • 限制cpu

    • [root@docker ~]# cd /sys/fs/cgroup/

      • 该目录下都是当前可以被cgroup进行限制的资源种类
    • [root@docker cgroup]# cd cpu

      • 该目录下文件为对cpu限制的具体方法
    • [root@docker cpu]# mkdir cg

      • 创建一个目录,这个目录就称为一个控制组
    • [root@docker cpu]# cd cg

      • 在控制组内会自动生成对该类资源的限制方法
    • [root@docker cg]# echo 20000 > cpu.cfs_quota_us

      • 限制cpu使用率上限为20%(100ms内使用20ms)
    • [root@docker cg]# echo 12411 > tasks

      • 设置此控制组所控制的进程(写入pid)
    • docker run -d --name nginx2 --cpu-quota 20000 nginx:v3

      • 运行容器时–cpu-quota指定cpu限制
  • 限制内存

    • Cgroups对资源的限制能力也有很多不完善的地方,被提及最多的是/proc文件系统的问题
    • /proc目录存储的是记录当前内核运行状态的一系列特殊文件,top, free等指令查看系统信息的主要数据来源
    • 通过docker run -itd -m 256m centos:v1限制内存,但是在容器里执行free,仍是宿主机的内存数据
    • 原因在/proc文件系统不了解Cgroups限制的存在。解决思路:容器不挂载宿主机的/proc目录
    • 实现工具:lxcfs。把宿主机的/var/lib/lxcfs/proc目录下的文件挂载到Docker容器的/proc目录中对应的位置
    • [root@docker ~]# vim /etc/yum.repos.d/lxcfs.repo
      [lxcfs]
      name=lxcfs3
      baseurl=https://copr-be.cloud.fedoraproject.org/results/ganto/lxc3/epel-7-x86_64/
      enabled=1
      gpgcheck=1
      gpgkey=https://copr-be.cloud.fedoraproject.org/results/ganto/lxc3/pubkey.gpg

[root@docker ~]# yum install -y lxcfs #安装软件
[root@docker ~]# lxcfs /var/lib/lxcfs/ & #启动服务

[root@docker ~]# docker run -itd --name centos1 -m 256m
-v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw
-v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw
-v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw
-v /var/lib/lxcfs/proc/stat:/proc/stat:rw
-v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw
-v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw centos:v1

镜像

  • 文件系统隔离

    • Mount Namespace,一般会在这个容器的根目录下挂载一个完整操作系统的文件系统
    • 这个挂载在容器根目录上、用来为容器进程提供隔离后执行环境的文件系统,就是所谓的"容器镜像"
    • 它也叫作rootfs(根文件系统),rootfs只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。操作系统只有在开机启动时才会加载指定版本的内核镜像
    • 由于rootfs里打包的不只是应用,而是整个操作系统的文件和目录,也就意味着,应用以及它运行所需要的所有依赖,都被封装在了一起,这就赋予了容器所谓的一致性
    • 为了解决每次操作都需要重复制作一次rootfs问题,Docker在镜像的设计中,引入了层(layer)的概念。即: 用户制作镜像的每一步操作,都生成一个层,也就是一个增量rootfs
  • overlay

    • layer的概念用到了联合文件系统(Union File System)的能力,最主要的功能是将多个不同位置的目录联合挂载(union mount)到同一个目录下

    • 文件系统:aufs, device mapper, btrfs, overlayfs, vfs, zfs。ubuntu和centos默认overlayfs

    • OverlayFS将主机上两个不同位置的目录(“层”) 联合挂载到同一目录下,底层的目录叫做lowerdir(镜像层),顶层的目录为upperdir(容器层),对外展示统一视图的目录为merged

    • 示意图

    • 操作

      • 读文件

        • 如果容器中有,则读取容器层中的文件;否则读取镜像层中的文件
        • 读操作不会改变任何层中的文件
      • 写文件

        • 创建

          • 创建在容器层中
        • 修改

          • 如果容器层有,直接修改容器层中的文件;否则将文件复制到容器层进行修改
        • 删除

          • 如果镜像层有,在容器层创建标记文件用于隐藏;否则直接删除
        • 写文件永远只发生在容器层

    • OverlayFS挂载

      • mkdir lower upper work merg
      • mount -t overlay -o lowerdir=lower/,upperdir=upper/,workdir=work/ overlay merg/
  • overlay2

    • overlay驱动只工作在一个lower OverlayFS层之上,因此需要硬链接来实现多层镜像,但overlay2驱动原生地支持多层镜像(最多128层)。因此overlay2驱动在合层相关的命令(如build和commit)中性能更好,消耗更少的inode

    • 镜像和容器的磁盘结构
      /var/lib/docker/overlay2

      • 层文件目录

        • docker image inspect nginx对应
      • l目录

        • 软连接,使用短名称指向了其他层。短名称用于避免mount参数时达到页面大小的限制
      • lower层中

        • link文件

          • 这个层对应的短名称
        • diff目录

          • 这个镜像的内容
      • upper层中

        • lower文件

          • 该层的组成,":"分隔,从高层到底层
        • diff、merged和work目录

容器

  • 运行容器包含的目录同样有着类似的文件和目录,且在镜像的基础上新增了两个目录

  • rootfs组成

    • 只读层

      • 容器的rootfs最下面的三层, 它们的挂载方式都是只读。这些层都以增量的方式分别包含了操作系统及应用程序的一部分
    • 读写层

      • 容器的rootfs最上面的一层,它的挂载方式为rw。专门用来存放修改rootfs后产生的增量,无论是增、删、改,都发生在这里
      • 可以使用docker commit和export指令,保存这个被修改过的可读写层,供其他人使用;同时,原先的只读层里的内容则不会有任何变化
    • init层

      • 一个以"-init”结尾的层,夹在只读层和读写层之间。Init层是Docker项目单独生成的一个内部层,专门用来存放/etc/hosts、/etc/resolv.conf 等信息
      • 这些文件属于只读镜像的一部分,但往往需要在启动容器时写入一些指定的值,所以就需要在可读写层对它们进行修改
      • 可是,这些修改往往只对当前的容器有效,并不希望把这些信息连同可读写层一起提交掉,所以docker在修改了这些文件之后,以一个单独的层挂载了出来
    • 所有这些层最终被联合挂载到读写层中的merged目录下,表现为一个完整的操作系统+应用来供使用

docker commit

  • 在容器运行起来后,把最上层的"可读写层”"加上原先容器镜像的只读层,打包组成了一个新的镜像
  • 由于使用了联合文件系统,在容器里对镜像rootfs所做的任何修改,都会被操作系统先复制到这个可读写层,然后再修改。这个过程,我们称之为:Copy-on-Write
  • Init 层的存在,是为了避免执行docker commit时,把Docker自己对/etc/hosts 等文件做的修改也一起提交掉

总结

  • 一个"容器",实际上是一个由 Linux Namespace、Linux Cgroups 和 rootfs 三种技术构建出来的进程的隔离环境
  • 一个正在运行的 Linux 容器可以分为两部分:
  • 一组联合挂载的 rootfs,这一部分我们称为"容器镜像"(Container Image),是容器的静态视图
  • 一个由 Namespace+Cgroups 构成的隔离环境,这一部分我们称为"容器运行时"(Container Runtime),是容器的动态视图

docker exec

  • Namespace信息在宿主机上是以一个文件的方式存在:/proc/进程号/ns

  • [root@docker ~]# docker inspect -f {{.State.Pid}} nginx
    1901

    • 查看容器的进程号
  • [root@docker ~]# ls -l /proc/1901/ns

  • 一个进程,可以选择加入到某个进程已有的Namespace当中,从而达到 “进入” 这个进程所在容器的目的,这正是docker exec的实现原理。这个操作所依赖的,乃是一个名叫 setns() 的 Linux 系统调用

  • docker网络中讲到的的container网络就是将一个容器加入到另一个容器的Network Namespace

volume

  • 容器进程

    • Docker创建的一个容器初始化进程(dockerinit),负责完成根目录的准备、挂载设备和目录、配置 hostname等一系列需要在容器内进行的初始化操作。最后通过execv()系统调用,让应用进程取代自己,成为容器里的PID=1的进程
  • 当容器进程被创建之后,尽管开启了Mount Namespace,但是在它执行chroot之前,容器进程一直可以看到宿主机上的整个文件系统,包括容器镜像

  • 镜像的各个层保存在/var/lib/docker/overlay2下,在容器进程启动时,它们会被联合挂载,容器所需的rootfs就准备好了

  • 所以只需要在rootfs准备好之后,在执行chroot之前,把Volume指定的宿主机目录(假设/home),挂载到指定的容器目录(假设/test)在宿主机上对应的目录(即/var/lib/docker/overlay2/[读写层 ID]/diff/test)上,这个Volume的挂载工作就完成了

  • 由于执行这个挂载操作时,"容器进程”"经创建了,也就意味着此时 Mount Namespace 已经开启了。所以,这个挂载事件只在这个容器里可见。你在宿主机上,是看不见容器内部的这个挂载点的。这就保证了容器的隔离性不会被 Volume 打破

  • bind mount

    • mount --bind /home /test
    • Linux的绑定挂载机制。允许将一个目录或者文件,而不是整个设备,挂载到一个指定的目录上。此时在该挂载点上进行的任何操作,仅发生在被挂载的目录或者文件上,原挂载点的内容则会被隐藏起来且不受影响
    • 绑定挂载实际上是一个inode替换的过程,挂载时将/test的inode替换为/home的inode,umount时还原
  • 容器的镜像操作,比如 docker commit,都是发生在宿主机空间的。而由于 Mount Namespace 的隔离作用,宿主机并不知道这个绑定挂载的存在。所以,在宿主机看来,容器中可读写层的 /test 目录始终是空的

你可能感兴趣的:(docker,linux,容器)