Docker基础
本文基于Ubuntu系统演示
入门
Docker 是一个开源的容器化平台,它允许开发者将应用程序及其依赖项打包到一个标准化的单元(称为 “容器”)中,从而确保应用在任何支持 Docker 的环境中都能以相同的方式运行
比如,在不使用Docker的情况下,想要安装MySQL比较麻烦,而使用Docker之后,只需要一个指令即可(记得先启动Docker)
|
|
docker run:创建并运行一个容器,-d是让容器在后台运行,不占用终端
--name mysql:给容器起个名字,必须唯一
-p 3307(宿主机端口):3306(容器端口):设置端口映射(在外部无法直接访问到容器,需要通过映射到宿主机的端口才能访问到容器)
-e KEY=VALUE:设置环境变量(KEY和VALUE的取值可以在Docker官网查看)
mysql(镜像名):8(版本)指定运行的镜像的名字、版本(不指定版本默认用最新版)
而且Docker创建的容器之间是相互隔离,互不影响的,可以再开一个MySQL试试
|
|
此时可以在外部分别连接端口号为3307的mysql和端口号为3308mysql01,如果在mysql01中进行新建一个库或表等操作,3307的mysql中是查询不到,不受影响的
常见指令
| 命令 | 说明 | 文档地址 |
|---|---|---|
| docker pull | 拉取镜像 | docker pull |
| docker push | 推送镜像到DockerRegistry | docker push |
| docker images | 查看本地镜像 | docker images |
| docker rmi | 删除本地镜像 | docker rmi |
| docker run | 创建并运行容器(不能重复创建) | docker run |
| docker stop | 停止指定容器 | docker stop |
| docker start | 启动指定容器 | docker start |
| docker restart | 重新启动容器 | docker restart |
| docker rm | 删除指定容器 | docs.docker.com |
| docker ps | 查看容器 | docker ps |
| docker logs | 查看容器运行日志 | docker logs |
| docker exec | 进入容器 | docker exec |
| docker save | 保存镜像到本地压缩文件 | docker save |
| docker load | 加载本地压缩文件到镜像 | docker load |
| docker inspect | 查看容器详细信息 | docker inspect |

指令详见官方文档: https://docs.docker.com/
案例演示
**案例:**查看DockerHub,拉取Nginx镜像,创建并运行Nginx容器
需求:
- 在DockerHub中搜索Nginx镜像,查看镜像的名称
- 拉取Nginx镜像
- 查看本地镜像列表
- 创建并运行Nginx容器
- 查看容器
- 停止容器
- 再次启动容器
- 进入Nginx容器
- 删除容器
演示:
1.找一个Nginx版本并拉取镜像:
如果直接复制粘贴进去提示
1permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/images/create?fromImage=docker.io%2Flibrary%2Fnginx&tag=stable-perl": dial unix /var/run/docker.sock: connect: permission denied可以试着在带代码前面加个
sudo
2.查看本地镜像列表:docker images

3.如果无法科学上网,镜像可能会下载得很慢,这时可以让有条件的人下载后打包为tar文件分享过来,接下来几步演示如何打包为tar文件,以及如何使用这个文件
打包为tar文件:docker save -o nginx-stable-perl.tar nginx:stable-perl
输入此命令后会把本地镜像打包到当前目录
4.将打包好的tar文件内容加载到本地镜像
为了演示此处先删除刚刚下载好的Nginx镜像

加载tar文件:docker load -i nginx-stable-perl.tar

5.创建容器:docker run -d --name nginx -p 80:80 nginx:stable-perl

6.查看容器:docker ps

此时就可以直接在Windows的浏览器中输入Linux主机的ip地址访问到Nginx页面了(http默认端口号是80所以可以不用输入端口号)
7.停止容器:docker stop nginx

8.再次启动Nginx容器:docker start nginx

9.进入Nginx容器并开启一个交互的bash终端:docker exec -it nginx bash
docker exec:用于在 已经运行的容器 内部执行命令
it:
-
-i(interactive):保持标准输入(STDIN)打开,确保能与容器内的进程交互 -
-t(tty):分配一个伪终端(模拟终端环境),让操作更像本地终端
两者结合实现了 “交互式终端” 的效果,缺一不可(否则无法正常输入命令)
bash:要在容器内执行的命令,即启动 Bash 解释器(Linux 系统中常用的命令行 shell)
如果是启动sh终端就是:docker exec -it nginx sh

10.退出容器回到宿主机:exit

11.删除容器:docker rm nginx
先stop容器然后删除,或是直接强制删除正在运行的容器:docker rm -f nginx

数据卷挂载
数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁
以Nginx为例,Nginx中有两个关键的目录:
html:放置一些静态资源conf:放置配置文件
如果要让Nginx代理静态资源,最好是放到html目录;如果要修改Nginx的配置,最好是找到conf下的nginx.conf文件
容器运行的Nginx所有的文件都在容器内部,但容器内部缺少很多工具,使得操作不便
所以利用数据卷将两个目录与宿主机目录关联,方便操作

在上图中:
- 创建了两个数据卷:
conf、html - Nginx容器内部的
conf目录和html目录分别与两个数据卷关联。 - 而数据卷conf和html分别指向了宿主机的
/var/lib/docker/volumes/conf/_data目录和/var/lib/docker/volumes/html/_data目录
容器内的conf和html目录就与宿主机的conf和html目录关联起来,称为挂载
常见命令:
| 命令 | 说明 | 文档地址 |
|---|---|---|
| docker volume create | 创建数据卷 | docker volume create |
| docker volume ls | 查看所有数据卷 | docs.docker.com |
| docker volume rm | 删除指定数据卷 | docs.docker.com |
| docker volume inspect | 查看某个数据卷的详情 | docs.docker.com |
| docker volume prune | 清除数据卷 | docker volume prune |
容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的
创建容器的过程中,如果发现挂载的数据卷不存在,则会自动创建
案例演示
演示内容:Nginx的html目录挂载
1.创建容器并指定数据卷,注意通过使用-v 数据卷:容器内目录来指定数据卷
|
|
容器内目录的位置可以查看Docker官网:https://hub.docker.com/

