导学:学习Docker使用的一些基础知识,为将来部署项目打下基础,具体用法可以参考Docker的官方文档:
Docker DocsHome page for Docker's documentation
https://docs.docker.com/
Docker最常见的命令就是操作镜像、容器的命令,可以参考官方文档:
Use the Docker command line | Docker DocsDocker's CLI command description and usage
https://docs.docker.com/engine/reference/commandline/cli/其中,比较常见的命令有:


用⼀副图来表示这些命令的关系:

默认情况下,每次重启虚拟机我们都需要手动启动Docker和Docker中的容器,通过命令可以实现开启自启:

1. 第一步,去DockerHub查看nginx镜像仓库及其相关信息

2. 第二步,拉取Nginx镜像:docker pull nginx

3. 查看本地镜像列表验证是否拉取成功:docker images

4. 保存Nginx镜像到本地压缩文件:docker save -o nginx.tar.gz nginx:latest

5. 删除Nginx本地镜像:docker rmi nginx:latest

6. 重新加载刚才保存到本地的Nginx镜像的压缩文件:docker load -i nginx.tar.gz

7. 第四步,创建并运行Nginx容器:docker run -d --name nginx -p 80:80 nginx

8. 第五步:docker ps查看正在运行的Docker容器的信息
也可以加格式化访问,格式会更加清爽:
9. 停止容器:docker stop nginx
10. 查看所有容器:docker ps -a

11. 再次启动Nginx容器:docker start nginx
12. 查看对应容器的日志:docker logs [ -f ] 容器名

13. 查看容器的详细信息:docker inspect nginx,返回格式为JSON

容器内部有自己独立的文件系统等:

15. 删除容器:docker rm 容器名,如果容器在运行中,那么会发现无法删除,可以强制删除容器:docker rm -f 容器名
容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,我们要读写容器内的文件非常不方便,大家思考几个问题:
因此,容器提供程序的运行环境,但是程序运行产生的数据、程序运行依赖的配置都应该与容器解耦!
以Nginx为例,我们知道Nginx中有两个关键的目录:
如果我们要让Nginx代理我们的静态资源,最好是放到html目录,如果我们要修改Nginx的配置,最好是找到conf下的nginx.conf文件,但遗憾的是,容器运行的Nginx所有的文件都在容器内部,所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作,如图:

在上图中:
这样一来,容器内的conf和html目录就间接的与宿主机的conf和html目录关联起来(双向映射),我们称为挂载。
此时,我们操作宿主机的/var/lib/docker/volumes/html/_data就是在操作容器内的/usr/share/nginx/html/_data目录,只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了。
小提示:
- /var/lib/docker/volumes这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为/数据卷名/_data。
为什么不让容器目录直接指向宿主机目录呢?
- 因为容器目录直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了,由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
- 但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合,如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可。
不过,通常由于数据库目录比较深,不好寻找,通常我们也允许让容器直接与宿主机目录挂载而不使用数据卷。
数据卷的相关命令有:

注意:
需求:
首先,删除原先下载好的nginx镜像:
1. 首先创建容器并指定数据卷,注意通过 -v 参数来指定数据卷(做数据卷和容器目录的挂载)

docker ps验证nginx是否安装成功:

2. 查看所有数据卷:docker volume ls

3. 查看html数据卷的详情

4. cd查看宿主机目录:/var/lib/docker/volumes/html/_data

index.html就是Nginx的欢迎界面!
5. 进入容器内部,查看/usr/share/nginx/html目录内的文件是否变化

利用数据卷我们就实现了宿主机目录与容器内目录之间自动的双向映射!
需求:
1. 查看MySQL容器的详细信息:docker inspect mysql

可以发现,其中有几个关键属性:
出于数据解耦的考虑。
删除容器后它的数据卷依然还存在!
在执行docker run命令时,使用 -v 本地目录:容器内目录 可以完成本地目录挂载!
-v mysql:/var/lib/mysql 会被识别为一个数据卷叫mysql,运行时会自动创建这个数据卷
-v ./mysql:/var/lib/mysql 会被识别为当前目录下的mysql目录,运行时如果不存在会创建目录
删除并重新创建mysql容器,并完成本地目录挂载:


创建并运行新MySQL容器,挂载本地目录:


前面我们一直在使用别人准备好的镜像,那如果我要部署一个Java项目,把它打包为一个镜像该则怎么做呢?
要想自己构建镜像,必须先了解镜像的结构。
回顾:镜像就是包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包!
镜像之所以能让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖等各种文件,这些文件分层打包而成。
因此,自定义镜像 / 构建镜像的本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成。
举个例子,我们要从0部署一个Java应用,大概流程是这样:
因此,我们打包镜像也是分成这么几步,即构建一个Java镜像的步骤:
上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合!我们的Docker镜像是有很多的压缩包合并而成的!
但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一ID,称为Layer - 层。
Docker为什么要把这些文件分层去打包呢?

Dockerfile就是利用固定的指令来描述镜像的结构和构建过程,这样Docker才可以依次来构建镜像!
Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像,将来Docker可以根据Dockerfile帮我们构建镜像,常见指令如下:
我们可以基于Ubuntu基础镜像,利用Dockerfile描述镜像结构,也可以直接基于JDK为基础镜像,省略前面的步骤:
当Dockerfile文件写好以后,就可以利用下面命令来构建镜像了:
docker build:就是构建一个Docker镜像
-t:-t参数是指定镜像的名称,是给镜像起名,格式依然是repository:tag的格式,不知道tag时,默认为latest,代表是最新的版本
.:最后的点是指构建时Dockfile所在路径,是指定Dockfile所在目录,如果就在当前目录,则指定为 ".",也可以直接指定Dockerfile目录

查看镜像列表:docker images

尝试运行该镜像:
查看容器:docker ps -a

查看日志:docker logs -f docker-demo

reset:重置
刚才我们创建了一个Java项目的容器,而Java项目往往需要访问其它各种中间件,例如MySQL、Redis等,而容器是有各自独立的隔离空间,现在,我们的容器之间能否互相访问呢?
我们来测试一下,首先,我们查看下MySQL容器的详细信息,重点关注其中的网络IP地址:
1. 用基本命令docker inspect mysql,寻找Networks.bridge.IPAddress属性

2. 通过命令docker exec -it docker-demo bash进入docker-demo容器
在容器内,通过ping命令测试网络

发现容器间可以进行网络互联,没有问题! (IP地址如果在一个网段当中,则说明是可以ping通的!)

但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很有可能MySQL容器的IP会发生变化,连接会失败。
所以,我们必须借助于docker的网络功能来解决这个问题,官方文档:
docker network | Docker Docs
https://docs.docker.com/engine/reference/commandline/network/加入自定义网络的容器才可以通过容器名互相访问(IP地址可以变,容器名总归是不会变的)!
Docker的网络操作的常见命令有:

查看所有网络:docker network ls
通过命令创建一个自己的网络:docker network create Melo
让docker-demo和mysql都加入该网络,这样该网络内的其它容器都可以用别名互相访问,注意,在加入网络时可以通过--alias给容器起别名:docker network connect 网络 要加入的容器

进入Java项目docker-demo容器,然后ping mysql:

OK,现在就无需记住IP地址也可以实现容器互联了!