docker

2023-05-16

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 目录始终是空的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

docker 的相关文章

随机推荐

  • ubuntu18+jetson nano +px4+ros <——>QGC+ubuntu20+ros(关于仿真和实物运行的持续记录心得)

    持续更新 写在前面 xff1a 1 如果存在rosdep问题参考Ubuntu20 04ROS rosdep update超时失败解决方法 npu2018302257的博客 CSDN博客 2 如果存在一些github com或者是raw gi
  • [学习记录]realsence d455 +vins-fusion+px4+ego_planner下无人机的悬停与控制

    写在前面 xff1a 持续更新修改 my env xff1a ubuntu20 my pixhawk xff1a 2 4 8 my px4 firmware xff1a 1 9 0 stablepx4fmu v2 1 6 0 px4v 经济
  • 通过ROS的 nmea_navsat_driver包发布GPS坐标

    通过nmea navsat driver包发布GPS坐标 0 硬件及基本环境1 接通硬件并测试1 1 打开一个终端 xff0c 修改端口的读写权限1 2 用cutecom读取串口数据 2 安装 nmea navsat driver 及 下载
  • samba服务

    一 简单介绍 NFS网络文件系统是不能跨操作系统使用的 xff0c 至少说现在跨windows和linux之间完成文件系统级的共享nfs是无法完成的 xff0c 据说在上个世纪90年度的时候 xff0c 在澳大利亚有一个大学生就面临这样的现
  • 近期尝试UR5和PhantomOmni的联动仿真出现的问题

    近期尝试UR5和PhantomOmni的联动仿真出现的问题 最近在Github找到了几个代码 xff0c 虽然代码是好几年前的 xff0c 但经过尝试编译后有部分可以用 xff0c 有部分有问题 xff0c 现在拿一个来解释一下几年前的RO
  • 解决Linux系统不能上网问题

    解决Linux系统不能上网问题 相信很多Linux的萌新们 xff0c 初次安装LInux 系统后会为不能上网而烦恼 这一问题表现为 xff1a 能连到wifi但就是上不了网 xff01 xff01 xff01 导致这一问题的原因是 xff
  • C语言——蔡勒(Zeller)公式的使用

    C语言 蔡勒公式的使用 蔡勒公式简介 xff1a 蔡勒 xff08 Zeller xff09 公式 xff0c 是一个计算星期的公式 xff0c 随便给一个日期 xff0c 就能用这个公式推算出是星期几 计算公式 xff1a 核心公式 xf
  • 基于单片机定时智能窗帘控制系统设计-毕业资料

    资料下载地址 1022 xff08 百度网盘 xff09 xff1a 点击下载 智能窗户 AT89S52 1602显示 步进电机转动模拟开窗关窗 xff08 1 xff09 手动控制 xff1a 该功能是根据用户的需求通过按键进行窗帘的开关
  • windows10下安装ubuntu子系统

    windows10下安装ubuntu子系统 在win10上使用Ubuntu除了使用虚拟机外 xff0c 还有一种官方支持的Linux子系统模式 子系统上的流畅度比虚拟机高出了不知多少 xff01 经过多次尝试才成功配置 废话不多说 xff0
  • Windows10系统下的WSL+Ubuntu图形桌面配置

    Windows10系统下的WSL 43 Ubuntu图形桌面配置 参考 xff1a windows10下安装Ubuntu子系统 Windows下安装VcXsrv WSL Ubuntu下安装xfce desktop span class to
  • C++:什么是STL?

    什么是STL xff1f 1 STL概论1 1 STL基本概念1 2 STL六大组件简介1 3 STL优点 2 STL三大组件2 1 容器2 2 算法2 3 迭代器2 3 案例 1 STL概论 长久以来 xff0c 软件界一直希望建立一种可
  • makefile中.PHONY的作用是什么?

    makefile中 PHONY的作用是什么 xff1f 初学makefile的时候 xff0c 有一个关键字 PHONY 搞不懂 xff0c 在请教过同学之后豁然开朗 xff0c 遂写下经验望帮助更多的同学能够理解 在某度可以搜到phony
  • TCP和UDP的区别

    TCP和UDP的区别 1 TCP 是什么2 UDP 是什么3 TCP 和 UDP 的不同 1 TCP 是什么 TCP 的全称是Transmission Control Protocol xff0c 传输控制协议 它能够帮助你确定计算机连接到
  • 操作系统死锁 四个必要条件

    操作系统死锁 四个必要条件 1 死锁 xff1a 如果一组进程中的每一个进程都在等待仅由该组进程中的其它进程才能引发的事件 xff0c 那么该组进程是死锁的 2 产生死锁的原因 xff1a xff08 1 xff09 竞争不可抢占性资源 x
  • C++智能指针总结(面试常问)

    C 43 43 智能指针总结 xff08 面试常问 xff09 1 智能指针的实现原理2 C 43 43 11四种智能指针总结2 1 auto ptr xff1a 2 2 unique ptr 2 3 share ptr 2 4 weak
  • C++可重入函数和不可重入函数

    C 43 43 可重入函数和不可重入函数 可重入函数是指能够被多个线程 同时 调用的函数 xff0c 并且能保证函数结果正确不必担心数据错误的函数 不可重入函数是指不能运行在多任务环境下 xff0c 除非能保证互斥的函数 由于使用了未受保护
  • STM32单片机中WIFI模块数据传输问题(float->u32->u8/u8->u32->float)

    STM32单片机中WIFI模块数据传输问题 xff08 float gt u32 gt u8 u8 gt u32 gt float xff09 最近在做毕业设计 xff0c 在做wifi数据传输与接收时 xff0c 涉及到了STM32单片机
  • Windows10忘记用户密码?怎么打开电脑?

    Windows10忘记用户密码 xff1f 怎么打开电脑 xff1f 首先在输入密码的登录界面连续敲5次shift按键进入cmd命令行 输入net user查看用户 现在是自己创建的用户忘记密码 xff08 我的是Sunrise用户 xff
  • java --向上转型和向下转型

    一 简述 Java 转型主要是用在继承和接口实现的场景 xff0c Java 转型可分为向上转型和向下转型 xff0c 区别如下 xff1a 向上转型 xff1a 通过子类对象实例化父类对象 xff0c 这属于自动转换 向下转型 xff1a
  • docker

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