Docker概述
—— Notes from WAX through KuangShen
准确来说,这是一篇学习笔记!!!
Docker为什么出现
一款产品:开发—上线 两套环境!应用环境如何铜鼓?
开发 – 运维。避免“在我的电脑米问题呀!!!”
环境配置十分麻烦,没一个机器都要部署环境(集群Redis、ES…)!费时费力。
发布一个项目(jar + (Redis、Mysql、jdk、ES))能否带上安装环境进行打包发布。
Windows开发,最后发布到Linux:
传统:开发项目,运维来做。
现在:开发打包部署上线,一套流程做完。
例:
java – apk – 发布(应用商店) – 张三使用apk – 安装即可使用!
java – jar(环境) – 打包项目带上环境(镜像)-- (Docker仓库:商店)-- 下载发布的镜像 – 直接运行即可!
Docker给出以上问题的解决方案!
Docker的思想来自于集装箱!
隔离:Docker核心思想,打包装箱!每个箱子是互相隔离的
Docker通过隔离机制,可以将服务器利用到极致!
Docker的历史
2010年,几个搞IT的年轻人,就在没美国成立了一家公司dotCloud
做一些pass的云计算服务,LXC有关的容器技术!
他们将自己的技术(容器话技术)命名 就是Docker!
Docker刚刚诞生的时候,没有引起行业注意!公司举步维艰,于是决定开源!!!
2013,Docker开源!
Docker越来越多的人发现Docker的有点!火了,每个月都会更新一个版本!
2014年4月9日,Docker1.0发布!
Docker为什么这么火?极其轻巧
在容器技术出来之前,我们都是使用虚拟技术!
虚拟机:在Windows中装一个Vmware,通过这个软件可以虚拟化出来一个或多个电脑,但是十分笨重。
虚拟机也是属于虚拟化技术,Docker容器技术,也是一种虚拟化技术
vm:linux centos原生镜像(一个电脑) 隔离,需要开启多个虚拟机! 几个G大小
docker:隔离,镜像(最核心的环境4m + jdk + mysql)十分轻巧,运行镜像就可以了,几 M Kb 秒级启动
到现在,所有开发人员都必须会的工具 Docker。
聊聊docker
Docker是基于 Go 语言开发的,开源项目!
官网:https://www.docker.com
文档:https://docs.docker.com 超详细
仓库地址:https://hub.docker.com
Docker的作用
虚拟机技术缺点:
1、资源占用多
2、冗余步骤多
3、启动慢
Docker和虚拟技术的不同:
-
统虚拟机,虚拟出一切硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件
-
容器内的应用直接运行再宿主机的内核,容器是没有自己的内核的,也没有虚拟我们的硬件,所以轻便
-
每个容器内是相互隔离的,每个容器内都有一个属于自己的文件系统,互不影响
DevOps(开发/运维)
应用更快速的交付和部署
-
传统方式:一堆帮助文档,安装程序
-
Docker:打包镜像发布测试,意见运行
更快捷的升级和扩缩容
-
使用Docker以后,我们不熟应用就类似搭积木简单
-
项目打包为一个镜像,扩容 服务器A 服务器B
更简单的系统运维
- 在容器化之后,开发、测试测试环境都是高度一致的。
更高效的计算资源利用:
- Docker是内核级的虚拟化,可以在一个物理机上运行很多个容器实例。服务器的性能能利用到极致。
docker的安装
docker的基本组成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cXdU4l9R-1691285233022)(C:\Users\wax\AppData\Roaming\Typora\typora-user-images\image-20210424153809962.png)]
镜像(image):
Docker镜像好比一个模板,可以通过这个模板来创建容器服务。
通过这个镜像可以创建多个容器(最终服务运行或项目运行就是在容器中的)
容器(container):
Docker利用容器技术,独立运行一个或者一组应用,通过镜像来创建。
虽然不是很准确,但是可以粗略的连接容器就是一个简单的LInux系统
仓库(repository):
仓库就是存放镜像的地方
仓库分为公有仓库和私有仓库
Docker Hub
阿里云…都有容器服务器
安装docker
安装环境
- 需要会一点Linux的基础
- CentOS(本次用例)
环境查看
#系统版本是3.10以上的
[root@sa ~]# uname -r
3.10.0-957.el7.x86_64
[root@sa ~]#
#系统版本
[root@sa ~]# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
安装docker
帮助文档:https://docs.docker.com/engine/install/
# 1、删除旧的版本
sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine
# 2、依赖包
sudo yum install -y yum-utils# 3、设置镜像的仓库,当然也可以设置阿里云的
sudo yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repo 官方
sudo yum-config-manager \--add-repo \https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 阿里云#更新yum软件包索引
yum makecache fast# 4、安装docekr -ce表示社区版 -ee表示企业版
yum install docker-ce docker-ce-cli containerd.io# Ubuntu 安装sudo apt install docker.io# 5、运行docker
sudo systemctl start docker# 6、查看版本信息,有则是安装成功
docker version# 7、查看启动是否成功
docker run hello-world # 会打印“Hello from Docker!” 则成功
卸载docker
# 1、卸载依赖
sudo yum remove docker-ce docker-ce-cli containerd.io# 2、删除资源
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
hello world流程
此处就不画图了:
docker run 开始,docker就会在本地查找镜像,
有,则直接运行该镜像。没有,就会在docker Hub(官方镜像仓库)上去下载,下载到本地后直接运行,没有下载到则返回错误
底层原理
docker是怎么工作的
Docker是一个 Client - Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问!
DockerServer 接收到Docekr Client的指令执行!
Docker 为什么比 VM 快
- Docker有着比虚拟机更少的抽象层
- Docker利用的是宿主机的内核,VM需要的是 Guest OS。
所以说,新建一容器的时候,Docker不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载Guest OS,分钟级别,而Docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级启动。
当然,安全性方面,肯定还是VM虚拟机更安全,毕竟是一个完整的虚拟系统。
Docker的常用命令
帮助命令
docker version # 显示docker版本信息
docker info # 显示docker的系统信息,包括镜像和容器的数量
docekr --help # 帮助
帮助文档 https://docs.docker.com/reference/
镜像命令
Docker images 查看所有本地镜像
[root@sa ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d1165f221234 2 months ago 13.3kB
vue-docker-demo-image latest 4835e3101950 8 months ago 135MB
docker-demo-image latest 831b48a0447e 8 months ago 133MB# 含义
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的id
REPOSITORY 镜像创建时间
SIZE 镜像大小# 可选项-a, --all Show all images (default hides intermediate images) # 显示所有镜像--digests Show digests # 显示摘要-f, --filter filter Filter output based on conditions provided--format string Pretty-print images using a Go template--no-trunc Don't truncate output-q, --quiet Only show numeric IDs # 只显示id
docekr search 搜索镜像
[root@sa ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 10876 [OK]
mariadb MariaDB Server is a high performing open sou… 4102 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create… 808 [OK]
...
[root@sa ~]# # 可选项,通过收藏来过滤-f, --filter filter Filter output based on conditions provided--format string Pretty-print search using a Go template--limit int Max number of search results (default 25)--no-trunc Don't truncate output
[root@sa ~]# docker search mysql --filter=STARS=3000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 10876 [OK]
mariadb MariaDB Server is a high performing open sou… 4102 [OK]
[root@sa ~]#
**docker pull ** 下载镜像
# 下载镜像 docker pull 镜像名[:tag]
[root@sa ~]# docker pull mysql
Using default tag: latest # 如果没有指定tag,默认就是latest
latest: Pulling from library/mysql
69692152171a: Pull complete # 分层下载,docker image的核心--联合文件系统
1651b0be3df3: Pull complete
951da7386bc8: Pull complete
0f86c95aa242: Pull complete
37ba2d8bd4fe: Pull complete
6d278bb05e94: Pull complete
497efbd93a3e: Pull complete
f7fddf10c2c2: Pull complete
16415d159dfb: Pull complete
0e530ffc6b73: Pull complete
b0a4a1a77178: Pull complete
cd90f92aa9ef: Pull complete
Digest: sha256:d50098d7fcb25b1fcb24e2d3247cae3fc55815d64fec640dc395840f8fa80969 # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址
[root@sa ~]# # 以下两个命令等价
docker pull mysql
docker pull docekr.io/library/mysql:latest
docker images 查看镜像
[root@sa ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql latest c0cdc95609f1 3 days ago 556MB
hello-world latest d1165f221234 2 months ago 13.3kB
vue-docker-demo-image latest 4835e3101950 8 months ago 135MB
[root@sa ~]#
docker rmi 删除镜像
# docker rmi 容器id -f 强制删除(运行中的)
# docker rmi 容器id 容器id 容器id 删除多个容器
[root@sa ~]# docker rmi d11
Untagged: hello-world:latest
Untagged: hello-world@sha256:5122f6204b6a3596e048758cabba3c46b1c937a46b5be6225b835d091b90e46c
Deleted: sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726
Deleted: sha256:f22b99068db93900abe17f7f5e09ec775c2826ecfe9db961fea68293744144bd
[root@sa ~]# # 批量删除
# -f 强制删除,-aq 仅显示所有的镜像id
[root@sa ~]# docker rmi -f $(docker images -aq)
容器命令
有了镜像才可以创建容器
以centOS镜像为例:
docker pull centos
新建容器并启动
docekr run [可选参数] image# 参数说明
--name="name" # 容器名称 name01 name02,用来区分容器
-d # 后台方式运行
-it # 使用交互方式运行,进入容器查看内容
-p # 指定容器的端口-p ip:主机端口:容器端口 映射-p 主机端口:容器端口(常用)-p 容器端口-P(大写p) # 随机指定端口# 启动并进入容器,你会发现,现在的用户名就是容器的id
[root@sa ~]#
[root@sa ~]# docker run -it centos /bin/bash
[root@74457211455c /]#
[root@74457211455c /]#
# 退出就是exit
...
列出所有运行的容器
docker ps # 列出正在运行的容器-a # 列出所有容器,包含已经停止的-n=? # 列出最近创建的容器,带数字-q # 只列出容器的id# 列出所有容器,包含已经停止的
[root@sa ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
74457211455c centos "/bin/bash" 2 minutes ago Up 2 minutes happy_wilson
退出容器
exit # 直接停止容器并退出
Ctrl +p +q # 容器不停止退出
删除容器
docker rm # 容器id-f # 强制删除正在运行的容器docker rm -f $(docker ps -aq) # 删除所有的容器
docker ps -aq |xargs docker rm # 也可以删除所有容器
启动容器
docker start 容器id # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止当前正在运行的容器
docker kill 容器id # 轻质停止当前容器
其他常用命令
后台启动
# 命令 docker run -d 镜像名
[root@sa ~]# docker run -d centos
26e18764513a7ebf03f73dda143d5b1bb73054cbf74bae10d8f22ba16d4c4ea9
[root@sa ~]#
# 会产生的问题:ps发现centos 直接停了
# 原因:docke容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
查看日志
docker logs -tf --tail# 显示日志
-tf # 显示日志 -t时间戳 -f Follow log output(不断输出日志)
--tail number # 要显示的日志条数,后接数字
查看容器中的进程信息
# 命令 docker top 容器id
[root@sa ~]# docker top 8b
UID PID PPID C STIME TTY TIME CMD
root 21913 21895 0 04:28 pts/0 00:00:00 /bin/bash
[root@sa ~]#
查看镜像的元数据
# 命令 docker inspect 容器id[root@sa ~]# docker inspect 8b
[{"Id": "8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05","Created": "2021-05-18T20:28:55.704722287Z","Path": "/bin/bash","Args": [],"State": {"Status": "running","Running": true,"Paused": false,"Restarting": false,"OOMKilled": false,"Dead": false,"Pid": 21913,"ExitCode": 0,"Error": "","StartedAt": "2021-05-18T20:28:56.270071675Z","FinishedAt": "0001-01-01T00:00:00Z"},"Image": "sha256:300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55","ResolvConfPath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/resolv.conf","HostnamePath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/hostname","HostsPath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/hosts","LogPath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05-json.log","Name": "/interesting_black","RestartCount": 0,"Driver": "overlay2","Platform": "linux","MountLabel": "","ProcessLabel": "","AppArmorProfile": "","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/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95-init/diff:/var/lib/docker/overlay2/85c0301341e0be53f9854cc3148968bb232564ca6cac942efebc0b9328ec192c/diff","MergedDir": "/var/lib/docker/overlay2/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95/merged","UpperDir": "/var/lib/docker/overlay2/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95/diff","WorkDir": "/var/lib/docker/overlay2/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95/work"},"Name": "overlay2"},"Mounts": [],"Config": {"Hostname": "8bdbd34f37a6","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/bash"],"Image": "centos","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {"org.label-schema.build-date": "20201204","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": "f90c37e119e8231aee5aed42652c64393959cd87f061092347b98b21a5764f8e","HairpinMode": false,"LinkLocalIPv6Address": "","LinkLocalIPv6PrefixLen": 0,"Ports": {},"SandboxKey": "/var/run/docker/netns/f90c37e119e8","SecondaryIPAddresses": null,"SecondaryIPv6Addresses": null,"EndpointID": "d3f5369c06fc9c93af1b5bfbc45617ee24b7900e88a33ac66793b80712912f88","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": "d29efb6707572fecd8ccaadfb2f011525f39fa0a9fb0a7732be8af61baf34003","EndpointID": "d3f5369c06fc9c93af1b5bfbc45617ee24b7900e88a33ac66793b80712912f88","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}}}}
]
[root@sa ~]#
进入正在运行的容器
# 进入正在运行的容器# 方式一:
docker exec -it 容器id bashShell# 方式二:
docker attch 容器id# docker exec 进入容器后开启一个新的终端
# docker attch 进入容器正在执行的终端,不会开启新的进程
容器和主机之间的拷贝
# 从容器内拷贝文件到主机
docker cp 容器id:/路径 /主机路径 # 建议使用绝对路径# 进入容器
[root@sa ~]# docker exec -it f2 /bin/sh
sh-4.4#
sh-4.4# touch 123.txt
sh-4.4# ls
123.txt ks-script-esd4my7v ks-script-eusq_sc5
sh-4.4# exit
[root@sa ~]# # 将容器内的文件拷贝至主机
[root@sa ~]# docker cp f2:/tmp/123.txt ./
[root@sa ~]#
[root@sa ~]# docker ps # 说明只要容器还在,启不启动数据都在
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@sa ~]#
[root@sa ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f24dcc07e0e5 centos "/bin/sh -c 'while t…" 15 ago Exited vigilant_hypatia
[root@sa ~]#
Docker 镜像讲解
镜像是什么
镜像是一种轻量级、可执行的独立软件包(类似于免安装),用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、运行是所需的库、环境变量及配置文件等等。
Docker镜像加载原理
UnionFS(联合文件系统)
UnionFS:Union文件系统是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂在到同一个虚拟文件系统(unite several directories into a single virtual filesystem)下。Union文件系统时Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:以此同时加载多个文件系统,单对外表现,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
Docker的镜像实际上有一层一层的文件系统组成,这种层级的文件系统叫联合文件系统
Bootfs(boot file system)主要包含bootloader 和kernel,BootLoader主要是引导加载kerner,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们经典的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev, /proc, /etc等标准目录文件。rootfs就是各种不同的操作系统发行版,例如:Ubuntu、CentOS等。
平时安装的虚拟机CentOS都是好几个G,但是Docker这里的才几百兆,为什么?
因为Docker镜像一般都是非常精简的,不要忘了,他用的宿主机的内核,自己只需要提供rootfs就可以了。所以镜像只需提供最基本的命令、工具和程序即可。由此,对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以共用bootfs。
分层理解
分层的镜像
在下载镜像时,可以注意到下载日志输出的是一层层下载的
分层的好处:
最大的好处莫过于资源共享!例如,有多个镜像都是相同的Base镜像构建而来,那么宿主机只需要在磁盘上保留一份Base镜像,同时,内存中也只需要加载一份Base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过 docker inspect 命令查看
理解:
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上创建一个新的镜像层。
例如:基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层,如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层,如果继续添加,则依次往上新建。
(这里其实忽略了一个重要的概念,那就是层,怎样的添加才能构成所谓的 “层")
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层。
分层理解2(理论更清晰)
比较好的理解:
Docker的镜像分层
接下来,我们来看看 Docker的镜像分层机制。
Docker镜像是分层构建的,Dockerfile 中每条指令都会新建一层。例如以下 Dockerfile:
FROM ubuntu:18.04 COPY . /app RUN make /app CMD python /app/app.py
以上四条指令会创建四层,分别对应基础镜像、复制文件、编译文件以及入口文件,每层只记录本层所做的更改,而这些层都是只读层。当你启动一个容器,Docker 会在最顶部添加读写层,你在容器内做的所有更改,如写日志、修改、删除文件等,都保存到了读写层内,一般称该层为容器层,如下图所示:
事实上,容器(container)和镜像(image)的最主要区别就是容器加上了顶层的读写层。所有对容器的修改都发生在此层,镜像并不会被修改,也即前面说的 COW(copy-on-write)技术。容器需要读取某个文件时,直接从底部只读层去读即可,而如果需要修改某文件,则将该文件拷贝到顶部读写层进行修改,只读层保持不变。
每个容器都有自己的读写层,因此多个容器可以使用同一个镜像,另外容器被删除时,其对应的读写层也会被删除(如果你希望多个容器共享或者持久化数据,可以使用 Docker volume)。
最后,执行命令 docker ps -s,可以看到最后有两列 size 和 virtual size。其中 size就是容器读写层占用的磁盘空间,而 virtual size 就是读写层加上对应只读层所占用的磁盘空间。如果两个容器是从同一个镜像创建,那么只读层就是 100%共享,即使不是从同一镜像创建,其镜像仍然可能共享部分只读层(如一个镜像是基于另一个创建)。因此,docker 实际占用的磁盘空间远远小于 virtual size 的总和。
以上就是 Docker 镜像分层的主要内容,至于这些层的交互、管理就需要存储驱动程序,也即联合文件系统(UnionFS)。Docker 可使用多种驱动,如目前已经合并入 Linux 内核、官方推荐的overlay, 曾在 Ubuntu、Debian等发行版中得到广泛使用的 AUFS,以及devicemapper、zfs等等,需要根据 Docker以及宿主机系统的版本,进行合适的选择。
commit 镜像
docker commit 提交容器成为一个新的副本(镜像)# 命令和git类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
容器数据卷
什么是容器数据卷
数据如果只保存在容器中,那么容器删除,则数据丢失。
有没有什么方式可以将容器中的数据同步或者说映射至本地/宿主机的长期保存所想要的数据。
容器之间也可以共享数据。
于是,有了卷技术。简单理解就是目录的挂载,将容器内的目录挂载到宿主机上,并且容器间也是可以数据共享的。
使用数据卷
方式一: 直接使用命令挂载 -v
# docker run -it -v 宿主机目录:容器内目录,宿主机目录可以不存在,会自动创建,最好使用绝对路径-it # 交互式启动-v # 卷volume 挂载[root@sa ~]# docker run -it -v ~/mont_volume:/home centos /bin/bash# 启动之后可根据 docker inspect 容器id 查看挂载结果
[root@sa ~]# docker inspect 0601e8ee0ec6
# 挂载以后基本上就理解成容器内核宿主机共用这个目录。
# 测试可发现,当容器停止后,在宿主机向mont_volume目录写入文件,然后重新启动容器,容器home目录可直接看到写入文件
# 即使,容器被删除,然后重新起一个容器,挂载宿主机目录还是mont_volume,那么,新容器也是可以直接看到写入文件。这也说明了,容器删除不会影响挂载的目录
# 总结,可用该方法将配置类的文件挂载出来,当我们需要修改容器配置时,无需进入容器即可修改
安装MySQL
# 1、获取镜像
[root@sa ~]# docker pull mysql# 2、安装mysql一定要注意配置密码
# Starting a MySQL instance is simple:
#
[root@sa ~] # docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# ... where some-mysql is the name you want to assign to your container, my-secret-pw # is the password to be set for the MySQL root user and tag is the tag specifying the # MySQL version you want. See the list above for relevant tags.
# from https://hub.docker.com/_/mysql# 3、启动镜像
[root@sa ~]# docker run -d -p 3310:3306 -v /home/myspl/conf:/etc/mysql/conf.d -v /home/mysql/date:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql ac4ca43eb68bc7bfadbd6da4cef7fa81f83d79eb10b9639cf8b7cbc016e50c9a-d 后台运行-p 端口映射-v 卷挂载-e 环境配置--name 容器名字(容器名字mysql01,而mysql是镜像名)
[root@sa ~]#
具名和匿名挂载
# 匿名挂载
-v 容器内目录
[root@sa ~]# docker run -d -P --name nginx -v /etc/nginx nginx# 查看所有的 volume 的情况
[root@sa ~]# docker volume ls
DRIVER VOLUME NAME
local ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27
[root@sa ~]## VOLIME NAME 实际上是一些具体的目录# 现在为了对比,起一个具名的挂在容器
[root@sa ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
42ca9c1cd1447c2d6f79caa9c9197cd7083a597ce01abec60c0109018cc69b06
[root@sa ~]#
[root@sa ~]# docker volume ls
DRIVER VOLUME NAME
local ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27
local juming-nginx# 通过 -v 卷名:容器内路径
# 查看这个卷
[root@sa ~]# docker volume inspect ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27
[{"CreatedAt": "2021-05-29T23:21:35+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27/_data","Name": "ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27","Options": null,"Scope": "local"}
]
[root@sa ~]#
[root@sa ~]# docker volume inspect juming-nginx
[{"CreatedAt": "2021-06-26T21:55:36+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data","Name": "juming-nginx","Options": null,"Scope": "local"}
]
[root@sa ~]#
[root@sa juming-nginx]# cd _data/
[root@sa _data]# ls
conf.d fastcgi_params koi-utf koi-win mime.types modules nginx.conf scgi_params uwsgi_params win-utf
总结:
所有的Docker容器的卷,在没有指定挂在目录的情况下都在 /var/lib/docker/volumes/xxxxx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况下使用 具名挂载
# 如何确定是具名还是匿名挂载
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v 宿主机路径:容器内路径 # 指定路径挂载
拓展:(下面所说的权限都是针对容器而言,也就是如果在容器上是只读的,但,在宿主机上是可以进行写操作的)
# 通过 -v 容器内路径 :ro :rw 来改变读写权限
# ro 只读
# rw 可读可写# 凡是设置了这个容器权限,容器对挂载的内容的权限就被限定了
[root@sa ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
[root@sa ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx# 上面所说的权限都是针对容器而言,也就是如果在容器上是只读的,但是可以在宿主机上进行写操作(当然,宿主机本身对该路径可写)
数据卷之DockerFile
Dockerfile是用来构建docker镜像的构建文件。命令脚本。
通过这个脚本可以生成镜像,镜像是一层一层的,脚本时一个个的命令,每个命令就是一层
# 创建一个dockerfile文件,名字可以任意,最好还是dockerfile
# 一般格式:指令(全大写) 参数
# 文件内容如下:
[root@sa Dockerfile-test]# cat dockerfile1 # 以下的每个指令就是镜像的一层
FROM centos # 将centos作为基础镜像 VOLUME ["volume01", "volume02"] # 挂载数据卷CMD echo "----is wax ---- first --- Dockerfile----"
CMD /bin/bash[root@sa Dockerfile-test]# # 查看镜像生成过程
[root@sa Dockerfile-test]# docker build -f dockerfile1 -t wax-centos:0.1 .-f 指定dockerfile文件-t tag 目标文件一定不要忘了生成的文件存放路径—— 末尾的那个 .(当前目录)Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos---> 300e315adb2f
Step 2/4 : VOLUME ["volume01", "volume02"]---> Running in 669152823e0d
Removing intermediate container 669152823e0d---> 5e192b1bef6d
Step 3/4 : CMD echo "----is wax ---- first --- Dockerfile----"---> Running in b65e28cc25db
Removing intermediate container b65e28cc25db---> cf5be1a83272
Step 4/4 : CMD /bin/bash---> Running in 8cb48405b199
Removing intermediate container 8cb48405b199---> 09081e56f27e
Successfully built 09081e56f27e
Successfully tagged wax-centos:0.1
[root@sa Dockerfile-test]# # 查看镜像
[root@sa Dockerfile-test]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wax-centos 0.1 09081e56f27e 5 minutes ago 209MB
redis latest fad0ee7e917a 3 weeks ago 105MB# 启动镜像
[root@sa Dockerfile-test]# docker run -it 09081e56f27e /bin/bash
[root@93d0f80f1c21 /]#
[root@93d0f80f1c21 /]# 可以看到已经成功进入
[root@93d0f80f1c21 /]# ls -l
total 0
...... # 篇幅问题省略
drwxr-xr-x. 2 root root 6 Nov 3 2020 srv
dr-xr-xr-x. 13 root root 0 May 15 10:53 sys
drwxrwxrwt. 7 root root 145 Dec 4 2020 tmp
drwxr-xr-x. 12 root root 144 Dec 4 2020 usr
drwxr-xr-x. 20 root root 262 Dec 4 2020 var
drwxr-xr-x. 2 root root 6 Jun 28 11:13 volume01 # 两个volume** 目录就是构建镜像时,
drwxr-xr-x. 2 root root 6 Jun 28 11:13 volume02 # 自动挂载的数据卷目录# 可以看到两个volume实际上是匿名挂载,那么,查看其挂载的具体位置
[root@sa ~]# docker inspect 93d0f80f1c21
....."Mounts": [{"Type": "volume","Name": "c716f0d4f9*****bddf","Source": "/var/lib/docker/volumes/c716f0d4f9*****bddf/_data","Destination": "volume01","Driver": "local","Mode": "","RW": true,"Propagation": ""},{"Type": "volume","Name": "14e0ac65b55b5******d156e","Source": "/var/lib/docker/volumes/14e0ac65b55b5*****d156e/_data","Destination": "volume02","Driver": "local","Mode": "","RW": true,"Propagation": ""}],
......
实际上,这种方式为十分常用,因为我们的目的大多需要自己构建镜像。
假如,构建镜像没有自动挂载卷,那就要自己手动挂载了, -v 卷名:容器内路径 (手动当然 具名挂载 啦)
数据卷容器
作用:实现多个容器间的数据同步,共享。
命令: - -volumes - from container_name
一般把被挂载的容器也就是父容器 称之为数据卷容器
# 启动数据卷容器
[root@sa ~]# docker run -it --name docker-father wax-centos:0.1 /bin/bash# 启动要和父容器共享的容器(为了简单,使用同一个镜像)
[root@sa ~]# docker run -it --name docker-child01 --volumes-from docker-father wax-centos:0.1 /bin/bash# 在启动2个容器
[root@sa ~]# docker run -it --name docker-child02 --volumes-from docker-father wax-centos:0.1[root@sa ~]# docker run -it --name docker-child03 --volumes-from docker-child02 centos# 查看父容器 (123. 456是03创建的)
[root@8eb24755d5fd volume01]# ls
123.txt 456.txt child01-create child02-create# 查看03容器
[root@sa ~]# docker attach docker-child03
[root@f2eb5b35ae88 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
[root@f2eb5b35ae88 /]#
直接给结论:
当我们删除任何一个容器时,其他容器的挂载目录不受影响。
可以看到,child03的镜像是基础的centos镜像,挂载到02上的,其自身并没有挂载volume01 、volume02,但是,使用了
–volumes-from 指令之后依然会挂载这两个目录。
另外,虽然03容器挂载的是02容器,但是停止并删除了02容器后,再在03中的volume01目录下创建文件在父容器、01中依然可以看到。另外,再把父容器删除,03中volume*目录下创建的文件,在01中还是可以看到。所以应该不是简单的拷贝。(原理我也不知奥)
另外,通过inspect可以看到,4个容器的挂载目录都是同一个地方,是不是突然明白了?也就是,即使,你把所有容器都删了,数据应该是还在的,都在宿主机上,需要注意的是,删除容器不影响数据(文件),但是在容器内删除文件是同步的,包括会同步到宿主机。其实这个比较好理解。
再另外,在宿主机上,删除宿主机上的被挂载目录,比如 /var/lib/docker/volumes/***/_data 的_data目录,那么已经启动的容器将无法在其挂载目录下进行操作——无法新建文件。就算手动将_data目录再创建出来,依然不能新建文件。需要停止后重启容器才可以新建。
Dockerfile
Dockerfile 介绍
Dockerfile 是用来构建docker镜像的文件,就是参数脚本!
构建步骤:
- 编写一个dockefile文件
- docker build 构建成一个镜像
- docker run 就可以运行镜像
- docker push 可以发布镜像
Dockerfile 构建过程
- 每个保留关键字(指令)都是大写
- 从上至下依次执行
- 使用 ‘#’ 注释
- 每一个指令都会创建提交一层新的镜像层
Dockerfile是面向开发的!
Dockerfile:构建文件,定义了所有步骤,源代码
Dockerimages:通过Dockerfile构建的镜像,做种发布和运行
Docker容器:容器就是镜像运行起来提供的服务
Dockerfile的指令
FROM # 基础镜像,一切从这里构建
MAINTAINER # 镜像的作者、维护者
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤:Ubuntu镜像,可以给他加vim配置文件啥的
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 保留端口配置
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承 Dockerfile,这个时候就会运行 ONBUILD 的指令
COPY # 类似ADD,将文件拷贝至镜像中
ENV # 构建的时候设置环境变量
实例
以ubuntu基础镜像为例
# 先通过 docker pull ubuntu 下载一个官方的最新的ubuntu基础镜像
#这里可以先尝试一下,刚下载的镜像是不支持vim 的,等下通过dockerFile制作一个带有vim的镜像#下面开始写dockerFile
root@wu:~/my_dockerfile# cat Dockerfile
FROM ubuntuMAINTAINER wax<1123@qq.com>ENV MYPATH /usr/local
WORKDIR $MYPATHRUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y net-toolsEXPOSE 80CMD echo $MYPATHCMD echo "------end!-------"
CMD /bin/bash
这里需要注意一点,如果是在centos系统上安装的docker,那么大概率执行以上dockerFile将会是失败的。据说是,容器使用的资源库的源是来自宿主机的,也就是centos上不能执行atp-get,而是 yum install命令
接着,我们就来构建自己的镜像(删除了安装vim过程中的打印)
# dockerFile的构建命令 可以通过 docker build --help查看# -f 指定dockerFile文件 -t 指定目标镜像root@wu:~/my_dockerfile# docker build -f Dockerfile -t my_ubuntu:1.0 ./
Sending build context to Docker daemon 2.048kB
Step 1/11 : FROM ubuntu---> ff0fea8310f3
Step 2/11 : MAINTAINER wax<1123@qq.com>---> Using cache---> 350bceb3effd
Step 3/11 : ENV MYPATH /usr/local---> Using cache---> 792e5469839a
Step 4/11 : WORKDIR $MYPATH---> Using cache---> 1b2933c2de8f
Step 5/11 : RUN apt-get update---> Using cache---> cee70ef91575
Step 6/11 : RUN apt-get install -y vim---> Using cache---> 628163e35047
Step 7/11 : RUN apt-get install -y net-tools---> Using cache---> 66ceeb0d5a36
Step 8/11 : EXPOSE 80---> Using cache---> 1a3c9ff3333b
Step 9/11 : CMD echo $MYPATH---> Using cache---> b9007091be62
Step 10/11 : CMD echo "------end!--------"---> Using cache---> 1c85c73c79fe
Step 11/11 : CMD /bin/bash---> Using cache---> 66c8af1a3f90
Successfully built 66c8af1a3f90
Successfully tagged my_ubuntu:1.0
root@wu:~/my_dockerfile#
完了之后就可以运行my_ubuntu镜像,并且可以直接执行vim 以及 ifconfg等命令。这里就不做演示了。