2.查看数据卷
|
|

发现除了刚刚创建的html外,还有别的匿名数据卷
这个匿名数据卷其实是之前创建mysql容器时自动创建的,为了保存mysql中的数据,这样及时是把mysql容器删除了,但数据还在
之后创建新的mysql容器时就可以挂载这个数据卷,从而快速恢复之前的数据
3.查看数据卷详情
|
|

其中Mountpoint就是映射的磁盘目录
4.查看映射目录的内容
|
|

发现内容确实是对应容器目录内的文件
5.进入该目录,并随意修改index.html内容
|
|
6.在Windows的浏览器中输入Linux服务器的ip地址

发现页面确实被成功修改了,证明在宿主机对容器内映射文件进行的操作会同步到容器中的文件内(如果在宿主机把映射目录内的文件删除,容器内的文件也会同步被删除)
本地目录挂载
数据卷的目录结构较深,如果去操作数据卷目录会不太方便
所以也可以直接将容器目录与宿主机指定目录挂载,不使用数据卷
|
|
- 目录只能挂载目录,文件只能挂载文件
- 本地目录或文件必须以
/或./开头,如果直接以名字开头,会被识别为数据卷名而非本地目录名
自定义镜像
前面一直在使用别人准备好的镜像,那如果我要部署一个Java项目,把它打包为一个镜像该怎么做呢? 接下来就来介绍如何自定义镜像
镜像结构
要想自己构建镜像,必须先了解镜像的结构
镜像之所以能快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖
因此,自定义镜像本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成
举个例子,要从0部署一个Java应用,大概流程是这样:
- 准备一个linux服务
- 安装并配置JDK
- 上传Jar包
- 运行jar包
那因此,打包镜像也是分成这么几步:
- 准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可)
- 安装并配置JDK
- 拷贝jar包
- 配置启动脚本
上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合
但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer(层),这样的好处是,如果在构建时用到的某些层其他人已经制作够,就可以直接拷贝使用这些层,而不用重复制作,也就是便于复用
例如,第一步中需要的Linux运行环境,通用性就很强,所以Docker官方就制作了这样的只包含Linux运行环境的镜像。在制作java镜像时,就无需重复制作,直接使用Docker官方提供的CentOS或Ubuntu镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个Java项目的镜像结构如图所示:

Dockerfile
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。将来Docker可以根据Dockerfile构建镜像
常用指令:
| 指令 | 说明 | 示例 |
|---|---|---|
| FROM | 指定基础镜像 | FROM ubuntu:22.04 |
| ENV | 设置环境变量,可在后面指令使用 | ENV key value |
| COPY | 拷贝本地文件到镜像的指定目录 | COPY ./xx.jar /tmp/app.jar |
| RUN | 执行Linux的shell命令,一般是安装过程的命令 | RUN yum install gcc |
| EXPOSE | 指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
| ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
更多指令见官方文档:Dockerfile reference | Docker Docs
例如,要基于 ubuntu:22.04 镜像来构建一个Java应用,其Dockerfile内容如下:
|
|
Dockerfile文件编写好了之后,就可以使用如下命令来构建镜像了
|
|
-t:是给镜像起名,格式依然是repository:tag的格式,不指定tag时,默认为latest.:是指定Dockerfile所在目录,如果就在当前目录,则指定为"."
演示
准备好打包好的jar包、Dockerfile文件、需要的jdk后即可开始创建镜像

网络
默认情况下,所有容器都是以bridge方式连接到Docker的一个虚拟网桥上,所以容器之间是可以互相访问的
演示:
1.用基本命令,寻找Networks.bridge.IPAddress属性
|
|
发现mysql容器的ip地址是172.17.0.3
2.进入其他容器中通过ping命令尝试能否连接
|
|
如果提示
bash: ping: command not found则说明容器中没有ping命令,可以手动安装一下
1 2 3 4# 先更新包索引(可选,但建议执行) apt update # 安装ping工具(属于iputils-ping包) apt install -y iputils-ping
安装成功后再次使用ping命令尝试连接,发现确实可以连接上

但是,容器中的ip是自动分配的,也就是说关闭容器后再打开ip可能会变,如果在开发时把ip写死,可能会连接失败
自定义网络
上述问题可以使用自定义网络解决
自定义网络的解决方式就是使用Docker指令自己创建一个虚拟网卡,将创建的容器全部连接在自己创建的虚拟网卡中,这样容器之间就可以直接使用名字来建立通信
常见命令:
| 命令 | 说明 |
|---|---|
| docker network create | 创建一个网络 |
| docker network ls | 查看所有网络 |
| docker network rm | 删除指定网络 |
| docker network prune | 清除未使用的网络 |
| docker network connect | 使指定容器连接加入某网络 |
| docker network disconnect | 使指定容器连接离开某网络 |
| docker network inspect | 查看网络详细信息 |
演示
1.创建一个网络
|
|
2.让mysql和myapp两个容器都加入刚刚创建的网络
|
|
检查后发现确实正常加入了自定义的网络
在创建容器时也可以指定默认网络
1 2 3docker run -d --name 容器名 -p 宿主机端口:容器端口 --network 网络名称 镜像名:版本 # 示例 docker run -d --name myapp -p 8080:8080 --network yuanyu app:1.0
3.使用容器名进行通信
|
|
发现可以使用名字成功连通,此时无论ip怎么变都无所谓了