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
镜像加速器
{
“registry-mirrors”: [“https://jvfknb9j.mirror.aliyuncs.com”,“https://pf5f57i3.mirror.aliyuncs.com”]
}
- systemctl restart docker
ExecStart=/usr/bin/dockerd --registry-mirror=https://pf5f57i3.mirror.aliyuncs.com
- systemctl daemon-reload;systemctl restart docker
- 可以登录阿里云控制台, 搜索容器镜像服务, 来获取自己的镜像加速地址
查看
- docker version
- docker info
远程连接
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)映射到宿主机,将仓库目录映射到宿主机目录(自定义)
-
配置
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 ~]# cd /usr/local/harbor/
[root@docker harbor]# docker-compose stop
- [root@docker ~]# cd /usr/local/harbor/
-
-
配置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”]
- VOLUME
-
挂载匿名卷,可被运行时设置的挂载所覆盖
-
可通过 docker inspect -f {{.Mount}} container查询所挂载的目录
WORKDIR
-
语法
- 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 ishostname -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 centos7:yum
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 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/redis−server","/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
- [root@docker2 ~]# hostname
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 目录始终是空的