Docker学习笔记02---镜像与容器
Docker学习笔记02
狂神B站视频链接:https://www.bilibili.com/video/BV1og4y1q7M4
Docker镜像讲解
镜像是什么
镜像是一种轻量级,可执行的独立软件,用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、运行环境、库、环境变量和配置文件。
所有的应用,直接打包docker镜像,就可以直接跑起来!
如何得到镜像
-
-
朋友拷贝给你
-
自己制作一个镜像DockerFile
Docker镜像加载原理
UnionFS
我们下载的时候看到的一层层就是这个!
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层,轻量级并且靠性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同的目录挂载到同一个虚拟文件系统下(unite several dirctories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有夫镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有的底层文件和目录。
Docker镜像加载原理
docker的镜像实际上又一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs,这一层与我们典型的Linux/unix系统是一样的,包含boot加载器和内核,当boot加载完成之后整个内核就都是在内存中了,此时内存的使用全以由bootfs转交给内核,此时系统也会加载bootfs。
rootfs(root file system),在bootfs之上,包含的就是典型Linux系统中的/div,/proc,/bin,/etc等标准目录和文件。rootfs各种不同的操作系统发行版,比如Ubuntu,Centos等等。
平时我们安装进虚拟机的CentOS都是好几个G,为什么Docker这里才200M?
对于一个精简的OS,rootfs可以很小,只需要包含最近本的命令,工具和程序库就可以了,因为底层直接用Host的Kernel,自己只需要提供rootfs就可以了,由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差异,因此不同的发行版就可以公用bootfs。
虚拟机是分钟级别的,容器是秒级的
分层理解
分层的镜像
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载
思考:为什么Docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于是资源共享了,比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需要在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就就可以为所有的容器服务了,而且镜像的每一层都可以被共享.
查看镜像的分层的方式可以通过 docker image inspect 命令
docker image inspect redis:latest
理解:
所有的Docker镜像都起始与一个基础镜像,当进行修改或者增加新的内容的时候,就会在当前镜像层之上,创建新的镜像层
举一个简单的例子,假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是镜像的第一层;如果在该镜像中添加python包,该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)
在添加额外的镜像层的同时,镜像始终保持的是当前所有的镜像组合,理解这一点非常重要,下图中举例一个简单的例子,每一个镜像层包含了3个文件,而镜像包含了来自两个镜像层的6个文件.
上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件按,这里因为最上层的文件7是文件5的一个更新版本.
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件.这样就是使得文件的更新版本作为一个新镜像层添加到镜像当中.
Docker通过存储引擎,(新版本采用快照机制)的方式来实现镜像层栈,并保证多镜像层对外展示为统一的文件系统
linux上可以用的存储引擎(新版本采用快照机制)的方式来实现镜像层的堆栈,并保证多镜像层对展示为统一的文件系统.
Linux上可用的存储引擎有AUFS.Oevice Mapper,Btrfs以及ZFS.顾名思义,每种存储引擎都是基于Linux中对应的文件系统或者块设备技术,并且每一种存储引擎都有其独有的性能特点.
Docker在windows上支持Windowsfilter 一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和Cow.
下图展示了与系统显示相同的三层镜像,所有镜像层堆叠并合并,对外提供统一的视图.
特点
Docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶层 !
这一层就是我们通常所说的容器层, 容器之下的都叫镜像层 !
如何提交一个自己的镜像
commit镜像
docker commit 提交容器成为一个新的副本 #命令和git原理类似 docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
实战测试
#1.启动一个默认的tomcat [root@localhost ~]# systemctl start docker [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest d1165f221234 13 days ago 13.3kB tomcat latest 040bdb29ab37 2 months ago 649MB nginx latest f6d0b4767a6c 2 months ago 133MB centos latest 300e315adb2f 3 months ago 209MB [root@localhost ~]# docker run -it -p 8080:8080 tomcat #前端界面 Using CATALINA_BASE: /usr/local/tomcat Using CATALINA_HOME: /usr/local/tomcat Using CATALINA_TMPDIR: /usr/local/tomcat/temp Using JRE_HOME: /usr/local/openjdk-11 Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar Using CATALINA_OPTS: #另启一个终端 [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 264bb068855e tomcat "catalina.sh run" 9 seconds ago Up 6 seconds 0.0.0.0:8080->8080/tcp quizzical_panini [root@localhost ~]# docker exec -it 264bb068855e /bin/bash #2.发现这个默认的tomcat,是没有webapps应用,镜像的原因,官方的镜像默认 webapps下面是没有文件的! #3.需要自己拷贝基本的文件 root@264bb068855e:/usr/local/tomcat# cp -r webapps.dist/* webapps root@264bb068855e:/usr/local/tomcat# cd webapps root@264bb068855e:/usr/local/tomcat/webapps# ls ROOT docs examples host-manager manager #4.将我们操作过的容器通过commit提交为一个镜像!我们以后就使用我们修改过的镜像即可,这就是我们自己的一个修改的镜像 [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0e8091b58b59 tomcat "catalina.sh run" 7 seconds ago Up 2 seconds 0.0.0.0:8080->8080/tcp clever_haslett [root@localhost ~]# docker commit -a="caichuanqi" -m="add webapps app" 0e8091b58b59 tomcat01:1.0 sha256:cdc47d2d86e79690c86d9699a98379fddf9dae2dce4daaaa5dfc8606bf7fec2d [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE tomcat01 1.0 cdc47d2d86e7 9 seconds ago 649MB hello-world latest d1165f221234 13 days ago 13.3kB tomcat latest 040bdb29ab37 2 months ago 649MB nginx latest f6d0b4767a6c 2 months ago 133MB centos latest 300e315adb2f 3 months ago 209MB
学习方式说明:理解概念,但是一定要实践,最后实践和理论相结合一次搞定这个知识
如果你想要保存当前容器的状态,就可以通过commit 来提交,获得一个镜像,就好比我们以前学习VM时候得快照;
容器数据卷
什么是容器数据卷
docker 的理念回顾
将应用和环境打包成一个镜像!
数据?如果数据都在容器中,那么我们的容器删除,数据就会丢失!需求:数据可以持久化
Mysql,容器删了,删库跑路!需求:MySQL数据可以存储在本地
容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!
这就是卷技术! 目录的挂载,将我们的容器内的目录,挂载在Linux上面!
同步机制!
总结:容器的持久化和同步操作,容器之间也可以数据共享!
使用数据卷
方式1:直接使用命令进行挂载 -v
docker run -it -v #测试 [root@localhost home]# docker run -it -v /home/ceshi:/home centos /bin/bash Unable to find image 'centos:latest' locally latest: Pulling from library/centos 7a0437f04f83: Pull complete Digest: sha256:5528e8b1b1719d34604c87e11dcd1c0a20bedf46e83b5632cdeac91b8c04efc1 Status: Downloaded newer image for centos:latest [root@ec7929b6f99c /]# #新建一个窗口查看 是否挂载成功 [root@localhost ceshi]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ec7929b6f99c centos "/bin/bash" 4 minutes ago Up 4 minutes kind_kepler 9553c6bad58e tomcat "catalina.sh run" 37 minutes ago Up 37 minutes 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp pensive_bose [root@localhost ceshi]# docker inspect ec7929b6f99c #启动后可以通过 docker inspect 容器id 查看挂载的详细信息
挂载之后,在本地/home/ceshi中创建的数据会同步到/home中!
systemctl start docker #1.停止容器 exit #2.宿主机上修改文件 #3.启动容器 [root@localhost ceshi]# docker ps -a ec7929b6f99c centos "/bin/bash" 18 minutes ago [root@localhost ceshi]# docker start ec7929b6f99c ec7929b6f99c [root@localhost ceshi]# docker attach ec7929b6f99c #4.容器内的数据依旧是同步的
好处:我们以后修改只需要在本地修改即可,容器内会自动同步!
实战:安装MySQL
思考:MySQL的数据持久化的问题!
#查询镜像 [root@localhost ceshi]# docker search mysql #获取镜像 [root@localhost ceshi]# docker pull mysql:5.7 [root@localhost ceshi]# docker images mysql 5.7 2c9028880e58 4 weeks ago 447MB #运行容器,需要做数据挂载! #安装启动mysql,需要配置密码,这点要注意 #官方测试:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag #启动我们的mysql -d 后台运行 -p 端口映射 -v 卷挂载 -e 环境配置 --name 容器的名字 [root@localhost ceshi]# docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 #启动成功之后,我们在本地使用 Navicat 来连接测试一下 #Navicat-连接到服务器3310 --- 3310 和容器内的3306 映射,这个时候我们就可以连接上了! #在本地创建一个数据库,查看一下我们的映射的路径是否ok! [root@localhost data]# docker rm -f mysql01 mysql01 [root@localhost data]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@localhost data]# docker ps -a #删除后本地数据还存在
具名和匿名挂载
#匿名挂载 -v 容器内的路径! [root@localhost data]# docker run -d -P --name nginx03 -v /etc/nginx nginx [root@localhost data]# docker volume --help Usage: docker volume COMMAND Manage volumes 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的情况 [root@localhost data]# docker volume ls DRIVER VOLUME NAME local 6c604de0883f7b2f4f0ae288febd00365910531248c9428e2f1c956a997bf0f7 #这里发现,这种就是匿名挂载,我们在 -v 只写了容器的路径,没有写容器外的路径! #具名挂载 [root@localhost data]# docker run -d -P --name nginx04 -v juming-nginx:/etc/nginx nginx e5ddcd7ac0df59dde870675e5bc70df226b56fc135d2eaa22f8b984d7fcd61ff [root@localhost data]# [root@localhost data]# docker volume ls DRIVER VOLUME NAME local 6c604de0883f7b2f4f0ae288febd00365910531248c9428e2f1c956a997bf0f7 local juming-nginx #通过 -v 卷名:容器内路径 #查看一下这个卷 [root@localhost data]# docker volume inspect juming-nginx [ { "CreatedAt": "2021-06-15T16:17:41+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data", "Name": "juming-nginx", "Options": null, "Scope": "local" } ]
所有的docker容器内的卷,没有指定目录的情况下都是在/var/lib/docker/volumes/xxx/_data
中!
[root@localhost /]# cd var/lib/docker/ [root@localhost docker]# ls buildkit containers devicemapper image network plugins runtimes swarm tmp trust volumes [root@localhost docker]# cd volumes/ [root@localhost volumes]# ls 6c604de0883f7b2f4f0ae288febd00365910531248c9428e2f1c956a997bf0f7 backingFsBlockDev juming-nginx metadata.db [root@localhost volumes]# cd juming-nginx/ [root@localhost juming-nginx]# ls _data [root@localhost juming-nginx]# cd _data/ [root@localhost _data]# ls conf.d fastcgi_params koi-utf koi-win mime.types modules nginx.conf scgi_params uwsgi_params win-utf [root@localhost _data]# pwd /var/lib/docker/volumes/juming-nginx/_data
我们通过具名挂载可以方便的找到我们的一个卷,大多数情况是用的是具名挂载。
#如何确定是具名挂载还是匿名挂载,还是指定路径挂载! -v 容器内路径 #匿名挂载 -v 卷名:容器内路径 #具名挂载 -v /宿主主机路径::容器内路径 #指定路径挂载
拓展:
# 通过 -v 容器内路径:ro rw 改变读写权限 ro readonly #只读 rw readerite #可读可写 #一旦设置了容器的权限,容器就对我们挂载出来的内容就有了限定! docker run -d -P --name nginx04 -v juming-nginx:/etc/nginx:ro nginx docker run -d -P --name nginx04 -v juming-nginx:/etc/nginx:rw nginx #ro 只要看到了ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作的
初识Dockerfile
Dockerfile 就是来构建docker镜像的构建文件!命令脚本!
通过这个脚本就可以生成镜像,镜像是一层一层的,脚本也是一个个的命令,没一个命令代表一层!
#创建一个测试目录; #创建一个dockerfile文件,名字可以随机,建议Dockerfile #文件中的内容 指令(大写) 参数 [root@localhost /]# cd /home/ [root@localhost home]# ls ccq ceshi mysql [root@localhost home]# mkdir docker-test-volume [root@localhost home]# cd docker-test-volume/ [root@localhost docker-test-volume]# ls [root@localhost docker-test-volume]# vim dockerfile [root@localhost docker-test-volume]# cat dockerfile FROM centos VOLUME ["volume01","volume02"] CMD echo "---end---" CMD /bin/bash #生成一个镜像 [root@localhost docker-test-volume]# docker build -f dockerfile -t caichuanqi/centos:1.0 . Sending build context to Docker daemon 2.048kB Step 1/4 : FROM centos ---> 300e315adb2f Step 2/4 : VOLUME ["volume01","volume02"] ---> Running in 83dafc72d7a7 Removing intermediate container 83dafc72d7a7 ---> c0ce64994f32 Step 3/4 : CMD echo "---end---" ---> Running in d329d3ce9040 Removing intermediate container d329d3ce9040 ---> c42231ffef96 Step 4/4 : CMD /bin/bash ---> Running in 0acc76596fe0 Removing intermediate container 0acc76596fe0 ---> a5591a0e6753 Successfully built a5591a0e6753 Successfully tagged caichuanqi/centos:1.0 [root@localhost docker-test-volume]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE caichuanqi/centos 1.0 a5591a0e6753 28 seconds ago 209MB tomcat02 2.0 7cd74f6ab736 3 hours ago 672MB #这里的每一个命令,就是镜像的一层
#启动自己写的容器,发现有数据卷目录
这个卷和外部一定有一个同步的目录!
查看一下卷的挂载路径
[root@4f6c8eab4f0a /]# cd volume01 [root@4f6c8eab4f0a volume01]# touch 123.txt #另起一个终端 [root@localhost /]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4f6c8eab4f0a a5591a0e6753 "/bin/bash" 12 minutes ago [root@localhost /]# docker inspect 4f6c8eab4f0a [root@localhost /]# cd /var/lib/docker/volumes/3f6c1d092f5cc14f7dfba38a244b6a40b82b6424ab9e686f4bb10aa28c26d647/_data [root@localhost _data]# ls 123.ttx 123.txt
测试一下刚才的文件是否同步出去了!
这种方式是我们未来使用最多的,因为我们会通常构建自己镜像!
假设构建镜像的时候没有挂载卷,就要手动的镜像挂载 -v 卷名 :容器内路径!
数据卷容器
多个mysql同步数据!
#测试,可以删除docker01,查看docker02和docker03是否还存在
#测试后依旧可以访问
经常用在:多个mysql进行数据共享!
[root@localhost ceshi]# docker run -d -p 3310:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7 [root@localhost ceshi]# docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-form mysql01 mysql:5.7 #这个时候,可以实现两个容器之间数据同步!
结论:
容器之间配置信息的传递,数据卷容器的生命周期一直持续到 没有容器使用为止。
但是一旦你持久化到本地,这个时候,本地的数据是不会被删除的!
小结
#停止docker systemctl stop docker #docker0 ip link set dev docker0 down #删除docker0网桥 brctl delbr docker0 #增加docker0 网桥 brctl addbr docker0 #增加网卡 ip addr add 172.16.10.1/24 dev docker0 #启用网卡 ip link set dev docker0 up #重启docker服务 systemctl restart docker systemctl stop docker&& \ systemctl stop docker&& \ ip link set dev docker0 down && \ brctl delbr docker0 && \ brctl addbr docker0 && \ ip addr add 172.16.10.1/24 dev docker0 && \ ip link set dev docker0 up && \ systemctl restart docker 1)首先需要停止所有的容器 docker stop $(docker ps -a -q) 2)删除所有的容器(只删除单个时把后面的变量改为image id即可) docker rm $(docker ps -a -q) 3)删除全部的images docker rmi $(docker images -q) 4)web题目 docker load < web_awd.tar docker run -d -p 80:80 -p 10021:22 awd 5)pwn题目 docker load < pwn_deploy_chroot_latest.tar docker run -d -p 10000:10000 -p 10022:22 6)进入pwn docker exec -it 264bb068855e /bin/bash systemctl start docker apt-get install net-tools netstat -ntlp|grep 22 sudo apt-get install openssh-server service ssh start sudo apt-get install ufw sudo ufw enable sudo ufw allow 22 1.启动镜像并做出修改 docker run -it centos /bin/bash [root@afcaf46e8305 /]# 注意afcaf46e8305是产生的容器ID,前面运行的时候不要-d后台运行了,不然无法进入容器交互执行模式: 安装vim并且退出容器: yum install -y vim exit 2.把容器打包成镜像 docker commit afcaf46e8305 centos-vim 3.查看镜像centos-vim docker images | grep centos-vim 查看镜像的详细信息: docker inspect centos-vim:afcaf46e8305 4.使用centos-vim这个镜像 docker run -it centos-vim /bin/bash 发现可以直接使用vim了,而不需要重新安装: vim --version