Docker镜像与容器:轻松理解与实战
目录
理解镜像和容器
docker命令
数据卷
匿名卷
自定义路径绑定挂载
自定义镜像
Dockerfile
网络
DockerCompose
一个c++可执行程序的镜像构建
理解镜像和容器
比如平时我们安装一个MySQL,需要下载安装包,解压,还有各种环境配置依赖等等,很麻烦
依赖就是MySQL需要哪些库,哪些Linux基础命令,也就是你的软件需要什么才能启动
预设的配置:MySQL的默认配置、端口映射、数据存储在哪等等
docker就是把这些一起打包给你,你直接解压就能够用了
这些整个的打包的东西叫镜像,也就类似模板,只是一个膜具
而你实例化出来的叫容器,就是一个实实在在的软件
最大的好处:
1.Docker会在运行容器的时侯创建一个隔离的环境,就是容器与宿主机(安装docker的机器)环境是隔离的,它有自己的独立的文件系统,网络空间和进程空间等,这就意味着,即使宿主机上有不同版本的软件,也不会影响容器内的应用
2.保证一致性,测试和生产使用同一个镜像,就能保证应用在不同的环境中的运行行为是一致的,解决了在我这能跑,在你那不行的问题
3.一台服务器是往往不同的应用之间有冲突,因为依赖可能不同,有了隔离环境就能隔离开
4.可以部署集群,也就是一台服务器上可以部署多个MySQL,避免了资源的浪费
docker命令
注意:对于一些命令的选项如果不了解,使用--help
如果有命令太长,可以在~/.bashrc中配置命令的别名,然后source ~/.bashrc就可以让这个配置文件生效
命令的详见官方文档:https://docs.docker.com/
1.docker ps:查看当前运行的docker容器
2.docker run:创建并且允许一个容器
-d:让容器在后台允许,如果没有这个选项就是前台,前台的话对于stdout等函数就会输出到你的终端,这样你就无法操作,只能ctrl+x这样就终止进程了,所以一般-d后台(守护进程)运行
--name:就是给容器起一个名字,比如mysql1,mysql2,用于区别不同的容器
-p:端口映射,宿主机端口:容器端口,比如mysql1的端口3306映射到宿主机3306,mysql2就不能映射到宿主机的3306,只能选择别的端口号
-e:设置环境变量,这个是由镜像的制作者也就是mysql的官方来弄的,怎么查,需要去文档,也就是dockerhub中查询
也就是run命令的最后一行 完整的应该是mysql:5.7(带上版本号,如果没有就是默认最新)
镜像仓库也就是你docker配置的镜像源,如果配置了阿里的那就去会阿里拉取,别人做的都放在镜像仓库中,pull就是拉到本地,run先会检查本地,如果本地没有就会去远端拉,注意:拉完本地也会有
注意:images就是镜像的意思,所以docker images就是看本地镜像
这里的stop停止容器是停掉这个进程,但容器还在,这不是删除
这里的build save load后面讲解
数据卷
在容器内部没有这些命令,说明在这个容器隔离的环境中只是装了nginx运行所需要的依赖和一些系统函数库,不是每个都装,不如ll,vim就没有,所以修改文件就很困难
所以提出了数据卷
进行一个双向的映射,即做一个绑定,宿主机当中修改会同步到容器,容器修改也会同步宿主机
所有的命令都是docker开头,然后操作的是数据卷,所以是docker volume
已经删除掉了Nginx,现在使用run命令直接在创建的时候实现挂载
使用docker volume ls查询的时候可以看到有个html的数据卷
可以看到对应关系,对应到/var/lib/docker/volumes/html/_data
/var/lib/docker/volumes是数据卷默认存放的目录,html是数据卷名,_data是数据卷实际存放数据的子目录
数据卷的好处:
1.数据持久化:容器的创建和删除十分方便,但是容器删除掉了之后,容器中的数据也没了,如果使用了数据卷就能给数据持久化,容器删除并不会影响到数据卷中的数据,当数据出现问题的时候,也可以使用数据卷备份和恢复
2.数据共享与迁移:多个容器可以挂载同一个数据卷完成数据共享,还可以进行跨主机迁移
匿名卷
命令docker inspect nginx,就可以查询到nginx容器的详细信息
这个是nginx之前挂载的数据卷
这个是mysql的,没有配置过,由容器运行的时候自动创建的,这种叫匿名卷,是自动生成的,往后mysql运行的数据就是存储在这里的
匿名卷产生的两种场景:
1.创建容器,当你用 -v
或 --mount
挂载时,若只写了容器内需要挂载的路径,没指定宿主机路径或已存在的命名卷,Docker 会自动创建匿名卷并挂载到该容器路径。
2.部分官方镜像(如 MySQL、Redis、PostgreSQL)的 Dockerfile 会提前用 VOLUME
指令声明 “需要持久化的目录”,目的是避免容器删除时数据丢失。(dockerfile是一个自动化构建镜像的文件)
注意:如果容器删除了,但这个匿名卷还是在的,所以数据还是在的,跟数据卷的特性一样
但匿名卷存在命名随机,不方便开发人员直接通过命名查看和维护,所以一般使用命名卷
自定义路径绑定挂载
你完全可以指定一个本地的目录来创建自定义路径的数据卷,不使用官方默认的路径
但注意docker volume之类的命令无法使用,因为没有在默认路径中是不由docker统一管理的
但是你可以通过宿主机的ls等命令来管理
自定义镜像
构建一个c++镜像的步骤:
1.准备一个Linux的运行环境
2. 安装 C++ 编译器(如 g++、clang)和构建工具(如 make)
3.拷贝 C++ 源码或编译好的可执行文件
4.编写运行或编译运行的脚本(若需在容器内编译,可编写编译脚本;若直接运行可执行文件,可编写启动脚本)
对于docker不是把所有产生的文件压缩成一个,是每一层产生的文件压缩出来一个
所谓的镜像就是所有的压缩包合在一起而成的
镜像的结构:
镜像是分层的,把每次操作都称为一层,所以我们之前在下载的时候,每一行显示的就是下载一个文件后进行解压,后续合并在一起就是一个完整的容器了
分层打包的好处
1.便于解耦,如果镜像都使用了相同的一层,这叫基础镜像,这样每次制作镜像的时候这一层就不需要考虑了
2.如果下载的时候b镜像的前两层和之前下载的a镜像的前两层一样,就不需要再下载了,这样就不会重复
总结:分层,结构清晰,但实际的操作复杂,为了简化,你只需要描述好入口和层和基础镜像就好
这样docker就会帮我们完成整个镜像的构建
Dockerfile
简单来说就是使用Dockerfile来描述镜像结构
总结一下之前的命令:也就是你构建好了镜像的结构/层次之后(dockerfile),使用命令build可以构建一个镜像,这个镜像就可以在不同的环境当中部署你的软件,docker save是将你的本地镜像打包成一个文件给别人,load就是把别人的镜像文件下载成本地镜像
网络
容器之间是相互隔离的,那容器之间是否可以相互访问呢
通过docker inspect 查看不同容器的详细信息
可以看到不同的容器当中ip地址不一样
一个是172.17.0.3
一个是172.17.0.4
说明是在同一个网段当中,因为他们有用一个网关(Gateway)
进入容器中,ping一下,发现是可以ping成功的
但是这样是有问题的,如果你当前容器stop了,别的容器启动就会分配ip地址,可能会占用你之前stop容器的ip地址,就会导致ip地址是可能会发生变换的,所以这种ip地址分配不是很好
所以产生了自定义网络
创建了一个test的自定义网络
通过ip addr查询可以看到多了一个网段172.18
容器与自定义网络相连
可以看到网络当中有两个,第一个就是创建容器的时候默认的,第二个就是test自定义网络
当然你可以创建的时候使用--network test 直接与自定义网络相连,这样就不会生产默认的了
然后进入容器ping一下test中的ip地址也是成功的或者直接ping test中其他的容器名
(注意只有处于同一组网络中才能ping成功的,因为网络具有隔离性)
自定义网络的好处:
1.容器间通信便捷与灵活:可以将不同组的容器分在不同的网络组中,实现通信隔离;一个容器还可以加入多个组,实现更复杂的通信场景;可以直接使用ping 容器名来通信,不必像之前一样ping ip地址,而且ip地址也会变换
DockerCompose
就是帮你一条龙服务
当应用需要多个容器协同工作时(例如:一个 Web 应用 + 一个数据库 + 一个缓存服务),手动管理每个容器的启动顺序、网络连接、数据卷挂载 等会非常繁琐。docker-compose
解决了这个问题:
- 用一个配置文件定义所有容器的参数(镜像、端口、网络、依赖关系等)
- 一键启动 / 停止 / 重启所有关联容器
- 自动处理容器间的网络连接(无需手动手动创建网络)
dockercompose配置文件中的信息跟run命令中的是差不多的
一个c++可执行程序的镜像构建
写一个可执行程序,也就是相当于你的软件
注意这个Dockerfile文件,docker会从上往下扫,逐行执行
WORKDIR:就是再容器内部创建目录,你后续的命令如COPY main.cpp . 就是把这个文件拷贝到容器中的WORKDIR的创建的目录
CMD就是别人启动容器的时候执行的,所以这里就是直接进入容器的交互式终端,由于前面WORKDIR设置了目录,所以直接就进行这个目录
注意:
1.EXPOSE就是设置端口号,如果你的程序当中绑定监听了某个端口号,你就需要EXPOSE提示使用者你可能绑定了什么端口号,这样它在未来使用容器的时候-p 宿主机端口号:镜像人绑定的端口号,使用者需要知道你绑定了哪个端口号,要进行映射
2.CMD与ENTRYPOINT的区别
CMD命令,如果使用容器的人在run命令中使用了额外的命令在最后就会覆盖掉CMD中的参数,前面的-p -v等不影响
ENTRPOINT不会覆盖,会追加在后面
现在我们简单写了一个Dockerfile,使用build构建一下镜像就可以,之后的命令就是运行