一、docker的学习路径
- 单体Docker
- Docker概述
- Docker安装
- Docker命令
- 镜像命令
- 容器命令
- 操作命令
- ……
- Docker镜像!!
- 容器数据卷
- Dockerfile
- Docker的网络原理
- IDEA整合Docker
- Docker集群
- Docker Compose
- Docker Swarm 简化版的k8s
- CI\CD Jenkins
二、docker概述
1.docker为什么会出现?
- 窘境:
- 一款产品:开放和上线 两套环境!测试环境和 线上环境,辛辛苦苦在测试环境中搭建的各项配置在应用环境中还要在进行一次配置,配置第一麻烦,第二容易出现问题,不够稳定
- 开发 -- 运维 问题:我在我的电脑上可以运行!
- 版本更新,导致服务不可用!(现实的例子就是多人开发的项目在服务器上,最开始的时候A,B用的包都是2.1的版本,包最新版本是2.4,这时候A想开发的功能,需要将包进行升级,但是兼容性不好,原来的2.1中好多功能被更改,这时候上线,运维将包更新到2.4,导致了B的服务不可用了,回退版本的话,A的新开发的功能也不能用了,这样运维和开发的压力都巨大)
- 环境配置是十分的麻烦,对多个机器进行管理,每个机器上都要部署环境(集群Redis,ES,Hadoop……)!费时费力,还容易出现问题
- 发布一个项目,能不能(程序代码+环境)放在一起进行打包,安装上线
- 之前在服务器配置一个应用的环境Redis,Mysql,jdk,ES,Hadoop,配置超级麻烦,而且还不能跨平台,在window上开发,最后发布到linux上
- 解决
- 传统:开发功能代码,运维部署环境
- 现在:开发打包部署,一套流程做完。开发兼运维
- Java ---apk--发布(应用商店)---张三使用apk--安装即可使用
- Java -- jar(环境) -- 打包项目带上环境(镜像)---(Docker仓库:商店)----下载我们发布的镜像----直接运行即可
- Docker的解决方案
- 思想:
集装箱(隔离)的思想,向上面的实例中的困扰(A,B因为包升级问题),我们将A的项目和A的开发环境打个包,放在服务器上运行,A的项目运行良好,同样B的项目和B的开发环境也打包,放在服务器上,B的项目也运行良好,就像船上的集装箱一样,A,B彼此隔离互不影响,A的安装包更新,B的不受影响,这样解决了包升级引发服务不可用的问题。 - 利用隔离机制,可以将服务器利用到极致。
2.Docker的历史
- 2010年。几个搞IT年轻人,就在美国成立了一家公司“dotcloud”一做一些pass的云计算
- LXC有关的虚拟机技术!
- 他们将自己的技术(虚拟化技术)命名Docker!
- Docker刚出生的时候,没有引起行业的重视!dotCloud,活不下去了!
- 2013年,"开源"
- 2014年4月9日。Docker 1.0 版本发布!
- Dockerk为什么这么火?十分的轻巧!
- 在容器技术出现之前。我们都是用虚以机技术!
- 重点
- 虚拟机属于虚拟化技术,Docker是容器技术,也是一种虚拟化技术
- 虚拟机:在windows中安装一个Vmware,通过这个软件可以虚拟出来一台或者多台的电脑,一个虚拟机模拟真实电脑非常的大,多达10几个G,非常的笨重,开机需要几分钟
- 容器:隔离技术,镜像(最核心的环境4M(兆)+jdk+mysql)<一个mysql的docker镜像>,十分的小巧,运行镜像就可以了,几兆/几十兆的很常见,秒级的启动非常快。
3.Docker简介
- docker是基于Go语言开发的!开源的项目
- 官网:www.docker.com/
- 官方文档:docs.docker.com/
- Docker的文档是十分详细的
- dockerhub的网址:hub.docker.com/
- Dockerhub的中文手册:www.widuu.com/chinese_doc…
4.Docker VS 虚拟机
-
虚拟机技术
-
- 虚拟机技术是真实的虚拟出来一个操作系统:其中包含 Kernel + Lib ,让后上层的应用都是基于这个环境进行安装和运行的,这个Lib就像我们的真实系统中的库一样,要为所有的上层软件服务,这样的话,lib的东西就要非常的全,就非常的大,这也是传统的虚拟机庞大而笨重的原因
- 技术缺点
- 占用的资源十分的多
- 冗余的步骤比较多
- 启动很慢
-
Docker
- 在docker中只是借助了kernel,完成操作系统的基本功能,这样的话,我们在安装上层的应用的时候,只需要安装相对应软件的依赖就行了,并不需要将用到的、用不到的lib全部安装,所以在每个部分,安装好应用和应用的依赖就是一个集装箱,所以非常的轻便
- 容器化技术不是模拟的一个完整的操作系统
-
举例理解:
- 假设我们想要安装python
- 虚拟机
- Kernel
- Lib(python的依赖环境+C语言的依赖环境+java的依赖环境+……等等许多软件的依赖环境)
- 人:安装python即可
- Docker
- Kernel
- 人:安装python + python的依赖环境
- 虚拟机
-
比较docker和虚拟机技术的不同
- 传统的虚拟机,虚拟出来一套硬件,运行一个完整的操作系统,然后在这个操作系统中中安装和运行软件
- 容器内的应用直接运行宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻便了
- 每个容器之间相互隔离,每个容器内都有一个属于自己的文件系统,互不影响。
-
DevOps(开发,运维)
- 应用更快速的交付和部署
- 传统:一堆的帮助文档,安装程序
- Docker:打包成镜像发布测试,一键运行
- 更便捷的升级和扩缩容
- 使用了Docker之后,我们部署应用就像是在搭积木一样
- 项目打包成一个文件,扩展 服务器A 服务器B
- 更简单的系统运维
- 在容器化之后,我们的开发,测试环境都是高度一致的
- 更高效的计算资源利用
- Docker是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例!服务器的性能可以被开发到极致
- 应用更快速的交付和部署
5.Docker的组成
- Docker的架构图
- Docker的组成部分
- 镜像(image)
- docker镜像就好像是一个模板,可以通过这个模板来创建出来容器的服务,mysql镜像--->run命令--->mysql容器(提供mysql服务)
- 通过这个镜像可以创建多个容器(最终服务运行或者项目运行就在容器中的)
- 容器(container)
- Docker利用容器技术,独立运行一个或者一个组应用,通过镜像进行创建
- 启动,停止,删除,基本命令
- 目前就可以把这个容器理解为就是一个简易的linux系统
- 仓库(repository)
- 仓库就是存放镜像的地方
- 仓库分为公有仓库和私有仓库
- Docker Hub(默认是国外的)
- 阿里云……都有容器服务器(配置镜像加速)
- 镜像(image)
6.安装Docker
-
使用命令
uname -r查看内核版本- 4.4.4-187-generic
-
命令
cat /etc/os-release查看系统的详细信息root@iZbp12kyfwxfku3288sr59Z:/# cat /etc/os-release NAME="Ubuntu" VERSION="16.04.7 LTS (Xenial Xerus)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 16.04.7 LTS" VERSION_ID="16.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" VERSION_CODENAME=xenial UBUNTU_CODENAME=xenial -
安装过程:文档中有详细的说明
7.docker的运行
-
run命令执行后到底发生了什么?
- 描述就是:在执行了docker这个命令后,从client端发送到server这个服务端,在服务中进行执行,这样首先docker会在本地进行查找,看看镜像是不是存在,存在直接运行,不存在的话,会发送指令到docker-hub中,进行查找,若找到了就进行下载,若没有找到,会从服务端返回错误信息,假设找到了,下载到本地,本地就会加载这个镜像,运行起来
-
底层原理
- docker是一个Client-Server结构的系统,docker的守护进程运行在主机上,通过socket从客户端进行访问
- Docker-server接收到docker-client的指令,就会执行这个命令
-
Docker 为什么会比 VM快?
-
docker有着比虚拟机更少的抽象层
-
docker利用的是宿主机的内核,vm需要的是Guest OS
-
- 所以说,新建一个容器的时候,docker不需要像虚拟机一样加载一个操作系统的内核,避免了引导。虚拟机是加载Guest OS,分钟级别的,而docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级
-
8.docker的常见命令
-
帮助文档
-
文档的使用方法
-
基础命令
-
docker version -
docker info -
docker 命令 --help
-
-
镜像命令
-
查看所有的镜像列表
-
docker images - 可选项
- -a 列出所有的镜像
- -q 只显示镜像的id
-
-
搜索镜像
-
docker search 镜像名称 -
通过dockerhub进行镜像的搜索:查找镜像
-
可选项
- --filter =stars=3000 通过stars进行过滤
-
-
下载镜像
-
docker pull 镜像名称 docker pull 镜像名称:tag
-
-
删除镜像
-
rmi -------remove image Docker rmi -f 镜像id/镜像名称 删除一个镜像 Docker rmi -f 镜像id 镜像id 镜像id 删除多个镜像 Docker rmi -f $(docker images -aq) 删除所有镜像
-
-
-
容器命令
-
我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习
- docker pull centos
-
新建容器并启动
- Docker run [可选参数] image
- --name = "Name" 给容器起名字 centos1 centos2 用来区分容器
- -d 后台的方式进行运行
- -it 使用交互式的运行方式,进入容器后查看内容
- -p 指定容器的端口 -p 8080:8080
- 四种端口的指定方式
- -p ip:主机端口:容器端口
- -p 主机端口:容器端口 (最常用)
- -p 容器端口
- 端口号
- 四种端口的指定方式
- 这个部分使用exit的命令退出了容器
- Docker run [可选参数] image
-
列出所有运行的容器
- docker ps 命令
- 无参数 列出当前正在运行的容器
- -a 列出当前正在运行的容器+带出历史运行过的容器
- -n=? 显示最近创建的容器
- -q 只显示容器的编号
- 无参数 列出当前正在运行的容器
- docker ps 命令
-
退出容器
- Exit 直接容器停止并且退出了
- Ctrl+Q 容器不停止但是退出了
- Exit 直接容器停止并且退出了
-
删除容器
- Docker rm 容器id
- Docker rm 容器id 容器id
- Docker rm -f $(docker ps -aq)
- Docker ps -a -q|xargs docker rm 管道的方式删除所有的容器
- -f 是强制删除
-
启动和停止容器的操作
- 启动
- docker start 容器id
- 重启容器
- docker restart 容器id
- 停止容器
- docker stop 容器id
- 杀掉容器
- docker kill 容器id
- 启动
-
-
其他常见命令
-
后台启动容器
- Docker run -d 镜像名
- 问题:docker ps 发现centos停止了
- 常见的坑:docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动提停止
- 通俗的理解就是:我们在run centos的时候,之前会切换到容器之中让我们进行操作,但是-d直接从后台进行启动,docker会检测到这个部分是没有人操作或者容器没有动静,自己就会将相应的资源进行停止
- 所以nginx容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
- Docker run -d 镜像名
-
查看日志
- Docker logs -tf --tail 10 容器id
- 测试
- 自己编写一段shell脚本让程序在后台自己跑
- Docker run -d centos /bin/sh -c "while true;do echo hahaha;sleep 1;done"
- docker ps
- Docker logs -tf --tail 10 容器id
- 自己编写一段shell脚本让程序在后台自己跑
- Docker logs -tf --tail 10 容器id
-
查看容器内部的进程信息
- top命令
- docker top 容器id
- top命令
-
查看容器的元数据
- docker inspect 容器id
-
[ { "Id": "eea991c4d00ba5edf76f1eaf1bc769c6d78169b39d8426ebcb6bfa8a1710d7d4", #容器的id "Created": "2020-10-02T10:40:04.102524201Z", # 创建时间 "Path": "/bin/sh", "Args": [ "-c", "while true;do echo haha;sleep 1;done" # 参数,运行的时候的参数 ], "State": { # 状态 "Status": "running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 28047, "ExitCode": 0, "Error": "", "StartedAt": "2020-10-02T10:45:04.993272581Z", "FinishedAt": "2020-10-02T10:42:22.663247801Z" }, "Image": "sha256:0d120b6ccaa8c5e149176798b3501d4dd1885f961922497cd0abef155c869566", "ResolvConfPath": "/var/lib/docker/containers/eea991c4d00ba5edf76f1eaf1bc769c6d78169b39d8426ebcb6bfa8a1710d7d4/resolv.conf", "HostnamePath": "/var/lib/docker/containers/eea991c4d00ba5edf76f1eaf1bc769c6d78169b39d8426ebcb6bfa8a1710d7d4/hostname", "HostsPath": "/var/lib/docker/containers/eea991c4d00ba5edf76f1eaf1bc769c6d78169b39d8426ebcb6bfa8a1710d7d4/hosts", "LogPath": "/var/lib/docker/containers/eea991c4d00ba5edf76f1eaf1bc769c6d78169b39d8426ebcb6bfa8a1710d7d4/eea991c4d00ba5edf76f1eaf1bc769c6d78169b39d8426ebcb6bfa8a1710d7d4-json.log", "Name": "/dreamy_mcclintock", "RestartCount": 0, "Driver": "overlay2", "Platform": "linux", "MountLabel": "", "ProcessLabel": "", "AppArmorProfile": "docker-default", "ExecIDs": null, "HostConfig": { "Binds": null, "ContainerIDFile": "", "LogConfig": { "Type": "json-file", "Config": {} }, "NetworkMode": "default", "PortBindings": {}, "RestartPolicy": { "Name": "no", "MaximumRetryCount": 0 }, "AutoRemove": false, "VolumeDriver": "", "VolumesFrom": null, "CapAdd": null, "CapDrop": null, "Capabilities": null, "Dns": [], "DnsOptions": [], "DnsSearch": [], "ExtraHosts": null, "GroupAdd": null, "IpcMode": "private", "Cgroup": "", "Links": null, "OomScoreAdj": 0, "PidMode": "", "Privileged": false, "PublishAllPorts": false, "ReadonlyRootfs": false, "SecurityOpt": null, "UTSMode": "", "UsernsMode": "", "ShmSize": 67108864, "Runtime": "runc", "ConsoleSize": [ 0, 0 ], "Isolation": "", "CpuShares": 0, "Memory": 0, "NanoCpus": 0, "CgroupParent": "", "BlkioWeight": 0, "BlkioWeightDevice": [], "BlkioDeviceReadBps": null, "BlkioDeviceWriteBps": null, "BlkioDeviceReadIOps": null, "BlkioDeviceWriteIOps": null, "CpuPeriod": 0, "CpuQuota": 0, "CpuRealtimePeriod": 0, "CpuRealtimeRuntime": 0, "CpusetCpus": "", "CpusetMems": "", "Devices": [], "DeviceCgroupRules": null, "DeviceRequests": null, "KernelMemory": 0, "KernelMemoryTCP": 0, "MemoryReservation": 0, "MemorySwap": 0, "MemorySwappiness": null, "OomKillDisable": false, "PidsLimit": null, "Ulimits": null, "CpuCount": 0, "CpuPercent": 0, "IOMaximumIOps": 0, "IOMaximumBandwidth": 0, "MaskedPaths": [ "/proc/asound", "/proc/acpi", "/proc/kcore", "/proc/keys", "/proc/latency_stats", "/proc/timer_list", "/proc/timer_stats", "/proc/sched_debug", "/proc/scsi", "/sys/firmware" ], "ReadonlyPaths": [ "/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger" ] }, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/d0ed21bb25573ab3a59182ddfc05c54a169353094b5742fc3394cdce4288e3b8-init/diff:/var/lib/docker/overlay2/8c0d8f954b69ff5d36f707acf628e795ab75eaff1be8a6013c8529a0515e6cc7/diff", "MergedDir": "/var/lib/docker/overlay2/d0ed21bb25573ab3a59182ddfc05c54a169353094b5742fc3394cdce4288e3b8/merged", "UpperDir": "/var/lib/docker/overlay2/d0ed21bb25573ab3a59182ddfc05c54a169353094b5742fc3394cdce4288e3b8/diff", "WorkDir": "/var/lib/docker/overlay2/d0ed21bb25573ab3a59182ddfc05c54a169353094b5742fc3394cdce4288e3b8/work" }, "Name": "overlay2" }, "Mounts": [], "Config": { "Hostname": "eea991c4d00b", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], "Cmd": [ "/bin/sh", "-c", "while true;do echo haha;sleep 1;done" ], "Image": "centos", "Volumes": null, "WorkingDir": "", "Entrypoint": null, "OnBuild": null, "Labels": { "org.label-schema.build-date": "20200809", "org.label-schema.license": "GPLv2", "org.label-schema.name": "CentOS Base Image", "org.label-schema.schema-version": "1.0", "org.label-schema.vendor": "CentOS" } }, "NetworkSettings": { "Bridge": "", "SandboxID": "95611910bf93a7f37cc5ce62b7430a21bde5736bc9831f658f3cbfa974b365a5", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": {}, "SandboxKey": "/var/run/docker/netns/95611910bf93", "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "a04cb929cd28c88618e0f170ff3d968b23d350f1bb7baa8e080d3e4e03d6c324", "Gateway": "172.17.0.1", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "MacAddress": "02:42:ac:11:00:02", "Networks": { "bridge": { "IPAMConfig": null, "Links": null, "Aliases": null, "NetworkID": "d80554cf321bff3d2821acfe37abdff08809fa1ee0f4a8d37693bb949e529476", "EndpointID": "a04cb929cd28c88618e0f170ff3d968b23d350f1bb7baa8e080d3e4e03d6c324", "Gateway": "172.17.0.1", "IPAddress": "172.17.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:11:00:02", "DriverOpts": null } } } } ]
-
进入当前正在运行的容器
- exec
-
通常情况下的容器都是在后台方式运行的,需要我们进入容器 ,修改一些配置
-
docker exec -it 容器id /bash/bin- 进入容器后打开一个新的终端,可以在里面进行操作(常用)
-
Attach
-
docker attach 容器id - 进入容器中正在执行命令终端,不会启动新的进程
-
-
- exec
-
从容器内拷贝文件到主机上
-
docker cp 容器id:/xx/文件路径 本机路径- 拷贝是一个手动的过程,未来我们使用 -v 卷的技术,可以实现,自动同步
-
-
查看历史命令
-
docker history 镜像id -
-
-
-
命令总结
三、Docker镜像讲解
镜像是什么?
- 镜像是一种轻量级,可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容,包括代码,运行时,库,环境变量和配置文件
- 所有的应用,直接打包成docker镜像,就可以直接跑起来。
- 如何得到镜像
- 从远程仓库下载
- 朋友拷贝给你
- 自己制作一个镜像Dockerfile
Docker镜像加载原理
- UnionsFS(联合文件系统)
- UnionFS(联合文件条统):Union文件条统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础,镜像可以通过分层来讲行继承,基于基础像(没有父镜像),可以制作各种具体的应用镜像。
- 特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
- Docker镜像加载原理
- docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
- bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linu×刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Uni×系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs.
- rootfs (root file system)。在bootfs之上、包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
- 在这个部分其实就是我们说的linux的系统(只不过是比平时用的linux要更加的精简),加载进来后,里面只包含一些基本的支持linux运行的东西
- 平时我们安装仅虚拟机的centos都是好几个G,为什么Docekr这里才200M?
- 对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工貝和程序库就可以了。因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs.
- 注意:内核还是用的宿主机的
分层理解
- 分层的镜像
-
我们可以去下载一个镜像,注意观察下载日志的输出,可以看到的是一层一层的下载
-
-
[ { "Id": "sha256:84c5f6e03bf04e139705ceb2612ae274aad94f8dcf8cc630fbf6d91975f2e1c9", "RepoTags": [ "redis:latest" ], "RepoDigests": [ "redis@sha256:1cfb205a988a9dae5f025c57b92e9643ec0e7ccff6e66bc639d8a5f95bba928c" ], "Parent": "", "Comment": "", "Created": "2020-09-10T19:14:19.090647481Z", "Container": "01e8a1053cea8d8adc30b6f9c0e1d84a9c76535f6b0896c8fcff25d54c9c0429", "ContainerConfig": { "Hostname": "01e8a1053cea", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "6379/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "GOSU_VERSION=1.12", "REDIS_VERSION=6.0.8", "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.0.8.tar.gz", "REDIS_DOWNLOAD_SHA=04fa1fddc39bd1aecb6739dd5dd73858a3515b427acd1e2947a66dadce868d68" ], "Cmd": [ "/bin/sh", "-c", "#(nop) ", "CMD [\"redis-server\"]" ], "ArgsEscaped": true, "Image": "sha256:194e5decbc63d8d28f685b6899326cc681b1e2265dd556d96ee12226400ca6d5", "Volumes": { "/data": {} }, "WorkingDir": "/data", "Entrypoint": [ "docker-entrypoint.sh" ], "OnBuild": null, "Labels": {} }, "DockerVersion": "18.09.7", "Author": "", "Config": { "Hostname": "", "Domainname": "", "User": "", "AttachStdin": false, "AttachStdout": false, "AttachStderr": false, "ExposedPorts": { "6379/tcp": {} }, "Tty": false, "OpenStdin": false, "StdinOnce": false, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "GOSU_VERSION=1.12", "REDIS_VERSION=6.0.8", "REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.0.8.tar.gz", "REDIS_DOWNLOAD_SHA=04fa1fddc39bd1aecb6739dd5dd73858a3515b427acd1e2947a66dadce868d68" ], "Cmd": [ "redis-server" ], "ArgsEscaped": true, "Image": "sha256:194e5decbc63d8d28f685b6899326cc681b1e2265dd556d96ee12226400ca6d5", "Volumes": { "/data": {} }, "WorkingDir": "/data", "Entrypoint": [ "docker-entrypoint.sh" ], "OnBuild": null, "Labels": null }, "Architecture": "amd64", "Os": "linux", "Size": 104192177, "VirtualSize": 104192177, "GraphDriver": { "Data": { "LowerDir": "/var/lib/docker/overlay2/b3f7aeaaad80f9a7f51fa25392102bce2a70010c317379b2573d2465fdc8a898/diff:/var/lib/docker/overlay2/acda3ac7c8a38a0a81bb7be57e886acd3deb9069d1ae1c63b85a0dcf0e1e2faa/diff:/var/lib/docker/overlay2/a7759817ae7c4ee1e049b2ae8def34e032d6f2c7d5a8a772be39b3f854d79d76/diff:/var/lib/docker/overlay2/e48e277bb4e92c80a0354415dddedb0fd20381cb7c323af98f0abd59b7ebbf6b/diff:/var/lib/docker/overlay2/4acd01baf323e85d1c950b33df577b136e15a8f927d555f9fff55a79d274a1e5/diff", "MergedDir": "/var/lib/docker/overlay2/b74978444100131212adf59d8583b58fb58dd69e72b0fd03ca3f0715b523806d/merged", "UpperDir": "/var/lib/docker/overlay2/b74978444100131212adf59d8583b58fb58dd69e72b0fd03ca3f0715b523806d/diff", "WorkDir": "/var/lib/docker/overlay2/b74978444100131212adf59d8583b58fb58dd69e72b0fd03ca3f0715b523806d/work" }, "Name": "overlay2" }, "RootFS": { "Type": "layers", "Layers": [ #在这个部分我们能看到文件系统的层 "sha256:07cab433985205f29909739f511777a810f4a9aff486355b71308bb654cdc868", "sha256:45b5e221b6729773b50b4fc89e83a623f9f63ddf37e37078d5f197811db6177d", "sha256:7fb1fa4d4022ba2387d0df7820fa41c797eeda6f1192920da8cb99c6475dd9d1", "sha256:47d8fadc671445422662d5a25e09b2fabd2a77c7da4338ab3f817592fd60c84b", "sha256:ea96cbf71ac4d770813f8fd209a20ddb3b81c69992be2c0c3e1d1a4b9fb0da1a", "sha256:2e9c060aef92b6b958bee61fbf5f239443c629e6a62f1103c3ada7deb10aa543" ] }, "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" } } ] -
思考:为什么Docker镜像要采用这种分层的结构那?
- 最大的好处,我觉得莫过于资源共享!比如有多个镜像团队都从相同的Base镜像构建而来,那么宿主机只需要在磁盘中保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
-
- 理解
-
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
-
举一个简单的例子,假如基于Ubuntu Linux16.04创建一个新的镜像,这就是新像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
-
该镜像当前已经包含3个镜像层,如下图所示(这只是一般用于演示的很简单的例子)。
-
-
在添加额外的镜像层的同时,像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。
-
-
上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
-
下图中展示了一个稍微复杂的三层镜像,在外部来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本。
-
-
这种情况下,上层像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
-
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。Linux上可用的存储引擎有AUFS、Overlay2、DeviceMapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术并且每种存储引擎都有具独有的性能特点。
-
Docker在Windows上仅支持windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和COW[I]。
-
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
-
-
Docker像都是只读的,当容器启动时,一个新的可写层被那载到镜像的顶部!
-
这一层就是我们通堂说的容器层,容器之下的都叫像层
-
commit镜像
-
Docker commit 提交容器成为一个新的副本
Docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像:[Tag]- 实战测试
- 启动一个默认的tomcat
- 发现这个tomcat 是没写webapps应用的,镜像的原因,官方镜像默认webapps下面是没有东西的
- 自己拷贝文件进去
- 将我们修改过的镜像提交成一个新的镜像
- 实战测试