一、Docker 简介
1.1 应用部署的环境问题
在 Java 大型项目的开发中,微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。
可以说项目越大,运行环境也越复杂,部署时就会遇到:
- 依赖关系复杂,容易出现兼容性问题
- 比如:分布式系统中,依赖的组件非常多,不同组件之间部署时往往会产生一些冲突。
- 开发、测试、生产环境的差异,不同机器硬件导致的差异
- 比如:在数百上千台服务中重复部署,环境不一定一致,会遇到各种问题
如上图:一个项目中,部署时需要依赖于 node.js、Redis、RabbitMQ、MySQL 等,这些服务部署时所需要的函数库、依赖项各不相同,甚至会有冲突,给部署带来了极大的困难。
1.2 Docker 如何解决问题
1.2.1 解决依赖兼容
Docker 为了解决依赖的兼容问题的,采用了两个手段:
- 将应用的 Libs(函数库)、Deps(依赖)、配置与应用一起打包
- 将每个应用放到一个隔离容器去运行,避免互相干扰
这样打包好的应用包中,既包含应用本身,也包含应用所需要的 Libs、Deps,无需再操作系统上安装这些,自然就不存在不同应用之间的兼容问题了。
虽然解决了不同应用的兼容问题,但是开发、测试等环境会存在差异,操作系统版本也会有差异,怎么解决这些问题呢?
1.2.2 解决操作系统环境
要解决不同操作系统环境差异问题,必须先了解操作系统结构。以一个 Ubuntu 操作系统为例,结构如下:
结构包括:
- 计算机硬件:例如 CPU、内存、磁盘等
- 系统内核:内核可以与计算机硬件交互,对外提供内核指令,用于操作计算机硬件。
- 系统应用:操作系统本身提供的应用、函数库。这些函数库是对内核指令的封装,使用更加方便。
而应用于计算机交互的流程如下:
- 应用调用操作系统应用(函数库),实现各种功能
- 系统函数库是对内核指令集的封装,会调用内核指令
- 内核指令操作计算机硬件
举例:Ubuntu 和 CentOS 都是基于 Linux 内核,无非是系统应用不同,提供的函数库有差异,那如果将一个Ubuntu 版本的 MySQL 安装到 CentOS 上,MySQL 在调用 Ubuntu 函数库时,会发现找不到或者不匹配,如图:
那 Docker 如何解决不同系统环境的问题?
- Docker 将用户程序与所需要调用的系统(比如 Ubuntu)函数库一起打包
- 当 Docker 运行到不同操作系统时,直接基于打包的函数库,借助于操作系统的 Linux 内核来运行,可以说屏蔽了操作系统
1.3 什么是 Docker
Docker 是一个开源的应用容器引擎,诞生于 2013 年初,基于 Go 语言实现,dotCloud 公司出品
Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。而容器是完全使用沙箱机制,相互隔离,容器性能开销极低。
Docker 从 17.03 版本之后分为 CE(Community Edition:社区版)免费,支持周期 7 个月和 EE(Enterprise Edition:企业版)强调安全,付费使用,支持周期 24 个月。
Docker 通俗的讲,是服务器中高性能的虚拟机,可以将一台物理机虚拟 N 多台虚拟机的机器,互相之间隔离,互不影响。
特点:
- 容器将应用打包成标准化单元,用于交付、部署;
- 容器及包含了软件运行所需的所有环境,而且非常轻量级
- 容器化的应用程序,可以在任何 Linux 环境中始终如一的运行
- 容器化的应用程序,具备隔离性,这样多团队可以共享同一 Linux 系统资源
1.4 虚拟化技术
虚拟化(Virtualization)是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍,使用户可以比原本的组态更好的方式来应用这些资源。
这些资源的核心虚拟部分是不受现有资源的架设方式,低于或者物理组态所限制,一般所指的虚拟化资源包括计算能力和资料存储。
虚拟化技术主要作用:
- 高性能的物理硬件产能过剩和老的硬件产能过低问题
- 软件跨环境迁移问题(代码的水土不服)
1.5 容器与虚拟机比较
Docker 可以让一个应用在任何操作系统中非常方便的运行。而以前我们接触的虚拟机,也能在一个操作系统中,运行另外一个操作系统,保护系统中的任何应用。两者有什么差异呢?
虚拟机(virtual machine):是在操作系统中模拟硬件设备,然后运行另一个操作系统,比如在 Windows 系统里面运行 Ubuntu 系统,这样就可以运行任意的 Ubuntu 应用了。
Docker:仅仅是封装函数库,并没有模拟完整的操作系统
下面的图片比较了 Docker 和传统虚拟化方式的不同之处,总的来说:Docker 是操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而虚拟机则是在硬件层面实现虚拟化。
特性 | Docker | 虚拟机 |
---|---|---|
隔离级别 | 进程级 | 操作系统级 |
隔离策略 | 直接运行在宿主机内核中 | 运行于 Hypervisor 上 |
启动速度 | 秒级 | 分钟级 |
占用磁盘空间 | 一般为MB | 一般为GB |
性能 | 接近原生硬件 | 弱鸡 |
系统支持量 | 单机可跑几十个容器 | 单机几个虚拟OS |
运行环境 | 主要在 Linux | 主要在 Windows |
相同:Docker 和虚拟机都是虚拟化技术,具备资源隔离和分配优势
不同:
- Docker 虚拟化的是操作系统,而虚拟机虚拟化的是硬件
- Docker 它是操作系统上的一个系统进程,而虚拟机它是在操作系统中的操作系统
1.6 Docker 概念与架构
1.6.1 Docker 基本概念
宿主机:安装 Docker 守护进程的 Linux 服务器,称之为宿主机;
镜像(Image):Docker 将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像。
容器(Container):镜像运行之后的实体,镜像和容器的关系,就像是面向对象程序设计中的类和对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等,也可以有多个。
例如:你下载了一个 QQ,如果我们将 QQ 在磁盘上的运行文件及其运行的操作系统依赖打包,形成 QQ 镜像。然后你可以启动多次,双开、甚至三开 QQ,跟多个妹子聊天。
仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。类似 Maven 的中央仓库
Docker 官方镜像的托管平台叫 Docker Hub,我们可以将自己的镜像共享到 Docker Hub,也可以从 Docker Hub 上拉取镜像。同时国内也有类似于 Docker Hub 的公开服务,比如网易云镜像服务、阿里云镜像库等。
1.6.2 Docker 基本架构
Docker是一个 C/S 结构的程序,由两部分组成:
-
服务端(Server):Docker 的守护进程,负责处理 Docker 指令,管理镜像和容器等等
-
客户端(Client):通过命令或 RestAPI 向 Docker 服务端发送指令。可以在本地或远程向服务端发送指令。
二、Docker 安装与启动
安装之前进行虚拟机网卡配置:IP 地址 192.168.88.101、网关为 192.168.88.2
2.1 安装
Docker 官方建议在 Ubuntu 中安装,因为 Docker 是基于 Ubuntu 发布的,而且一般 Docker 出现的问题 Ubuntu 是最先更新或者打补丁的。在很多版本的 CentOS 中是不支持更新最新的一些补丁包的。
由于我们学习的环境都使用的是 CentOS,因此这里我们将 Docker 安装到 CentOS 上。
注意:这里建议安装在 CentOS 7.x 以上的版本!其他更多安装参考官网:https://docs.docker.com/get-docker/
0)如果之前安装过旧版本的Docker,可以使用下面命令卸载
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
docker-ce
1)yum 包更新到最新
sudo yum -y update
2)安装需要的软件包, yum-util 提供 yum-config-manager 功能,另外两个是 devicemapper 驱动依赖的
sudo yum install -y yum-utils
3)设置 yum 源为阿里云
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo yum makecache fast
4)安装 docker
sudo yum -y install docker-ce
5)安装后查看 docker 版本
docker -v
6)通过运行 hello-world 来验证是否正确安装了 Docker
docker run hello-world
2.2 Docker 守护进程相关命令
Docker应用需要用到各种端口,逐一去修改防火墙设置。非常麻烦,因此建议大家直接关闭防火墙!
# 关闭
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
操作 | 指令 |
---|---|
启动 Docker | systemctl start docker |
停止 Docker | systemctl stop docker |
重启 Docker | systemctl restart docker |
查看 Docker 状态 | systemctl status docker |
设置开机启动 | systemctl enable docker |
查看 Docker 概要信息 | docker info |
查看 Docker 帮助文档 | docker --help |
如下图 docker info 展示信息
2.3 镜像加速
默认情况,将从Docker Hub(https://hub.docker.com)下载镜像,因为太慢,一般都会配置镜像加速器;
建议配置阿里云镜像加速,可以自己从阿里云上申请!必须要注册,每个人分配一个免费的 Docker 镜像加速地址,速度杠杠的~,配置完成记得刷新配置
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://6wgh41zg.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
三、Docker 中常用命令
3.1 镜像相关命令
首先来看下镜像的名称组成:
- 镜名称一般分两部分组成:[repository]:[tag]
- 在没有指定tag时,默认是latest,代表最新版本的镜像
这里的 MySQL 就是 repository,5.7就是tag,合一起就是镜像名称,代表5.7版本的MySQL镜像。
而常见的镜像操作命令如图:
3.1.1 搜索镜像
如果你需要从网络中查找需要的镜像,可以通过以下命令搜索,注意:必须确保当前系统能联网,当然最好的方式还是在 Docker Hub 上进行搜索查找
docker search 镜像名称
3.1.2 拉取镜像
拉取镜像:从Docker仓库下载镜像到本地,镜像名称格式为名称:版本号,如果版本号不指定则是最新的版本。如果不知道镜像版本,可以去 Docker Hub 搜索对应镜像查看。
docker pull 镜像名称
例如,我要下载 nginx 最新的镜像:docker pull nginx
3.1.3 查看镜像
查看本地所有镜像
docker images
这些镜像都是存储在 Docker 宿主机的 /var/lib/docker 目录下
3.1.4 删除镜像
如果该镜像正在使用(创建了容器),则先删除容器再删除镜像
docker rmi 镜像ID # 按镜像ID删除镜像
docker rmi `docker images -qa` # 删除所有镜像
3.2 容器相关命令
有镜像才能创建容器,这是根本前提
容器的三个状态:
- 运行:进程正常运行
- 暂停:进程暂停,CPU不再运行,并不释放内存
- 停止:进程终止,回收进程占用的内存、CPU等资源
其中:
- docker run:创建并运行一个容器,处于运行状态
- docker pause:让一个运行的容器暂停
- docker unpause:让一个容器从暂停状态恢复运行
- docker stop:停止一个运行的容器
- docker start:让一个停止的容器再次运行
- docker rm:删除一个容器
3.2.1 查看容器
查看正在运行的容器:docker ps
查看所有容器:docker ps –a
查看最后一次运行的容器:docker ps –l
查看停止的容器:docker ps -f status=exited
查看容器日志:docker logs 容器ID
3.2.2 创建与启动容器
创建容器命令:
docker run
创建容器常用的参数说明:
# OPTIONS说明(常用):有些是一个减号,有些是两个减号
--name:为创建的容器命名。
-d:在run后面加上-d参数,会创建一个守护式容器在后台运行(这样创建容器后不会自动登录容器)。
-i:以交互模式运行容器,通常与 -t 同时使用
-t:表示容器启动后会进入其命令行。为容器重新分配一个伪输入终端,通常与 -i 同时使用
-p: 表示端口映射,前者表示宿主机端口,后者是容器内的映射端口,可以使用多个-p做多个端口映射
--restart=always:可以让容器开机自启
1)交互式容器
docker run -it --name=容器名称 镜像名称:标签 /bin/bash
# 举例:/bin/bash的作用是因为docker后台必须运行一个进程,否则容器就会退出,在这里表示启动容器后启动bash。
docker run -it --name=mycentos centos:7 /bin/bash
这时我们通过ps命令查看,发现可以看到启动的容器,状态为启动状态
退出当前容器
exit
2)守护式容器
什么是守护式容器:能够长期运行、 没有交互式会话、 适合运行应用程序和服务
docker run -d --name=容器名称 镜像名称:标签
# 举例:
docker run -d --name=nginx1 nginx:latest
3)进入容器
docker exec -it 容器名称 (或者容器ID) /bin/bash
# 举例:
docker run -it nginx1 /bin/bash
注意:这里的登陆容器之后执行的脚本 /bin/bash 必须写
4)端口的注意点
默认情况下,容器是隔离环境,我们直接访问宿主机的 80 端口,肯定访问不到容器中的 nginx。
所以我们需要将容器的 80 端口与宿主机的 80 端口关联起来,当我们访问宿主机的 80 端口时,就会被映射到容器的 80,这样就能访问到 nginx 了:
3.2.3 停止与启动容器
操作 | 指令 |
---|---|
启动容器 | docker start 容器名称(或者容器ID) |
停止容器 | docker stop 容器名称(或者容器ID) |
重启容器 | docker restart 容器名称(或者容器id) |
强制停止容器 | docker kill 容器名称(或者容器id) |
3.2.4 文件拷贝
如果我们需要将文件拷贝到容器内可以使用cp命令
docker cp 宿主机需要拷贝的文件或目录 容器名称:容器目录
# 举例:
docker cp /etc/hosts nginx1:/tmp
也可以将文件从容器内拷贝出来
docker cp 容器名称:容器目录 宿主机存放的文件或目录
# 举例:
docker cp nginx1:/usr/share/nginx/html/index.html /tmp
3.2.5 查看容器 IP 地址
我们可以通过以下命令查看容器运行的各种数据
docker inspect 容器名称(容器ID)
# 举例:
docker inspect nginx1
也可以直接执行下面的命令直接输出IP地址
docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称(容器ID)
# 举例:
docker inspect --format='{{.NetworkSettings.IPAddress}}' nginx1
3.2.6 删除容器
删除指定的容器
docker rm 容器名称(容器ID) # 无法删除运行的容器
docker rm `docker ps -qa` # 删除所有容器
docker rm -f 容器名称(容器ID) # 慎用:可以删除正在运行的容器
3.3 迁移与备份
1)镜像备份
我们可以通过以下命令将镜像保存为 tar 文件
docker save -o {镜像的备份文件} {镜像名称}
# 举例:-o表示输出到的文件
docker save -o nginx.tar nginx:latest
2)镜像恢复与迁移
我们先删除掉 nginx:latest 镜像:docker rmi nginx:latest
,然后再进行恢复
docker load -i {备份的镜像文件}
# 举例:-i表示指定导入的文件
docker load -i nginx.tar
3)容器保存为镜像
我们可以通过以下命令将容器保存为镜像
docker commit [OPTIONS] 容器名称 [镜像名称[:TAG]]
# 举例,不指定为latest
docker commit nginx1 test_nginx
docker images # 查看
四、Docker 数据卷
4.1 什么是数据卷
由于每个容器是互相隔离的,那么就会产生以下的问题:
Docker 容器删除之后,在容器中产生的数据还在吗?
Docker 容器和外部机器可以直接交换文件吗?
容器之间怎么进行数据交互?
要解决以上问题,就需要将数据与容器解耦,这就要用到数据卷了。
数据卷(volume):是一个虚拟目录,指向宿主机文件系统中的某个目录。当容器目录和数据卷目录绑定后,对方的修改自然会立即同步;一个数据卷可以被多个容器同时挂载,一个容器也可以挂载多个数据卷。
4.2 数据卷基本命令
数据卷操作的基本语法如下:
docker volume [COMMAND]
其中 docker volume 命令是数据卷操作,根据命令后跟随的 command 来确定下一步的操作:
create # 创建一个volume
ls # 列出所有的volume
inspect # 显示一个或多个volume的信息
prune # 删除未使用的volume
rm # 删除一个或多个指定的volume
使用上面命令演示:创建一个数据卷,并查看数据卷在宿主机的目录位置,然后删除它
# 1、创建数据卷
docker volume create html
# 2、查看所有数据卷
docker volume ls
# 3、查看数据卷的详细信息
docker volume inspect html
# 可以看到,我们创建的html这个数据卷关联的宿主机目录为/var/lib/docker/volumes/html/_data目录。
[
{
"CreatedAt": "2021-11-11T16:52:34+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/html/_data",
"Name": "html",
"Options": {},
"Scope": "local"
}
]
# 4、删除html的数据卷
docker volume rm html
# 5、删除所有未使用的数据卷
docker volume prune
4.3 挂载数据卷
我们在创建容器时,可以通过 -v 参数来挂载一个数据卷到某个容器内目录
1)1个容器挂载1个数据卷
创建启动容器时,使用 –v 参数设置数据卷,冒号前面是宿主机目录,冒号后是容器目录
注意事项:如果宿主机目录不存在,会自动创建,可以挂载多个数据卷
docker run ... –v 宿主机目录(文件):容器内目录(文件) ...
案例:创建一个 nginx 容器,挂载容器内的 html 目录到默认目录
# 1、创建容器并挂载数据卷到容器内的HTML目录
docker run -d --name=nginx2 -v html:/usr/share/nginx/html -p 80:80 nginx
# 2、查看html数据卷的位置
docker volume inspect html
# 3、进入html数据卷所在位置,并修改HTML内容
cd /var/lib/docker/volumes/html/_data
# 修改文件,保存,然后刷新网页就可以看到效果了
vi index.html
2)1个容器挂载多个数据卷
我们可以通过以下命令,挂载多个数据卷,其实就是多个 -v 参数
docker run -d --name=nginx3 -v /tmp/data1:/data1 -v /tmp/data2:/data2 nginx
3)多个容器挂载1个数据卷
方式一:多个容器挂载1个数据卷,实现数据共享,2个容器都挂载到/tmp/data_common
下
docker run -d --name=nginx4 -v /tmp/data_common:/data4 nginx
docker run -d --name=nginx5 -v /tmp/data_common:/data5 nginx
方式二:多个容器挂载1个容器(这个容器挂载1个数据卷)
# 创建启动 c3 数据卷容器,使用 –v 参数设置数据卷,激素创建一个容器,挂载一个目录,让其他容器继承自该容器
docker run -d --name=c3 -v /tmp/data_container:/c3 nginx
# 创建启动 c1 c2 容器,使用 –-volumes-from 参数设置数据卷
docker run -d --name=c1 --volumes-from c3 nginx
docker run -d --name=c2 --volumes-from c3 nginx
五、在 Docker 中部署应用
熟悉基本应用部署的使用
5.1 端口映射
开启 IP 转发,参考:https://www.cnblogs.com/sfnz/p/6555723.html
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf
# 重启network和docker服务
systemctl restart network && systemctl restart docker
# 查看是否修改成功 (备注:返回1,就是成功)
sysctl net.ipv4.ip_forward
5.2 MySQL 部署
- 拉取MySQL镜像
docker pull mysql:8.0.27
- 创建容器、设置端口映射、设置数据卷
docker run -d --name=mysql1 -p 3307:3306 \
-v /opt/mysql/logs:/logs \
-v /opt/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0.27
# 参数说明:
-p 3307:3306:将容器的 3306 端口映射到宿主机的 3307 端口。
-v /opt/mysql/logs:/logs:将主机目录(/opt/mysql/logs)挂载到容器中的 /logs 日志目录
-v /opt/mysql/data:/var/lib/mysql:将主机目录(/opt/mysql/data)挂载到容器的 /var/lib/mysql 数据目录
-e MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码。
- 进入容器,操作mysql
docker exec -it mysql1 mysql -uroot -p123456
# 修改远程访问
GRANT ALL ON *.* TO 'root'@'%';
- 使用 Navicat、DataGrip 等工具连接容器中的 MySQL
5.3 Tomcat 部署
- 拉取 Tomcat 镜像
docker pull tomcat
- 创建容器,设置端口映射、目录映射
docker run -d --name=tomcat1 -p 8080:8080 -v /opt/tomcat/webapps:/usr/local/tomcat/webapps tomcat
# 参数说明:
-p 8080:8080:将容器的8080端口映射到主机的8080端口
-v /opt/tomcat/webapps:/usr/local/tomcat/webapps:将主机目录(/opt/tomcat/webapps)挂载到容器webapps
-
向 Tomcat 中部署服务
-
使用外部机器访问 Tomcat,测试部署服务
5.4 Redis 部署
- 拉取 Redis 镜像
docker pull redis:latest
- 创建容器,设置端口映射
docker run -d --name=redis1 -p 6379:6379 redis:latest
- 进入容器,使用 redis-cli
docker exec -it redis1 redis-cli
- 使用外部机器连接 Redis,测试
六、制作 Docker 镜像
常见的镜像在 Docker Hub 就能找到,但是我们自己写的项目就必须自己构建镜像了。而要自定义镜像,就必须先了解镜像的结构才行。
6.1 镜像结构
镜像就是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。以MySQL为例看看镜像的组成结构:
简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件。我们要构建镜像,其实就是实现上述打包的过程。
6.2 镜像原理(重要)
Docker镜像都是只读的, 当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称作“容器层”, “容器层”之下的都叫“镜像层”。
6.2.1 UnionFS(联合文件系统)
UnionFS(联合文件系统): Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。
Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
UnionFS 的特性:可以一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
6.2.2 镜像加载原理
Docker 镜像本质是什么?
Docker 中一个 CentOS 镜像为什么只有200MB,而一个 CentOS 操作系统的 ISO 文件要几个个G?
Docker 中一个 Tomcat 镜像为什么有500MB,而一个 Tomcat 安装包只有70多MB?
在下载镜像的过程中我可以看到 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等标准目录和文件。不同的 Linux 发行版,bootfs 基本一样,而 rootfs 不同,如 Ubuntu、CentOS 等。
1)Docker 镜像本质是什么?
- 是一个分层文件系统,层层叠加
2)Docker 中一个 CentOS 镜像为什么只有 200MB,而一个 CentOS 操作系统的 ISO 文件要几个 G?
- 因为 Docker 的 CentOS 镜像复用了操作系统的 bootfs,自己只需要提供 rootfs 就行了,而对于一个精简的OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了
3)Docker 中一个 Tomcat 镜像为什么有 500MB,而一个 Tomcat 安装包只有 10 多 MB?
- 因为 Docker 中镜像是分层的,Tomcat 虽然只有 10 多 MB,但它也需要依赖于父镜像和基础镜像,所有整个对外暴露的 Tomcat 镜像大小是 500 多 MB
4、为什么 Docker 镜像采用分层结构?
- 最大的一个好处就是共享资源,比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需保存一份 base 镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。可以说镜像中的每一层都可以被共享。
6.3 Dockerfile 简介
构建自定义的镜像时,并不需要一个个文件去拷贝,打包。
我们只需要告诉 Docker,我们的镜像的组成,需要哪些 BaseImage、需要拷贝什么文件、需要安装什么依赖、启动脚本是什么,将来 Docker 会帮助我们构建镜像。而描述上述信息的文件就是 Dockerfile 文件。
可以说 Dockerfile 就是一个文本文件,包含了构建镜像文件的指令,用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层 Layer,最终构建出一个新的镜像;
- 对于开发人员:可以为开发团队提供一个完全一致的开发环境
- 对于测试人员:可以直接拿开发时所构建的镜像或者通过 Dockerfile 文件构建一个新的镜像,直接开始工作
- 对于运维人员:在部署时,可以实现应用的无缝移植
6.3.1 Dockerfile 构建过程解析
1)Dockerfile 内容基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
2)Docker 执行 Dockerfile 大致流程
- Docker 从基础镜像运行一个容器
- 从上到下执行指令,每执行一条指令就对容器作出修改
- 执行类似 Docker Commit 的操作提交一个新的镜像层
- Docker 再基于刚提交的镜像运行一个新容器
- 不断重复 2 ~ 4 的操作,直到所有指令都执行完成
3)从应用软件的角度来看,Dockerfile、Docker 镜像、Docker 容器分别代表软件的三个不同阶段
- Dockerfile 是软件的原材料
- Dockerfile 定义了进程需要的一切东西。包括执行文件、环境依赖、动态链接库、服务进程和内核进程等
- Docker 镜像是软件的交付品
- 在用 Dockerfile 定义一个文件后,docker build 时会产生一个 Docker 镜像
- Docker 容器则可以认为是软件的运行状态
- 当运行 Docker 镜像时,就会启动一个容器来提供服务
可以理解为 Dockerfile 面向开发,Docker 镜像成为交付标准,Docker 容器则涉及部署与运维,三者缺一不可,合力充当 Docker 体系的基石。
6.3.2 Dockerfile 语法
更多语法说明,参考官网文档:https://docs.docker.com/engine/reference/builder
指令示例 | 作用 |
---|---|
FROM image_name:tag | 定义了使用哪个基础镜像启动构建流程 |
LABEL key value | 声明我们需要各种元数据,比如版本、作者、描述等等(可以写多条) |
ENV key value | 设置环境变量(可以写多条) |
RUN command | 执行一段命令默认是/bin/sh,也是Dockerfile的核心部分(可以写多条) |
COPY source_dir/file dest_dir/file | 将宿主机文件复制到容器内,如果有压缩文件并不能解压 |
ADD source_dir/file dest_dir/file | 与COPY类似,但如果是压缩文件会在复制后自动解压 |
WORKDIR path_dir | 设置工作目录,如果没有会自动创建 |
EXPOSE 8080/udp | 运行时监听的端口, 启动容器的时候使用 -p 绑定 |
ENTRYPOINT command | 镜像中应用的启动命令,在容器运行时调用,只有最后一个会生效 |
6.4 制作 Docker 镜像
发布 SpringBoot 项目到 Docker 容器中
实现过程:
1)编辑 Dockerfile 文件
# 定义基础镜像
FROM java:8-alpine
# 添加jar包文件到镜像中
COPY ./springboot.jar /opt/app.jar
# 暴露端口
EXPOSE 8080
# 定义当前镜像启动容器时,执行命令
ENTRYPOINT java -jar /opt/app.jar
2)在宿主机中,构建镜像
docker bulid –t {镜像名称:版本} {构建文件所在路径}
# 举例:注意后面的空格和点,不要省略,点表示当前目录
docker build -t boot:1.0 ./
# -t:镜像的名字及标签,通常 name:tag 或者 name 格式,不设置为latest
3)基于镜像,启动容器
docker run -d --name=boot1 -p 9001:8080 boot:1.0