一、Docker镜像原理
docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS 。
bootfs(boot fifile system) 主要包含 bootloader 和 kernel, bootloader 主要是引导加载 kernel, Linux 刚启
动时会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs 。这一层与我们典型的 Linux/Unix 系统是
一样的,包含 boot 加载器和内核。当 boot 加载完成之后整个内核就都在内存中了,此时内存的使用权已
由 bootfs 转交给内核,此时系统也会卸载 bootfs 。
rootfs (root fifile system) ,在 bootfs 之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标
准目录和文件。 rootfs 就是各种不同的操作系统发行版,比如 Ubuntu , Centos 等等。
分层理解
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!
为什么 Docker 镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的 Base 镜像构建而来,那么宿主机 只需在磁盘上保留一份base 镜像,同时内存中也只需要加载一份 base 镜像,这样就可以为所有的容器服 务了,而且镜像的每一层都可以被共享。 查看镜像分层的方式可以通过
docker image inspect 命令! 所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之 上,创建新的镜像层。
举一个简单的例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python 包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。 该镜像当前已经包含 3 个镜像层,如下图所示(这只是一个用于演示的很简单的例子)
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了 一个简单的例子,每个镜像层包含 3 个文件,而镜像包含了来自两个镜像层的 6 个文件
上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有 6 个文件,这是因为最上层中的文件 7 是文件 5 的一个更新版本。
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新 镜像层添加到镜像当中。
Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统 一的文件系统。
Linux 上可用的存储引擎有 AUFS 、 Overlay2 、 Device Mapper 、 Btrfs 以及 ZFS 。顾名思义,每种存储 引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
Docker 在 Windows 上仅支持 windowsfifilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分 层和 CoW[1] 。
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图
Docker镜像特点
Docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层,容器之下的都叫镜像层!
------------------------------------------------------------------------------------------------------------------------------
二、镜像Commit
docker commit 从容器创建一个新的镜像
#docker commit -m="描述镜像的信息" -a ="作者" 容器id 要创建的目标镜像名:[标签名]
# 测试-->先从镜像仓库下载一个官方的Tomcat
发现Tomcat下的Webapps里面是没有Root的目录的, 所以访问IP:8080 是404页面..
此时我们可以使用exec -it 容器id /bin/bash 进入tomcat容器内部,将webapps.dist里面的文件复制到webapps文件中,使得该文件里有Root, 并且将DOSC删掉.
docker ps # 查看容器
id docker exec -it 容器id /bin/bash /usr/local/tomcat # cd webapps/
/usr/local/tomcat/webapps # ls -l # 查看是否存在 docs文件夹
/usr/local/tomcat/webapps # curl localhost:8080/docs/ # 可以看到 docs 返回的内容
/usr/local/tomcat/webapps # rm -rf docs # 删除它
/usr/local/tomcat/webapps # curl localhost:8080/docs/ # 再次访问返回404
#提交自制的强化版的Tomcat , 此时我们的webapps里面已经有Root并且没有DOCS了.
docker commit -a="alenwong" -m="this its no docs tomcat" tomcatnew:1.1
[root@localhost alenwong]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat latest 46cfbf1293b1 43 hours ago 668MB
nginx latest 4cdc5dd7eaad 2 weeks ago 133MB
mysql 5.7 09361feeb475 4 weeks ago 447MB
portainer/portainer latest 580c0e4e98b0 4 months ago 79.1MB
hello-world latest d1165f221234 4 months ago 13.3kB
centos latest 300e315adb2f 7 months ago 209MB
[root@localhost alenwong]# docker commit -a="alenwong" -m="thisits no docs tomcat" 46cfbf1293b1 tomcatnew:1.1
提交完成后,直接启动新的镜像容器,访问就有页面了。
三、数据容器卷
问题:你安装一个 MySQL ,结果你把容器删了,就相当于删库跑路了,这 TM 也太扯了吧!
所以我们希望容器之间有可能可以共享数据, Docker 容器产生的数据,如果不通过 docker commit 生成 新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了!这样是行 不通的! 为了能保存数据在Docker 中我们就可以使用卷!让数据挂载到我们本地!这样数据就不会因为容器删除 而丢失了。
卷就是目录或者文件,存在一个或者多个容器中,由 docker 挂载到容器,但不属于联合文件系统,因此 能够绕过 Union File System , 提供一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 Docker 不会在容器删除时删除其挂 载的数据卷。
特点:
1、数据卷可在容器之间共享或重用数据
2、卷中的更改可以直接生效
3、数据卷中的更改不会包含在镜像的更新中
4、数据卷的生命周期一直持续到没有容器使用它为止
方式一,挂载
docker run -it -v 服务器绝对路径目录:容器内目录 镜像名
这里现在Linux主机创建一个文件夹进行测试
[root@localhost alenwong]# cd /home/
[root@localhost home]# ls
alenwong
[root@localhost home]# mkdir guazaitest
[root@localhost home]# ll
总用量 4
drwx------. 16 alenwong alenwong 4096 7月 25 11:40 alenwong
drwxr-xr-x. 2 root root 6 7月 25 13:05 guazaitest #创建一个挂载测试文件夹
#docker run -it -v 宿主机绝对路径:需要挂载的容器路径 镜像名 操作后台
[root@localhost home]# docker run -it -v /home/guazaitest:/home centos /bin/bash #挂载指令
[root@88145a9d4692 /]# cd /home/
[root@88145a9d4692 home]# touch test2.txt
[root@88145a9d4692 home]# ls
test2.txt
在创建了test2.txt后 到第二个session窗口 看看Linux是否有这个文件?
挂载成功。可以通过docker insepct 容器id 查看 是否挂载成功: 如下图
测试容器停止退出后,主机修改数据也是会同步!
1. 停止容器
2. 在宿主机上修改文件,增加些内容
3. 启动刚才停止的容器
4. 然后查看对应的文件,发现数据依旧同步! ok
停止了
修改文件
启动再查看后...
匿名与具名挂载
# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 匿名挂载的缺点,就是不好维护,通常使用命令 docker volume维护
docker volume ls
# 具名挂载 -v 卷名:/容器内路径
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx nginx
# 查看挂载的路径
[root@kuangshen ~]# docker volume inspect nginxconfig
[
{
"CreatedAt": "2020-05-13T17:23:00+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/nginxconfig/_data",
"Name": "nginxconfig",
"Options": null,
"Scope": "local"
} ]
# 怎么判断挂载的是卷名而不是本机目录名?
不是/开始就是卷名,是/开始就是目录名
# 改变文件的读写权限
# ro: readonly
# rw: readwrite
# 指定容器对我们挂载出来的内容的读写权限
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:rw nginx
ro = 只读权限
rw = 读写权限
但是用MYSQL的话,只要删除容器,那么数据就会丢失,显然不可行,这里我们进入
方式二、DockerFile方式进行学习
DockerFile 是用来构建 Docker 镜像的构建文件,是由一些列命令和参数构成的脚本
#命令:
docker build -f dockerfile的绝对路径 -t 镜像名称:版本号 . (注意那个点不要漏了)
[root@localhost docker-test-volume]# pwd
/home/docker-test-volume
[root@localhost docker-test-volume]# vim dockerfile1
[root@localhost docker-test-volume]# cat dockerfile1
FROM centos #来自centos基底
VOLUME ["volumn01","VOLUMN02"] #挂载路径
CMD echo "---------------end----------"
CMD /bin/bash
#docker build -f 是dockerfile的绝对路径 -t 是目标镜像名字 : 版本号 即TAG
[root@localhost docker-test-volume]# docker build -f /home/docker-test-volume/dockerfile1 -t alenwong/centos:1.0 .
#执行过程
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM centos
---> 300e315adb2f
Step 2/4 : VOLUME ["volumn01","VOLUMN02"]
---> Running in ddcd857cec8b
Removing intermediate container ddcd857cec8b
---> 07a38f5a1f62
Step 3/4 : CMD echo "---------------end----------"
---> Running in 9589d8def621
Removing intermediate container 9589d8def621
---> c856b8b4975f
Step 4/4 : CMD /bin/bash
---> Running in d6fc4d6b0cff
Removing intermediate container d6fc4d6b0cff
---> 8f2c3218f044
Successfully built 8f2c3218f044
Successfully tagged alenwong/centos:1.0
[root@localhost docker-test-volume]#
创建成功
如何找到这个容器对应的服务器路径呢?? 我们先找到目前运行中的容器..看看id,然后
docker inspect 这个容器id 就可以找到卷挂载的路径了.
在Linux卷目录中同步成功
数据卷容器
--volumes-form
先启动一个父容器
再创建docker02和03 让他们继承docker01 --volumes-form
[root@localhost docker-test-volume]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da182c3fdf28 portainer/portainer "/portainer" 4 days ago Up 3 hours 0.0.0.0:8088->9000/tcp, :::8088->9000/tcp distracted_torvalds
[root@localhost docker-test-volume]# clear
[root@localhost docker-test-volume]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alenwong/centos 1.0 b32442cbdd6c 36 minutes ago 209MB
tomcatnew 1.1 3507faca9348 2 hours ago 670MB
tomcat latest 46cfbf1293b1 45 hours ago 668MB
nginx latest 4cdc5dd7eaad 2 weeks ago 133MB
mysql 5.7 09361feeb475 4 weeks ago 447MB
portainer/portainer latest 580c0e4e98b0 4 months ago 79.1MB
hello-world latest d1165f221234 4 months ago 13.3kB
centos latest 300e315adb2f 7 months ago 209MB
[root@localhost docker-test-volume]# docker run -it --name docker01 b32442cbdd6c
#创建docker02
[root@localhost docker-test-volume]# docker run -it --name docker02 --volumes-from docker01 alenwong/centos:1.0
[root@0274106f2c4d /]# [root@localhos
#创建docker03
[root@localhost docker-test-volume]# docker run -it --name docker03 --volumes-from docker01 alenwong/centos:1.0
[root@44ed54af47d2 /]# cd home/
#进入容器后,看看挂载的数据卷
[root@44ed54af47d2 /]# ls -l
total 0
lrwxrwxrwx. 1 root root 7 Nov 3 2020 bin -> usr/bin
drwxr-xr-x. 5 root root 360 Jul 25 06:47 dev
drwxr-xr-x. 1 root root 66 Jul 25 06:47 etc
drwxr-xr-x. 2 root root 6 Nov 3 2020 home
lrwxrwxrwx. 1 root root 7 Nov 3 2020 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 Nov 3 2020 lib64 -> usr/lib64
drwx------. 2 root root 6 Dec 4 2020 lost+found
drwxr-xr-x. 2 root root 6 Nov 3 2020 media
drwxr-xr-x. 2 root root 6 Nov 3 2020 mnt
drwxr-xr-x. 2 root root 6 Nov 3 2020 opt
dr-xr-xr-x. 230 root root 0 Jul 25 06:47 proc
dr-xr-x---. 2 root root 162 Dec 4 2020 root
drwxr-xr-x. 11 root root 163 Dec 4 2020 run
lrwxrwxrwx. 1 root root 8 Nov 3 2020 sbin -> usr/sbin
drwxr-xr-x. 2 root root 6 Nov 3 2020 srv
dr-xr-xr-x. 13 root root 0 Jul 25 03:32 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 Jul 25 06:43 volumn01 #数据卷
drwxr-xr-x. 2 root root 6 Jul 25 06:43 volumn02 #数据卷
#在docker03 里的volumn01创建文件...
[root@44ed54af47d2 /]# cd volumn01
[root@44ed54af47d2 volumn01]# ls
[root@44ed54af47d2 volumn01]# touch docker03
在子容器docker03的数据卷目录内 创建了一个文件..看看父容器是否有
注意:容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止。
存储在本机的文件则会一直保留! (即系 只要有一个容器有这个文件, 这个文件会一直保留, 哪怕你突然删除其中一个容器。 但是删除其中一个文件,即会同步删除了。)
DockerFile正式学习
DockerFel基础知识:
1 、每条保留字指令都必须为大写字母且后面要跟随至少一个参数
2 、指令按照从上到下,顺序执行
3 、 # 表示注释
4 、每条指令都会创建一个新的镜像层,并对镜像进行提交
流程:
1、docker从基础镜像运行一个容器
2、执行一条指令并且对容器做出修改
3、执行类似docker commit 的操作提交一个新的镜像层
4、Docker再基于刚刚提交的镜像运行一个新容器
5、执行dockerfile中的下一条指令知道所有指令都执行完成!
说明:
从应用软件的角度来看,DockerFile,docker镜像与docker容器分别代表软件的三个不同阶段。
- DcokerFile是软件的原材料(代码)
- Dcoker镜像则是软件的交付品(.apk)
- Dcoker容器则是软件的运行转改(客户下载安装执行)
DockerFile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可。
DockerFile指令
FROM #基础镜像,当前新镜像是基于哪个镜像的
MAINTAINER #镜像维护者的姓名+邮箱地址
RUN #容器构建时需要运行的命令
EXPOSE #当前容器对外保留出的端口
WORKDIR #指定在创建容器后,终端默认登陆进来的工作目录, 一个落脚点
ENV #构建过程中设置环境变量的
ADD #将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL或解压tar压缩包
COPY #类似ADD , 拷贝文件和目录到镜像中
VOLUME # 容器数据卷,用于数据保存和持久化的工作
CMD #指定一个容器启动时要运行的命令,dockerFile中可以有多个CMD命令,但是只有最后一个生效。
ENTRYPOINT #指定一个容器启动时要运行的命令,和CMD一样
CMD和ENTRYPOINT的区别
CMD : Dockerfifile 中可以有多个 CMD 指令,但只有最后一个生效, CMD 会被 docker run 之后的参数 替换!
ENTRYPOINT : docker run 之后的参数会被当做参数传递给 ENTRYPOINT ,之后形成新的命令组合(即是可以追加)
ONBUILD #当构建一个被继承的DockerFile时运行命令,父镜像在子镜像继承后,父镜像ONBUILD被触发
构建DockerFile指令
docker build -f DockerFile路径 -t 镜像名字:tag版本号 .
注意:!! 当DockerFile文件名为 DockerFile后, 不需要-f docker build会自动去识别DockerFile文件!!!!
试试用DockerFile创建一个增强的Centos
原本下载的Cetnos没有任何命令拆件。。。
创建一个DockerFile文件进行增强
文件内容
利用DockerFile构建一个新的镜像
构建完成后, 看看自己造的centos。。已经有增强了。
查看镜像地的变更历史
docker history 镜像id
练习: 自定义制作TOMCAT
先用rz上传 JDK和TOMCAT9 的压缩包
2. 编写DockerFile文件
vim Dockerfile :
创建刚刚写的DockerFile镜像
搞掂..即刻运行一下
启动自定义tomcat!!!!!!!!!
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
diytomcatgogogo latest c904850b0018 5 minutes ago 614MB
alencentos/havetools 1.0 09d2bcc3a56d About an hour ago 302MB
alenwong/centos 1.0 b32442cbdd6c 4 hours ago 209MB
tomcatnew 1.1 3507faca9348 6 hours ago 670MB
tomcat latest 46cfbf1293b1 2 days ago 668MB
nginx latest 4cdc5dd7eaad 2 weeks ago 133MB
mysql 5.7 09361feeb475 4 weeks ago 447MB
portainer/portainer latest 580c0e4e98b0 4 months ago 79.1MB
hello-world latest d1165f221234 4 months ago 13.3kB
centos latest 300e315adb2f 7 months ago 209MB
#启动容器
#第一个-v 挂载webapps到宿主机
#第二个-v 挂载日志.
[root@localhost dev]# docker run -d -p 9090:8080 --name mydiytomcat -v /home/dev/build/tomcat/test:/usr/local/apache-tomcat-9.0.50/webapps/test -v /home/dev/build/tomcat/tomcat9logs/:/usr/local/apache-tomcat-9.0.50/logs --privileged=true diytomcatgogogo
15698eb3eaa237056549cb38fd647fb76acdb01a4ecefb5222aa42e0b8900edd
测试可访问后,可以加入以下东西令页面有东西可以访问.
#web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>test</display-name>
</web-app>
#jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>hello,alenwong</title>
</head>
<body> -----------welcome------------
<%=" my docker tomcat,kuangshen666 "%> <br> <br> <% System.out.println("-------my docker tomcat-------");%> </body> </html>