镜像和容器
利用docker安装应用时,Docker会自动搜索并下载应用镜像(image) 。镜像不仅包含应用本身,还包含应用运行所需要的环境、配置、系统函数库。Docker会在运行镜像时创建一个隔离环境,称为容器(container)。
镜像仓库:存储和管理镜像的平台,Docker官方维护了一个公共仓库:Docker Hub
命令解读
端口映射时,前者是宿主机端口,后者是容器内端口
常见命令
docker run:创建并运行容器(每执行一次都会创建一个新的容器)
docker stop:停掉容器内的进程(容器还在)
docker start:启动容器内停掉的进程(不会创建容器 )
docker ps:查看当前容器的运行状态(有哪些容器以及容器的状态)
|----------------|--------------------------------------------------------|
| docker pull | 拉取镜像 |
| docker push | 推送镜像到DockerRegistry |
| docker images | 查看本地镜像 |
| docker rmi | 删除本地镜像 |
| docker run | 创建并运行容器(不能重复创建) |
| docker stop | 停止指定容器 |
| docker start | 启动指定容器 |
| docker restart | 重新启动容器 |
| docker rm | 删除指定容器(不能删除运行中的容器,但结尾加 -f可以强制删除) |
| docker ps | 查看容器状态(结尾加 -a查看所有容器,包括停掉的) |
| docker logs | 查看容器运行日志(后加 -f表示实时加载,后加 容器名) |
| docker exec | 进入容器(后加 -it表示添加一个可输入的终端,再加 容器名表示目标容器名,再加 bash表示用命令行交互) |
| docker save | 保存镜像到本地压缩文件 |
| docker load | 加载本地压缩文件到镜像(后加 -i 压缩包全名) |
| docker inspect | 查看容器详细信息 |
命名别名
vi /root/.bashrc
alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
alias dis='docker images'
镜像原理
镜像本质
镜像是一种轻量级的、可执行的独立软件包。用来打包软件运行环境和基于运行环境的开发软件,它包含运行某个软件所需要的内容,包括代码、运行时、库、环境变量和配置文件。
Docker镜像加载原理
UnionFS(联合文件系统):UnionFS文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层一层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是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, /etc等等标准文件。rootfs就是各种不同的操作系统发行版本,比如Ubuntu、CentOS等。
对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令、工具和程序即可,因为底层直接用Host的kernel,自己只需要提供rootfs即可。由此可见不同的Linux发行版本,bootfs基本上是一致的,rootfs会有差别,所以不同的发行版可以公用bootfs,这也是一个镜像仅有几百MB的原因。
数据卷
数据卷是由 Docker 管理的存储,可以被一个或多个容器共享。它通常用于持久化数据并确保容器停止或删除时数据不会丢失。数据卷的优势是独立于容器生命周期,可以跨容器使用,并且易于备份和恢复。
数据卷是一个虚拟目录,是容器内目录与虚拟机宿主目录之间映射的桥梁。是一个双向自动映射,其中一方改变,另一方会自动改变。由于容器会自动配置其必须得依赖和环境,读写文件的编辑器一般不会被配置,所以本来无法再容器内直接读写文件,但是数据卷解决了这一问题。
卷就是目录或者文件,存在一个或者多个容器之中,由docker挂载到容器,但是不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或者共享数据的特性。
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此docker不会再容器删除时删除其挂载的数据卷。
它还存在以下几种特点:
1、数据卷可在容器之间共享或者重用数据。
2、卷中的更改可以直接生效。
3、数据卷中的更改不会包含在镜像的更新中。
4、数据卷的生命周期一直持续到没有容器使用它为止。
我们创建了两个数据卷:
conf、html
- Nginx容器内部的conf目录和html目录分别与两个数据卷关联。
- 而数据卷conf和html分别指向了宿主机的/var/lib/docker/volumes/conf/_data目录和/var/lib/docker/volumes/html/_data目录,这样以来,容器内的conf和html目录就 与宿主机的conf和html目录关联起来,我们称为挂载。此时,我们操作宿主机的/var/lib/docker/volumes/html/_data就是在操作容器内的/usr/share/nginx/html/_data目录。只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了。
注意:容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建。
绑定挂载
- 执行docker run命令时,使用-v 数据卷名称:容器内目录 可以完成数据卷挂载(自动映射)
- 当创建容器时,如果挂载了数据卷且数据卷不存在,会自动创建数据卷
为什么不让容器目录直接指向宿主机目录呢?
- 因为直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了。由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
- 但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合。如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可。
本地目录挂载
数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载
- 执行docker run命令时,使用 -v 本地目录:容器内目录 可以完成本地目录挂载
- 本地目录必须以"/"或"./"开头,如果直接以名称开头,会被识别为数据卷而非本地目录
例:
-v mysql:/var/lib/mysql 会被识别为一个数据卷叫mysql
-v ./mysql:/var/lib/mysql 会被识别为当前目录下的mysql目录
- 挂载 /root/mysql/data到容器内的/var/lib/mysql目录
- 挂载 /root/mysql/init到容器内的/docker-entrypoint-initdb.d目录(初始化的SQL脚本目录)
- 挂载 /root/mysql/conf到容器内的/etc/mysql/conf.d目录(这个是MySQL配置文件目录)
这样做的好处,当这个mysql容器关闭之后(理论上容器mysql的相关数据都被删除,但本地目录中的还在),再次创建一个新的mysql容器时(并指定与本地目录挂载),mysql中的数据依然存在。
自定义镜像
Dockerfile
Dockerfile是一个文本文件,其中包含一个个的指令,用 指令来说明要执行什么操作来构建镜像,将来Docker可以根据Dockerfile帮我们构建镜像。
Dockerfile构建过程
dockerfile的关键字建议使用大写,它是从上往下按照循序执行的,在dockerfile中,#代表注释。我们可以通过这个脚本来生成镜像,脚本中的每一个命令,都是一层镜像。
我们先通过下面这张图片来理解一下镜像的构成以及运行过程。
在这里我们来整理一下docker容器、dockerfile、docker镜像的关系:
- dockerfile是面向开发的,发布项目做镜像的时候就要编写dockerfile文件。
- dockerfile:构建文件,定义了一切的步骤,源代码。
- dockerImanges:通过dockerfile构建生成的镜像,最终发布和运行的产品。
- docker容器:容器就是镜像运行起来提供服务的。
构建镜像
编写好Dockerfile后,可以(在jar包所在目录下)用下面命令来构建镜像,将jar包构建成一个镜像)
容器网络互联
docker安装后,会在宿主机内创建一个虚拟网卡docker0,所有容器默认通过桥接连接到这个网卡,不同容器属于一个网段,相互之间可以传输信息。
由于容器的ip地址每次启动会发生改变,所以默认写死ip地址会造成问题。
解决方案:ip地址会变但容器名不会变,让容器之间通过容器名互相访问,加入自定义网络的容器才可以通过容器名互相访问,Docker的网络操作命令如下:
启动容器时,使用 --network 网桥名,指定容器使用的网桥(可以替换默认网桥)
DockerCompose
通过一个单独的docker-compose.yml模版文件(yaml格式)来定义一组相关联的应用容器,帮助我们实现多个相互关联的Docker容器的快速部署。这个模版文件中定义了容器的一系列配置,包括端口映射、环境变量、数据卷挂载、网桥、指定dockerfile(来构建镜像)等信息,可以利用DockerCompose文件进行一键部署
左侧是docker run命令,右侧是docker-compose.yml