[学习笔记】学习 Docker,看完这篇超级详细的教程就够了!
本文为观看B站狂神的视频学习Docker时所整理的笔记,中间加了一些自己的理解,如果对大家有所帮助,还希望点赞支持一下,后续我会继续将自己遇到的Docker问题以及学习的新的知识全部整理添加到本文后面或者整理发布新的文章,如果感兴趣可以关注一波。
目录
- Docker的概述
- 为什么需要Docker?
- 简述虚拟机和Docker容器的区别
- Docker中的DevOps(开发、运维)
- Docker的基本组成
- Docker的安装
- 前期准备(看看即可)
- 开始安装
- 如何卸载Docker(了解即可)
- 配置阿里云镜像加速
- Run的流程以及Docker原理
- Docker的常用命令
- 查看信息和帮助命令
- 镜像命令
- 容器命令
- 常用的其他命令
- 后台启动容器
- 查看系统CPU状态
- 查看日志
- 查看容器中进程信息
- 查看镜像中的元数据
- 进入当前正在运行的容器
- 从容器中拷贝文件到主机上
- 常用命令小结
- 练习
- 练习一:使用Docker安装Nginx
- 练习二:使用Docker部署Tomcat
- 练习三:使用Docker部署Elasticsearch、Kibana
- Docker镜像原理
- 镜像是什么?
- 如何得到镜像?
- Docker镜像加载原理
- 镜像原理之分层理解
- Commit镜像(如何提交一个自己的镜像)
- 注意
- Docker容器数据卷的使用
- 什么是容器数据卷?
- 如何使用数据卷?
- 测试文件同步
- 实战:MySQL同步数据
- 具名挂载和匿名挂载
- 匿名挂载
- 具名挂载
- 扩展
- 数据卷之初识Dockerfile(方式二)
- 数据卷容器
- DockerFile的学习
- Dockerfile的介绍
- DockerFile 的构建过程
- Dockfile的指令
- 实战:构建自己的Centos
- CMD 和 ENTRYPOINT区别【了解】
- 实战:使用DockerFile制作一个Tomcat镜像
- 发布镜像到DockerHub
- 发布镜像到阿里云容器服务
- Docker所有流程小结
- Docker网络的学习
- Docker0 网络详解
- 小结
- 容器互联 --link 【了解即可】
- 自定义网络【create】
- 网络连通【connect】
- 实战:部署Redis集群
- SpringBoot微服务打包成Docker镜像
Docker的概述
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。
Docker的官网地址:https://www.docker.com/
文档地址:https://docs.docker.com/
仓库地址:https://hub.docker.com/
为什么需要Docker?
传统的开发问题:
-
环境(切换/配置)麻烦
在开发一个产品的途中,时常会出现的问题就是:“在我的电脑上可以运行,而到了另外一个开发人员的电脑上就不能使用的问题”,或者是因为版本更新导致服务不可用等等,这对于运维人员来说,压力十分大,而环境配置是一个非常头疼的问题,每一个机器都需要部署环境(Redis集群、ES…)十分麻烦且费时费力,并且配置的环境不能跨平台,非常不方便。 -
应用之间的冲突(隔离性)
假如我们将开发的两个应用部署到同一个服务器上,如果一个应用出了问题,导致CPU出问题上升到了100%,那么第二个应用也会受到关联;并且如果两个应用分别使用不同的语言或者技术栈,当安装在同一个服务器上的时候可能就会造成各种冲突/无法兼容,到时候调试就非常头疼。如下图所示:
Docker的出现解决以上问题
-
关于环境问题解决方案
Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。将环境构建打包成一个镜像发布到网上,想要用这个环境的时候就直接拉取一份就OK了。 -
解决应用之间隔离问题
Docker核心思想就是使用容器化技术,打包装箱,每个箱子是互相隔离的。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。如下图所示:
简述虚拟机和Docker容器的区别
这里引用知乎上大佬的说法:https://www.zhihu.com/question/48174633
服务器虚拟化解决的核心问题是资源调配,而容器解决的核心问题是应用开发、测试和部署。
- 使用Docker容器和使用虚拟机在运行多个相互隔离的应用时对比,Docker要简洁很多;
- Docker守护进程可以直接与主操作系统进行通信,为各个Docker容器分配资源;它还可以将容器与主操作系统隔离,并将各个容器互相隔离。
- 虚拟机启动需要数分钟,而Docker容器可以在数毫秒内启动。由于没有臃肿的从操作系统,Docker可以节省大量的磁盘空间以及其他系统资源。
- 虚拟机更擅长于彻底隔离整个运行环境。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而Docker通常用于隔离不同的应用,例如前端,后端以及数据库。
- 传统的虚拟机,首先是虚拟出一条硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件;
- Docker容器内的应用直接运行在宿主机的内容,容器是没有自己的内核的,也没有虚拟我们的硬件,所以就轻巧了很多;
Docker中的DevOps(开发、运维)
(1)应用更快速的交付和部署:
- 传统:一堆的帮助文档和安装程序
- Docker:打包镜像发布测试,一键运行
(2)更快捷的升级和扩缩容
- 使用了Docker以后我们部署应用就像搭积木一样;
- 项目打包成一个镜像
(3)更简单的系统运维
- 在容器话之后我们的开发、测试环境都是高度一致的
(4)更高效的计算资源利用
- Docker是内核级别的虚拟化,可以再一个物理机上可以运行很多的容器实例!服务器的性能可以被压榨到极致。
Docker的基本组成
-
镜像(image):docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像—>run---->tomcat01容器(提供服务),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中运行的)。
-
容器(container):docker利用容器技术,独立运行一个或者一组应用,通过镜像来创建的,启动、停止、删除、基本命令,目前就可以把这个容器理解为就是一个简易的Linux系统。
-
仓库(repository):仓库就是存放镜像的地方,仓库分为公有仓库和私有仓库,Docker Hub(默认是国外的)、阿里云…都有容器服务器(配置镜像加速)
Docker的安装
前期准备(看看即可)
不一样的话也行,主要是看Centos的版本
运行环境:阿里云服务器 Centos 7
连接工具:XShell 5
需要掌握:Linux的基本命令
由于这里的运行环境是Centos 7,与Centos 6的命令还是有一些区别的,但是区别不大,后面我会将命令详细的介绍。
查看环境:
- 系统内核是3.10以上的
[root@oldou ~]# uname -r
3.10.0-957.21.3.el7.x86_64
- 查看系统的版本:
cat /etc/os-release
开始安装
这里可以参考官网的帮助文档进行安装
地址:https://docs.docker.com/engine/install/centos/
第一步:卸载旧版本(运行以下代码即可)
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
第二步:安装yum工具包
命令:yum install -y yum-utils
第三步:设置镜像仓库
yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 这里默认是国外的,十分的慢,我们用以下阿里云的地址
yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 这是阿里云的docker镜像地址,建议使用这个
更新一下yum索引:
命令:yum makecache fast
第四步:安装Docker
docker-ce:社区版 ee:企业版
命令:yum install docker-ce docker-ce-cli containerd.io
注意:以上是安装Docker最新的版本,如果想要安装指定的版本,就需要进行以下命令:yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
这里的<VERSION_STRING>
表示版本。
第五步:启动Docker
命令:systemctl start docker
注意:Centos6使用的是service
,而Centos7使用的都变成了systemctl
。
第六步:查看Docker是否安装成功
命令:docker version
查看Docker的状态: systemctl status docker
第七步:测试hello-word
命令:docker run hello-world
第八步:查看一下刚刚下载的hello-word的这个镜像
命令:docker images
如何卸载Docker(了解即可)
官网中有介绍,下面我给出来:
第一步:卸载依赖
命令:yum remove docker-ce docker-ce-cli containerd.io
第二步:删除资源 安装的资源都在/var/lib/docker目录下
命令:rm -rf /var/lib/docker
这样就OK了。
配置阿里云镜像加速
由于我们拉取镜像的时候,使用国外的下载太慢了,当然我们这里是使用国内阿里云的,所以肯定比国外的访问和下载快,这个时候再配个加速器就美滋滋。
第一步:登录我们的阿里云控制台,然后找到容器镜像服务
第二步:找到镜像加速器---->镜像加速地址
第三步:配置使用(直接复制命令运行即可)
(1)在服务器中创建一个目录:sudo mkdir -p /etc/docker
(2)编辑配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://7ih0bv9h.mirror.aliyuncs.com"]
}
EOF
(3)将服务重启:sudo systemctl daemon-reload
(4)启动docker:sudo systemctl restart docker
这样就配置完成了。
Run的流程以及Docker原理
回顾hello-word的流程
就拿我们运行hello-word来说,一开始我们运行hello-word的时候,Docker会在本机中寻找镜像,而本机中没有这个镜像,所以就回去我们配置的Hub上去下载镜像,如果找到了就将镜像下载到本地运行,如果没有就返回错误。如下所示:
底层原理
(1)Docker是怎么工作的?
- Docker是一个Client-Server结构的系统,Docker的守护进程运行在本机上(运行在后台,和mysql一样),通过Socket从客户端访问。
- DockerServer接收到Docker-Client的指令,就会执行这个命令。
(2)Docker为什么比虚拟机(VM)快?
- Docker利用的是宿主机的内核,VM需要的是Guest OS
所以说,新建一个容器的时候,Docker不需要像虚拟机那样重新加载一个操作系统内核,避免引导,虚拟机是加载Guest OS,是分钟级别的,而Docker利用的是宿主机的操作系统,省略了这个复杂的过程。
Docker的常用命令
官方帮助文档:https://docs.docker.com/engine/reference/run/
查看信息和帮助命令
命令:docker version
# 查看docker的版本信息
命令:docker info
# 显示Docker的系统信息,包括镜像和容器的数量
命令:docker xxx --help
#帮助命令,可以显示docker的所有命令
镜像命令
查看镜像
命令:docker images
# 查看所有本地的主机上的镜像
REPOSITORY :镜像的仓库源
TAG :镜像的标签
IMAGE ID :镜像的ID
CERATED :镜像的创建时间
SIZE :镜像的大小
docker images [可选项]
-a , --all # 列出所有镜像
-q , --quiet # 只显示镜像的ID
搜索镜像
命令:docker search 镜像名
# 搜索镜像
可选项,通过搜过过滤
--filter-STARS=3000 #搜索出来的镜像修饰STARS大于3000的
下载镜像
命令:docker pull 镜像名 [:tag]
# 下载镜像,默认是下载最新版 latest 。
如果是下载指定版本的镜像,那么就需要指定:版本号
,如下所示:docker pull mysql:5.7
# 指定版本下载 下载mysql5.7
删除镜像
# 删除镜像 -f就是全部删除,后面的条件是根据什么来删除,这里是根据容器的id进行删除
docker rmi -f 容器id
# 删除多个镜像
docker rmi -f 容器id 容器id 容器id
# 删除所有的镜像
docker rmi -f $(docker images -aq)
容器命令
说明:我们有了镜像才可以去创建容器,Linux,下载一个centos镜像来进行学习。
下载centos镜像
命令:docker pull centos
新建容器并启动
命令:docker run [可选参数] image
参数说明:
--name="Name" : 容器名字 ,tomcat01、tomcat02,用来区分容器
-d :后台方式运行
-it:使用交互方式运行,进入容器查看内容
-p :指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口(常用方式)
-p 容器端口
容器端口
-p: 随机指定端口
测试,启动并进入容器
命令:docker run -it centos /bin/bash
#启动容器
命令: ls
# 查看容器内的centos,基础版本,很多命令都是不完善的
命令:exit
# 从容器中退回到主机
列出所有运行中的容器
命令:docker ps [可选参数]
# 列出当前正在运行的程序
-a #列出当前正在运行的容器+带出历史运行过的容器
-n=? #显示最近创建的容器
-p #只显示容器的编号
退出容器
命令:
exit # 直接停止容器并退出
Ctrl + P + Q # 容器不停止退出
删除容器
docker rm 容器id # 删除指定的容器,不能删除正在运行的容器
docker rm -f $(docker ps -aq) # 删除所有的容器
docker ps -a -q|xargs docker rm # 删除所有的容器
启动和容器的操作
docker start 容器id # 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止正在运行的容器
docker kill 容器id # 强制停止当前正在运行的容器
常用的其他命令
后台启动容器
命令:docker run -d 镜像名
docker run -d centos # 后台启动centos
-
问题:使用
docker ps
查看,发现 centos停止了 -
常见的坑:docker容器使用后台运行的时候就必须要一个前台线程,不然docker发现没有应用就会自动停止
-
例如:nginx容器启动后,发现自己没有提供服务就会立刻停止,就是没有程序了。
查看系统CPU状态
命令:docker stats [容器id]
查看日志
命令:docker logs -tf --tail 容器
,没有日志
- 编写一段shell脚本
docker run -d centos /bin/sh -c "while true; do echo oldouTest;sleep 1;done"
- 使用
docker ps
去查看 - 显示日志 :
docker logs -tf --tail number 容器id
-tf #显示日志
--tail number #要显示日志条数
例如:docker logs -tf --tail 10 容器id
查看容器中进程信息
命令:docker top 容器id
查看镜像中的元数据
命令:docker inspect 容器id
进入当前正在运行的容器
我们的容器通常都是使用后台方式运行的,需要进入容器,修改一些配置
方式一:
命令:docker exec -it 容器id bashShell
例如:docker exec -it bb6ddb943ea5 /bin/bash
方式二:
命令:docker attach 容器id
两种方式的对比:
-
docker exec
: 进入容器后开启一个新的终端,可以在里面操作(常用) -
docker attach
:进入容器正在执行的终端,不会启动新的进程
从容器中拷贝文件到主机上
# 进入到容器内:
[root@oldou home]# docker exec -it bb6ddb943ea5 /bin/bash
[root@bb6ddb943ea5 /]# cd home
[root@bb6ddb943ea5 home]# ls
# 创建一个java文件
[root@bb6ddb943ea5 home]# touch Test.java
[root@bb6ddb943ea5 home]# ls
Test.java
# 退出容器回到主机
[root@bb6ddb943ea5 home]# exit
exit
#查看当前运行的容器
[root@oldou home]# docker ps
# 将容器内/home目录下的Test.java文件拷贝到主机的home目录下
[root@oldou home]# docker cp bb6ddb943ea5:/home/Test.java /home
[root@oldou home]# ls
Test.java
拷贝只是一个手动的过程,后面学习了 -v 卷的技术时,可以实现同步数据
常用命令小结
命令 | 英文描述 | 中文意思 |
---|---|---|
attach |
Attach to a running container | 当前shell下attach连接指定运行镜像 |
build |
Build an image from a Dockerfile | 通过Dockerfile定制镜像 |
commit |
Create a new image from a container changes | 提交当前容器为新的境像 |
cp |
copy files/folders from the containers filesystem to the host path | 从容器中拷贝指定文件或者目录到宿主机中 |
create |
Create a new container | 创建一个新的容器,同run.但不启动容器 |
diff |
Inspect changes on a container’s filesystem | 查看docker容器变化 |
events |
Get real time events from the server | 从docker服务中获取容器实时事件 |
export |
Stream the contents of a container as a tar archive | 导出容器的内容流作为一个 tar归档文件[对应import] |
history |
Show the history of an image | 展示一个境像形成历史 |
images |
List images | 列出系统当前所有镜像 |
import |
Create a new filesystem image from the contents of a tarball | 从tar包中的内容创建一个新的文件系统映像[对应export] |
info |
Display system-wide information | 显示系统相关信息 |
inspect |
Return low-level information on a container | 查看容器详细信息 |
kill |
Kill a running container | kill指定docker容器 |
load |
Load an image from a tar archive | 从—个 tar包中加载一个镜像[对应save] |
login |
Register or Login to the docker registry server | 注册或者登陆一个docker源服务器 |
logout |
Log out from a Docker registry server | 从当前Docker registry退出 |
logs |
Fetch the logs of a container | 输出当前容器日志信息 |
port |
Lookup the public-facing port which is NAT-ed to PRIVATE_PORT | 查看映射端口对应的容器内部源端 |
pause |
Pause all processes within a container | 暂停容器 |
ps |
List containers | 列出容器列表 |
pull |
Pull an image or a repository from the docker registry server | 从docker镜像服务器中拉取指定镜像或者库镜像 |
push |
push an image or a repository to the docker registry server | 推送指定镜像或者库镜像至docker源服务器 |
restart |
Restart a running container | 重启运行的容器 |
rm |
Remove one or more containers | 移除—个或者多个容器 |
rmi |
Remove one or more images | 移除一个或多个境像[无容器使用该镜像才可删除,否则需删除相关容器才可能继续或者 -f 强制删除 |
run |
Run a command in a new container | 创建—个新的容器并运行一个命令 |
save |
Save an image to a tar arehive | 保存一个镜像为—个tar包「对应load] |
search |
Search for an image on the Docker Hub | 在docker hub中搜索镜像 |
start |
start a stopped containers | 启动容器 |
stop |
stop a stopped containers | 停止容器 |
tag |
Tag an image into a repository | 给源中镜像打标签 |
top |
Lookup the running processes of a container | 查看容器中运行的进程信息 |
unpause |
Unpause a paused container | 取消暂停容器 |
version |
show the docker version information | 查看docker版本号 |
wait |
Block until a container stops,then print its exit code | 截取容器停止时的退出状态值 |
练习
练习一:使用Docker安装Nginx
官网:https://hub.docker.com/_/nginx
第一步:搜索镜像(这里建议去docker Hub上查找一下版本信息,上面还有帮助文档介绍)
命令:docker search nginx
第二步:下载镜像
命令:docker pull nginx
第三步:查看镜像并且启动
命令:docker images # 查看所有镜像
**命令:docker run -d --name nginx01 -p 3344:80 nginx
-d 表示后台运行,--name 表示给容器起名字,而nginx01表示起的名字,
-p 宿主机端口 :容器内部端口
后面的3344表示主机开发的3344端口,80表示容器内部的端口,这里表示将80端口映射到外部的3344,
可以通过公网的3344访问到docker里面的80端口,后面的nginx表示的是镜像名字。
第四步:查看容器并且运行测试
命令:docker ps
#查看是否启动
命令:curl localhost:3344
关闭防火墙:systemctl stop firewalld
到这一步的时候我们就可以使用 公网ip:3344 在浏览器访问了
第五步:进入Nginx容器
命令:docker exec -it nginx01 /bin/bash
#进入容器
命令:whereis nginx
命令:cd /etc/nginx
命令:ls查看
命令:exit #退出
命令:docker stop 容器id
#停止Nginx容器
端口暴露的概念:
思考问题:我们每次改动nginx配置文件,都需要进入容器内部?十分的麻烦,我要是可以在容器外部提供一个映射路径,达到在容器修改文件名,容器内部就可以自动修改?
使用 -v数据卷。
练习二:使用Docker部署Tomcat
官网:https://hub.docker.com/_/tomcat
方式一:
-
官方给出的命令:
docker run -it --rm tomcat:9.0
# 下载并启动
上面介绍Nginx的启动都是后台启动,停止了容器以后,容器还是可以查到,并且占用着配置的端口。
使用官方给出的docker run -it --rm tomcat:9.0
,一般用于测试,用完停止就删除。
方式二:
还是使用之前的方式。
-
第一步:搜索镜像(这里建议去docker Hub上查找一下版本信息,上面还有帮助文档介绍)
命令:docker search tomcat
-
第二步:下载镜像
命令:docker pull tomcat
# 这是下载最新版的tomcat
命令:docker pull tomcat:7.0
# 指定版本号 -
第三步:查看镜像并且启动
命令:docker images
# 查看所有镜像
命令:docker run -d --name tomcat01 -p 3355:8080 tomcat:7.0
-d 表示后台运行,--name 表示给容器起名字,而tomcat01表示起的名字,
-p 宿主机端口 :容器内部端口
后面的3355表示主机开发的3355端口,8080表示容器内部的端口,这里表示将8080端口映射到外部的3355,
可以通过公网的3355访问到docker里面的8080端口,后面的tomcat表示的是镜像名字。
我们这个时候去浏览器访问发现,报了404异常,那我们进入到tomcat容器内部查看一下原因.
-
第四步:进入容器查看原因
命令:docker exec -it tomcat01 /bin/bash
当我们使用ll
命令时竟然不能使用,只能使用ls
命令查看,同时进入到webapps目录下发现这个目录竟然是空的。
发现问题:
1、Linux的命令少了;
2、webapps目录下没有文件;
原因:
阿里云的镜像默认是最小的镜像,它把所有不必要的都剔除掉了。保证最小可运行的环境。
解决办法:
在tomcat目录下有一个webapps.dist
目录,这个目录下有我们所需要的文件,也就是webapps目录所需要的文件,我们将这个文件中的内容全部拷贝到webapps下。
-
第五步:拷贝资源
命令:cp -r webapps.dist/* webapps
这样我们再去浏览器中去访问就可以访问到了。
思考问题:我们以后要部署项目,如果每次都要进入容器是不是十分麻烦?要是可以在容器外部提供一个映射路径,webapps ,我们在外部放置项目,就自动同步到内部就好了!
练习三:使用Docker部署Elasticsearch、Kibana
由于暂时没有学过Elasticsearch,所以暂时没有做这个部分的笔记,只是暂时看了一遍,后续补上…
【2020.10.06 02:52更新】
Docker镜像原理
镜像是什么?
镜像是一种轻量级的、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容,包括代码、运行时、库、环境变量和配置文件等等。
如何得到镜像?
- 从远程仓库下载镜像
- 从朋友那里拷贝镜像
- 自己制作一个镜像
Docker镜像加载原理
(1)UnionFS(联合文件系统)
UnionFS(联合文件系统):Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以讲不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行集成,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
(2)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系统中的/dev,/proc, /bin,letc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。|
提问:平时我们安装虚拟机的Centos都是好几个G的,为什么Docker里面的Centos镜像只有200M左右呢?
回答:对于一个精简的OS, rootfs 可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别,因此不同的发行版可以公用bootfs.
虚拟机是分钟级别的,而容器是秒级别的,区别就是因为以上解释。
镜像原理之分层理解
(1)分层的镜像
当我们去下载一个镜像的时候,注意观察下载的日志输出,可以看到的是镜像是一层一层的下载的,如下所示:
思考:为什么Docker镜像要采用这种分层的结构呢?
- 最大的好处就是资源共享,例如多个镜像都是从相同的Base镜像构建而来的,那么宿主机只需要在磁盘上保留一份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上仅支持windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW[1]。
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
特点
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部,这一次就是我们通常说的容器层,容器之下的都叫镜像层。
Commit镜像(如何提交一个自己的镜像)
命令:docker commit
#提交一个新的副本
命令和git原理类似
格式为:docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG版本号]
练习:封装一个Tomcat镜像
# 1、启动一个默认的tomcat;
# 2、发现这个默认的tomcat是没有webapps应用,镜像的原因,官方的镜像默认webapps下面是没有文件的;
# 3、我自己拷贝进去了基本的文件;
注意:前面三步不熟悉的话可以看上面的tomcat练习。
# 4、将我们操作过的容器通过commit提交为一个镜像!我们以后就使用我们修改过的镜像即可,这就是我们自己的一个修改的镜像
命令:docker commit -a="oldou" -m="add webapps app" 容器的ID tomcat02:版本号
如果想要保存当前容器的状态,那么就可以通过commit
来进行提交,获取一个镜像,这就好比VM的快照。
注意
学到这里的时候,才算入门Docker,接下来想要对Docker了解更深,就继续往后学吧。
Docker容器数据卷的使用
什么是容器数据卷?
Docker的理念回顾:将应用和环境打包成一个镜像,如果数据都在容器中,那么当我们容器被删除的时候,数据就会丢失。【需求:数据可以持久化】。假如MYSQL数据库,如果容器被删除了,那么数据库内的数据就会丢失,因此我们希望:MySQL数据库中的数据可以存储在本地。
容器之间可以有一个数据共享的技术,Docker容器中产生的数据可以同步到本地。这就是卷技术,目录的挂载:就是将容器内的目录挂载到Linux上面。
总结:容器的持久化和同步操作,容器间也是数据共享的。
如何使用数据卷?
方式一:直接使用命令来挂载 -v
命令格式:docker run -it -v 主机目录 : 容器内目录
测试
-
第一步:我们使用以下命令连接容器,并且将Linux的/home/ceshi目录和容器内的/home目录连接起来,如果没有ceshi目录就会自动创建一个。
命令:docker run -it -v /home/ceshi:/home centos /bin/bash
做完以上的操作,这个时候我们在容器内对/home
目录下的操作会被同步到Linux的/home/ceshi
目录下 -
第二步:当容器启动起来的时候,我们在Linux的操作命令窗口处通过
docker inspect 容器id
去查看卷的挂载信息
命令:docker inspect centos容器的id
以上两个目录的数据会进行同步,例如我在主机内的ceshi目录下做一些操作【增、删、改等等】会同步到docker容器内的home目录下。
注意:如果没有上图中的这个挂载项,就说明挂载失败了,重新再挂载一下。下面我们进行数据同步测试。
测试文件同步
1、测试容器中的数据会不会同步到主机中
测试内容:我们在容器内的home目录下创建一个test.java文件,然后我们在主机内的/home/ceshi目录下查看是否有这个文件。
结果证明,我们在容器中做了一个添加test.java文件的操作,数据会被同步到主机的对应目录下,这就是双向绑定。
2、测试主机中的数据会不会同步到容器中
- 我们先停止容器
- 在主机上修改test.java文件内容【vim test.java】
- 启动容器
结果证明了,容器内的数据依旧是同步的。
好处:我们以后修改只需要在本地修改即可,容器内会自动同步!【2020.10.07 15:54更新】
实战:MySQL同步数据
我们来实现一下将MySQL容器中的数据同步到我们的Linux中,同时实现用Windows电脑上的Navicat连接上Linux上的MySQL容器。
官网:https://hub.docker.com/_/mysql
思考:Mysql的数据持久化的问题
-
第一步:获取镜像,这里以mysql5.7为例
命令:docker pull mysql:5.7
然后开两个窗口: -
第二步:运行容器,需要做数据挂载,同时安装MySQL需要配置密码
官方给出的测试代码:docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
【参考】
运行命令: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
-d 表示后台运行
-p 表示端口映射
-v 表示卷挂载
-e 表示环境配置
--name 表示容器名字
另开一个窗口进行查看
-
第三步:启动成功以后我们在本地使用Navicat连接测试一下
【注意:Navicat —> 连接到服务器的3310 ----> 3310 和 容器内的3306映射 ---- > 这个时候就连接成功了】
【注意】如果连接失败,记得去查看一下是不是防火墙没关【systemctl stop firewalld】或者是服务器的安全组3310端口没配置。 -
第四步:在本地创建一个数据库进行测试,查看一下我们映射的路径是否OK。
原先的主机数据库目录:
然后我在Navicat上创建了这个数据库
再次查看:
发现数据已经挂载到本地了,测试OK。 -
第五步:假如我们将mysql干掉会怎么样?数据还能不能同步?
我们再去另外一个窗口看看我们的数据
我们发现挂载到本地的数据卷依旧没有丢失,这就实现了容器数据持久化功能!
具名挂载和匿名挂载
匿名挂载
通过【-v 容器内路径
】 而没有指定卷名或者指定主机路径的挂载方式
运行以下命令【注意:需要将本机已下载的nginx镜像删除】
命令:docker run -d -P --name nginx02 -v /etc/nginx nginx
同时通过docker volume --help查看一下卷的帮助文档
分别是:创建卷、查看卷、查看所有的卷、移除卷、移除卷
我们使用以下命令查看一下所有本地的volume【卷】信息:
命令:docker volume ls
通过以上我们发现,我们刚刚创建的匿名卷挂载,就是没有给它起名字,这是因为我们在运行的时候只指定了容器内的路径而没有指定容器外的路径和卷名,这里发现,这种就是匿名挂载,我们在-v只写了容器内的路径,没有写容器外的路径!
具名挂载
通过【-v 卷名:容器内路径
】 而没有指定主机路径的挂载方式
运行以下命令
命令:docker run -d -P --name nginx03 -v juming-nginx:/etc/nginx nginx
我们使用以下命令查看一下所有本地的volume【卷】信息:
命令:docker volume ls
我们通过【docker volume inspect juming-nginx
】命令查看一下这个卷的详细信息
我们发现所有的docker容器内的卷,在没有指定目录的情况下都是在/var/lib/docker/volumes/xxxx/_data
通过具名挂载可以方便的找到我们的一个卷,大多数情况在使用的具名挂载
# 如何锁定是具名挂载还是匿名挂载,还是指定路径挂载
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径:容器内路径 #指定路径挂载
扩展
# 通过 -v 容器内路径:ro rw 改变读写权限
ro readonly # 只读
rw readwrite # 可读可写
# 一旦设置了容器权限,容器对我们挂载出咯哎的内容就会有限定了
docker run -d -p --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -p --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作的。
数据卷之初识Dockerfile(方式二)
Dockerfile就是用来创建docker镜像的构建文件,是一个命令脚本,下面我们来初步体验一下Dockerfile。
通过这个脚本可以生成镜像,镜像是一层一层的,脚本也是一个一个的命令,每个命令都是一层。
- 第一步:在home目录下创建一个docker-test-volume目录用于存放脚本测试文件
命令:mkdir docker-test-volume # 创建一个目录
命令:cd docker-test-volume # 进入该目录
# 创建一个dockerfile文件,名字可以随意,建议Dockerfile
命令:vim Dockerfile01 # 编写脚本
# 文件中的内容,指令需要大写
FROM centos # 用什么作为基础
VOLUME ["volume01","volume02"] # 挂载卷的目录,这里没有指定
CMD echo "-------end-------" # 生成完之后发一段消息
CMD /bin/bash # 生成完之后默认是走的控制台
# 这里的每个命令就是镜像的一层
查看一下文件的内容:【cat dockerfile01
】
-
第二步:构建脚本生成镜像
命令:docker build -f /home/docker-test-volume/dockerfile01 -t oldou/centos:1.0 .
-f 通过什么文件来构建,后面接构建文件的地址
-t 生成文件的版本
-
第三步:启动自己的容器
命令:docker run -it db89686baa5c /bin/bash
命令:ls -l
我们在编写脚本时什么都没写,所以挂载方式为匿名挂载,而这个卷和外部有一个同步的目录,我们去查看一下。
我们现在容器内创建一个文件:
然后进行后续操作。 -
第四步:查看一下卷挂载的路径
命令:docker ps
# 先查看一下启动的id
命令:docker inspect 7a9c7b088444
# 根据容器启动的ID查看信息 -
第五步:进入这个路径查看一下卷信息
命令:cd /var/lib/docker/volumes/642be56674ca60230a61455cdb8b06e8968ea7dd11236129a906c1cafaa7fe73/_data
刚刚在容器内创建的文件已经在主机中同步了。测试OK。
结论:Dockerfile这种方式的使用非常多见,因为我们通常会构建自己的镜像。假如我们构建镜像的时候没有挂载卷,这个时候就需要手动镜像挂载,就需要使用【-v 卷名:容器内路径
】
数据卷容器
这里的 centos02 --volumes-from centos01
就相当于son extends father
这里的