1.Docker介绍
Docker可以让项目部署和软件安装等步骤丝滑很多,大大减少了运维工作量。
Docker主要实现应用和环境的隔离,从而保证应用在不同的操作系统上保持一致性。
1.1 镜像仓库(DockerRegistry)
Docker官方提供了一个专门管理,存储镜像的网站,并对外开放了镜像上传,下载的权利。
Docker官方提供了一些基础镜像 –补充–> 各大软件公司在基础镜像上制作的自家软件的镜像【基本上我们能用到的都会找得到】
DockerRegistry(镜像仓库):这种提供存储,管理Docker镜像的服务器 –在国外下载速度较慢,一般会使用第三方仓库提供的镜像加速功能,提高下载速度。而企业内部的机密项目往往会采用私有镜像仓库。
总之,镜像的来源有两种:
- 基于官方基础镜像自己制作
- 直接去DockerRegistry官方网站下载
【Docker本身就包含一个后台服务,我们可以利用Docker命令告诉Docker服务,帮助我们快速部署指定的应用。】
【Docker服务部署应用时,①去搜索并下载应用对应的镜像,②根据镜像创建并允许容器,这样应用就部署完成了】
如图所示:
==以下是环境层面,保证拥有环境==
Docker安装和Linux安装参考文件:
==以下是操作层面,保证可以操作==
1.容器(container) –实例
命令 | 说明 | 文档地址 |
---|---|---|
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 |
用一副图来表示这些命令的关系:
其中,如果我们需要给其他人使用自己的镜像就有两种方式:
①docker save存储为tar包,别人就可以docker load运行镜像
②docker push上传到私有镜像仓库,别人就可以docker pull拉取镜像
1.1 创建容器举例
以部署nginx服务器为例:
接下来我们可以生成一个tar压缩包,然后通过压缩包加载进来并且启动:
1.2 指令详解
其中,docker run指令的具体细节如下:
我们可以通过docker run指令搭配-x的形式配置一些参数信息,可以指定容器名为mysql,可以指定宿主机和容器mysql端口对比,指定mysql信息和镜像名。
2. 数据卷(volume) –中间商
==使用操作:会在docker run指令内添加-v xxx:xxx==
2.1 数据卷定义
数据卷是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁。
以Nginx为例,我们知道Nginx有两个关键的目录:
- html:放置一些静态资源
- conf:放置配置文件
如果我们要让Nginx代理我们的静态资源,那就需要放在html目录,如果我们要需要Nginx配置,那就需要放在conf下的nginx.conf文件。
因此,我们的最初是思路就是直接docker exec 进入容器内部进行修改,但会出现一个问题就是docker容器内部很多指令瘫痪。
只能是给docker容器对应目录 <—-通过数据卷联系—-> 宿主机(服务器)目录。
如图所示:
我们在宿主机里面,独立于宿主机文件系统创建数据卷,以html为例,我们可以将宿主机默认目录和Docker容器的Nginx的目录联系起来
在上图中:
- 我们创建了两个数据卷:
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代理了
为什么不让容器目录直接指向宿主机目录呢?
- 因为直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了。由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
- 但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合。如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可(只需要改映射关系)
2.2 数据卷指令
命令 | 说明 | 文档地址 |
---|---|---|
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 |
【注意】容器和数据卷的挂在要在创建容器时候配置(docker run的时候添加-v xxx:xxx)。如果是已经docker run创建好的容器是不能设置数据卷的。
【默认】docker run不指定-v就会生成一个随机名的数据卷【匿名数据卷,不好用于操作】
2.3 数据卷分类
目前可以接触到的挂载方式有两种:
- -v 数据卷名:docker容器目录
- -v 本地目录:docker容器目录 【本地目录:一定要以 /或者./形式输入】
2.3.1 挂载固定目录
意思就是数据卷名为html,而docker容器目录是usr/share/nginx/html,宿主机目录就是用固定的默认目录(/var/lib/docker/volumes)。
【/var/lib/docker/volumes这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为
/数据卷名/_data】
2.3.2 挂载本地目录/文件
2.3.1的第一种方式,我们数据卷的目录结构比较深,去操作数据卷目录的时候不方便。因此我们可以直接将容器目录和宿主机目录挂载
1 | # 挂载本地目录 |
举例:我们将mysql的data,init,conf进行挂载
2.4 查看数据卷内容
同时,我们可以使用指令docker volume inspect查看数据卷详情,其中数据卷部分在:Mounts部分
可以发现,其中有几个关键属性:
- Name:数据卷名称。由于定义容器未设置容器名,这里的就是匿名卷自动生成的名字,一串hash值。
- Source:宿主机目录
- Destination : 容器内的目录
【注意:每一个不同的镜像,将来创建容器后内部有哪些目录可以挂载,可以参考DockerHub对应的页面(参考DockerHub查看容器内部哪些目录可以挂载)】
3.镜像(image) –类
镜像类似于Java的类,镜像是一个模版,可以通过镜像创建多个容器(实例)。此外,镜像分为两种:
- 别人提供的镜像
- 自己构建的镜像
镜像中包含了程序运行需要的系统函数库,环境,配置和依赖(快速跨操作系统部署应用)
3.1 镜像结构
举例,如果是从0部署一个Java应用大致流程如下:
那因此,我们打包镜像也是分成这么几步:
- 准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可)
- 安装并配置JDK
- 拷贝jar包
- 配置启动脚本
上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合。
需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤==分层叠加==而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer(层)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。
例如,第一步中需要的Linux运行环境,通用性就很强,所以Docker官方就制作了这样的只包含Linux运行环境的镜像。我们在制作java镜像时,就无需重复制作,直接使用Docker官方提供的CentOS或Ubuntu镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个Java项目的镜像结构如图所示:
==【每一步操作都拆分到每一层,每一层都可以使用基础镜像的基础上搭建其它层】==
就像我们,docker pull redis的时候就会发现有一层其实在pull其他容器的时候就已经加载了
3.2 Dockerfile
由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以Docker就提供了==自动打包镜像==的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给Docker去执行即可。
其中的语法比较多,比较常用的有:
指令 | 说明 | 示例 |
---|---|---|
FROM | 指定基础镜像 | FROM centos:6 |
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 |
例如,要基于Ubuntu镜像来构建一个Java应用,其Dockerfile内容如下:
3.3 构建镜像(docker build)
基本语法:
1 | docker build -t 镜像名 Dockerfile目录 |
举例:
docker build
: 就是构建一个docker镜像-t docker-demo:1.0
:-t
参数是指定镜像的名称(repository
和tag
).
: 最后的点是指构建时Dockerfile所在路径,由于我们进入了demo目录,所以指定的是.
代表当前目录,也可以直接指定Dockerfile目录
后续可以通过docker images查看是否构建成功:
4.网络
Java项目往往需要访问其它各种中间件,例如MySQL、Redis等。
4.1 默认网络
==【这种情况,默认容器在同一个网络中可以访问。但是,是以ip进行连接,很容易出问题】==
首先,我可能可以通过docker exec进入Java项目,然后在内部ping一下Mysql等中间件发现可以访问。这是因为我们在docker run启动容器的时候会默认添加到一个名字为docker0的网络中。这样我们会在默认情况下在同一个网络中。
但是,容器的网络IP其实是一个虚拟IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败。【因为我可能关闭容器,新开一个其他容器就会占用这个虚拟IP】
因此,我们必须借助docker网络功能来解决这个问题。
4.2 自定义网络
==【这种情况,同一个网络中可以通过容器名访问】,解决了默认网络连接用ip死板,并且ip可变的情况==
常用指令如下:
命令 | 说明 | 文档地址 |
---|---|---|
docker network create | 创建一个网络 | docker network create |
docker network ls | 查看所有网络 | docs.docker.com |
docker network rm | 删除指定网络 | docs.docker.com |
docker network prune | 清除未使用的网络 | docs.docker.com |
docker network connect | 使指定容器连接加入某网络 | docs.docker.com |
docker network disconnect | 使指定容器连接离开某网络 | docker network disconnect |
docker network inspect | 查看网络详细信息 | docker network inspect |
4.2.1 通过docker network connect连接
基本步骤:
1 | # 创建网络 |
案例:创建网络heima,然后加入【这时候容器会加入两个网络。第一个是默认的docker0,第二个是新建网络heima】
1.通过docker network ls先查询已有网络,然后通过docker network create xx创建网络
2.通过ip addr查看网络信息:
3.接着,通过docker network connect 网络名 容器名形式加入网络。通过docker inspect容器名形式查看mysql容器的网络连接情况。
4.2.2 通过docker run链接
在docker run里面加【直接加入到创建的网络heima】
5.开机自启动
【默认】每次重启虚拟机都需要手动启动Docker和Docker中容器。我们可以通过命令实现开机自启:
1 | # Docker开机自启 |
6.Docker指令别名
1.修改/root/.bashrc文件:
2.然后,执行命令source /root/.bashrc使别名生效
==以下是部署项目层面==
1.部署后端项目
- 1.1 打包jar包
- 1.2 创建Dockerfile文件
- 1.3 构建镜像
- 1.4 准备linux系统并且准备docker容器和mysql容器(创建表和导入数据)
1 | 1.搭建linux环境 --参考本篇1.Linux环境搭建 |
- 1.5 创建容器
- 1.6 在windows系统测试
然后我们可以在linux服务器中docker logs 容器名查看日志:
2.部署前端项目
- 2.1 将整个nginx导入linux服务器
- 2.2 创建nginx容器(将nginx.conf和html目录与容器挂载)
- 2.3 windows页面访问
3.DockerCompose快速部署
可以看到,1和2的前后端部署包含多个容器:Mysql,Nginx,Java项目。如果有一个新的稍微复杂的项目就会有很多中间件,就不能这样手动逐一部署,太麻烦了。
而Docker Compose就可以帮助我们实现多个相互关联的Docker容器的快速部署。
它允许用户通过一个单独的==docker-compose.yml==模板文件(YAML 格式)来定义一组相关联的应用容器。
使用步骤:
- 1.编写docker-compose.yml文件
- 2.导入yml文件
- 3.使用docker compose指令启动
3.1 docker run和docker compose对比
docker-compose文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务(service)。由于service就是在定义某个应用的运行时参数,因此与docker run
参数非常相似。
==基本上也就是在一个yml配置多个services(就是多个容器)==
对比如下:
docker run 参数 | docker compose 指令 | 说明 |
---|---|---|
–name | container_name | 容器名称 |
-p | ports | 端口映射 |
-e | environment | 环境变量 |
-v | volumes | 数据卷配置 |
–network | networks | 网络 |
则1和2整体黑马商城部署文件如下:
1 | version: "3.8" |
3.2 基本命令
编写好docker-compose.yml文件,就可以部署项目了。常见的命令格式如下:
3.3 测试
导入对应容器的文件和compose的yml文件,然后执行docker compose指令即可一键多容器启动。