Docker 2020最新版 超详细教程

超长文警告!!!建议先看目录

如有错误,欢迎指正!
编辑不易,赞同的话请点个赞哦~

最后更新时间 ===2020 06 17===


Docker 2020最新版 超详细教程_第1张图片


Docker 目录

  • 文档介绍
  • Docker 概述
    • Docker 为什么会出现?
    • Docker 简介
    • Docker 的历史
    • Docker 能干嘛?
  • Docker 安装
    • Docker 的基本组成
    • Docker 安装步骤
    • 配置阿里云镜像加速
    • HelloWorld启动流程分析
    • 底层原理
  • Docker 常用命令
    • 帮助命令
    • 镜像命令
      • docker images 查看所有本地主机上的镜像
      • docker search 搜索镜像
      • docker pull 下载镜像
      • docker rmi 删除镜像
    • 容器命令
      • docker run 启动容器
      • docker ps 查看正在运行的容器
      • 退出容器
      • docker rm 删除容器
      • 启动和停止容器
    • 常用其他命令
      • docker run -d 后台启动容器
      • docker logs 查看日志
      • docker top 查看容器中进程信息
      • docker inspect 查看镜像的元数据(重要)
      • docker exec/attach 进入当前正在运行的容器
      • docker cp 从容器内拷贝文件到主机上
    • 命令小结
    • 实战练习
    • Docker 可视化
  • Docker 镜像讲解
    • 什么是镜像?
    • Docker 镜像加载原理
    • 分层理解
    • commit 镜像
  • 容器数据卷
    • 什么是容器数据卷?
    • 使用数据卷
    • MySQL 同步数据
    • 具名和匿名挂载
    • 初识 DockerFile
    • 数据卷容器
    • 小结
  • Docker File
    • DockerFile 介绍
    • DockerFile 构建过程
    • DockerFile 指令
    • 实战:构建一个自己的CentOS
    • CMD 和 ENTRYPOINT 的区别
    • 实战:制作一个 Tomcat 镜像
    • 发布自己的镜像到 DockerHub
    • 小结
  • Docker 网络
    • 理解【Docker0】
    • 容器互联 --link 命令
    • 自定义网络
    • 网路连通
    • 实战:部署 Redis 集群
    • SpringBoot 微服务打包 Docker 镜像


文档介绍

本文档基于B站教学视频,强烈推荐去看一看这个UP主的教学视频,视频地址
如果可以的话请点击视频链接,在视频下方点赞投币分享转发哦,你的支持对我们真的很重要!
Docker 2020最新版 超详细教程_第2张图片

MarkDown 文件下载

仓库里还有许多的文件,喜欢的话点个 Star 哦


Docker 概述

Docker 为什么会出现?

  1. 在一款产品/项目的开发过程中,要经历 【开发 ==> 上线】 这样的一个过程,就会有两套环境(dev prod),例如:应用环境和应用配置
  2. 一般公司也会配有两个部门(开发、运维),环境问题就会导致项目出现问题,有句话是这么说的:【明明在我的电脑就可以运行,为什么你的就不行】。这也能看出项目的运行有对环境的要求了,有可能是因为版本不同,导致服务不可用,也有可能是其他的原因。
  3. 那解决方案就是,要么运维来管理开发,统一环境;要么开发把运维的活也包了,不然工资也不会这么高。
  4. 那么这里面最根本的就是环境问题,其中环境配置就是比较麻烦的,特别是在搭建集群环境时,要一台一台机器去配置环境,费时费力。
  5. 以往我们发布项目的时候有jar包/war包,假设我们能够带着环境来上线项目,就会方便很多。
  6. 特别是在Windows上开发,Linux上发布,这样跨平台的环境配置,十分麻烦
  7. 那么相比于传统的【开发人员给jar包 运维来上线】 ,现在更倾向于 【开发打包部署上线一套流程】就能解决。
  8. 这就诞生了Docker,为以上的问题提出了一套解决方案
  9. 【java开发出jar包】--->【带上环境打包项目(镜像)】--->【发布到Docker仓库】--->【下载镜像,直接运行】

Docker 简介

官网首页: https://www.docker.com/
官方文档: https://docs.docker.com/
Docker镜像仓库地址: https://hub.docker.com/
Docker 2020最新版 超详细教程_第3张图片
Docker 2020最新版 超详细教程_第4张图片

  1. Docker 是 PaaS 提供商 【dotCloud】 开源的一个基于 LXC 的高级容器引擎,源代码托管在 Github 上, 基于go语言并遵从 Apache2.0 协议开源。
  2. Docker 的思想就来自于【集装箱】,这个可以从logo中看出来,在码头上集装箱会将货物【隔离】开,类似于在电脑上跑了很多应用,原来可能应用之间会发生冲突,例如:JRE上会跑很多Java应用,可能会发生端口冲突,在原来都是交叉的,现在就要将他们隔离开来
  3. 隔离,也是Docker的核心思想!将应用打包装箱,每个箱子互相隔离!另外,Docker也可以通过隔离机制,将服务器的性能发挥到极致。就好比在集群环境中,以往要开多台虚拟机,现在只需要一台虚拟机,然后利用Docker的隔离机制,跑多个应用,模拟集群环境。

Docker 的历史

【了解技术的历史才能更好的学习技术】

  • 【2010年】,有几个搞IT的年轻人,在美国成立了一家公司:dotCloud,做一些 PaaS 的云计算服务!说白了就是 LXC 有关的容器技术。
  • 他们就将自己的容器化技术进行统一的命名,称为Docker
  • Docker 刚刚诞生的时候,没有引起行业的注意,难以维持
  • 于是打算将技术开源,将源代码开放
  • 【2013年】 公司创始人 Solomon Hykes 将Docker开源,于是一下火爆了起来,Docker每个月都会更新一个版本
  • 【2014年4月9日】,Docker 1.0发布
  • Docker的优点
    • 十分的轻巧!在容器技术出来之前,使用的都是虚拟机技术(通过软件模拟出一台或多台电脑),但是虚拟机特别占内存,浪费性能
    • 虚拟机也是虚拟化技术,Docker的容器技术,也是一种虚拟化技术!本质是差不多的。
    • 两者区别
      • 虚拟机时下载原生镜像,然后安装,要实现隔离只能开启多个虚拟机
      • docker的隔离是通过【镜像机制】,不是完整的电脑,只是最核心的环境,十分的轻巧,再加上需要的环境,打包成镜像,运行的时候运行镜像即可,十分小巧
      • 虚拟机要运行的话需要几个G,而Docker镜像只需要几M或几百M;而且几乎是秒级启动
  • 至今已经发展成所有开发人员必须要会的技术

Docker 能干嘛?

Docker 2020最新版 超详细教程_第5张图片

一台电脑中需要有内核、依赖库,在这个基础上再运行应用

虚拟机技术就是在电脑上再模拟出一台计算机

虚拟机技术的缺点

  1. 资源占用十分多
  2. 冗余步骤多
  3. 启动很慢

容器化技术
Docker 2020最新版 超详细教程_第6张图片

  1. 容器化技术不是模拟出一个完整的操作系统
  2. 每一个容器都会包含依赖库环境和应用,而且每个容器之间互相隔离,不同于传统的虚拟机技术,各个应用交叉共用依赖库,容易发生冲突

比起 Docker 和虚拟机技术的不同

  • 传统虚拟机:虚拟出一套硬件,运行一个完整的操作系统,然后在这个系统上安装和运行环境
  • 容器内的应用直接运行在宿主机的内核,容器时没有自己的内核的,也没有虚拟硬件,所以就轻便了
  • 每个容器之间是互相隔离的,每个容器内都有一个属于自己的文件系统,互不影响

使用了Docker之后,我们部署应用就和搭积木一样

  • 应用更快速的交付和部署
    传统:一堆帮助文档,安装程序
    Docker: 打包镜像发布测试,一键运行
  • 更便捷的升级和扩缩容
    项目打包为一个镜像,如果服务器A上运行出现性能瓶颈,需要进行水平扩展,可以直接在服务器B上一键运行这个镜像
  • 更简单的系统运维
    在容器化之后,开发、测试的环境都是高度一致的
  • 更高效的计算资源利用
    Docker是内核级别的虚拟化,可以在一个物理机上运行很多的容器实例,将服务器的性能发挥到极致

Docker 安装

Docker 的基本组成

Docker 2020最新版 超详细教程_第7张图片

  1. 镜像(image):就好比是一个模板,可以通过这个模板来创建多个容器【最终服务运行或者项目运行就是在容器中】
  2. 容器(container):
    Docker利用容器技术,独立运行一个或一个组应用,通过镜像来创建的
    有启动、停止、删除等基本命令
    目前就可以把这个容器理解为一个简易的Linux系统
  3. 仓库(repository):
    仓库就是存放镜像的地方!
    仓库分为【公有仓库】和【私有仓库】

Docker 安装步骤

  1. 环境准备:CentOS7 虚拟机【可以在虚拟机上,也可以在远程终端上,例如XShell / FinalShell】
  2. 环境查看
uname -r // 查看系统内核
cat /etc/os-release  // 查看系统基本信息
  1. 安装
    【查看帮助文档】
    Docker 2020最新版 超详细教程_第8张图片

Docker 2020最新版 超详细教程_第9张图片

接下来的操作跟随着帮助文档即可

# 1. 卸载旧的版本
$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

# 2. 需要的安装包
$ sudo yum install -y yum-utils

# 3. 设置镜像仓库 这里默认是国外的,我们最好用国内的 推荐使用阿里云
$ sudo yum-config-manager \
    --add-repo \
    # https://download.docker.com/linux/centos/docker-ce.repo
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 4. 文档上接下来的是一些配置【Optional: Enable the nightly or test repositories.】
#    没有什么用,这里就暂时不配置了

# 5. 安装docker相关的内容 docker-ce是社区版  ee是企业版 推荐使用ce 
#    在安装之前最好先更新一下yum软件包索引 => yum makecache fast
$ sudo yum install docker-ce docker-ce-cli containerd.io
# 这里也可以指定版本安装,帮助文档的第二点就是安装的方法,先查看版本列表,再使用命令指定版本安装

# 6. 启动Docker
$ sudo systemctl start docker

# 7. 查看是否启动成功
docker version # 查看版本

# 8. 启动hello-world
$ sudo docker run hello-world

# 9. 卸载docker 了解即可 两个步骤【卸载依赖 删除目录】
$ sudo yum remove docker-ce docker-ce-cli containerd.io
$ sudo rm -rf /var/lib/docker

# 10.升级docker引擎 可以下载更新的软件包文件然后再重复一遍安装步骤
#    然后使用 yum -y upgrade 代替 yum -y install  并指向新文件

配置阿里云镜像加速

链接地址:https://cr.console.aliyun.com/cn-shanghai/instances/repositories

Docker 2020最新版 超详细教程_第10张图片

Docker 2020最新版 超详细教程_第11张图片
【跟随步骤完成之后再重启服务即可】

HelloWorld启动流程分析

Docker 2020最新版 超详细教程_第12张图片
Docker 2020最新版 超详细教程_第13张图片

底层原理

Docker 是怎么工作的?

Docker 是一个Client · Server 结构的系统,Docker的守护进程(Docker daemon)运行在主机上,通过Socket从客户端访问!

Docker Server 接收到 Docker Client 的指令,就会执行这个命令

Docker 2020最新版 超详细教程_第14张图片

Docker 为什么比虚拟机快?

1、Docker 有着比虚拟机更少的抽象层
2、Docker 利用的是宿主机的内核,VM 需要的是GuestOS【就是需要再搭建一个系统环境】
Docker 2020最新版 超详细教程_第15张图片

所以说,新建一个容器的时候,Docker 不需要像虚拟机那样重新加载一个操作系统的内核,避免一些引导性的操作;而虚拟机是加载 Guest OS,是分钟级别的,Docker 是利用宿主机的操作系统,省略了这个复杂的过程,所以是秒级的启动

Docker 2020最新版 超详细教程_第16张图片

如果看着还不是很懂,没关系,接着往下看,当你学习完所有的命令之后再回来看这段理论,就会清晰许多。


Docker 常用命令

帮助命令

docker version       # 显示docker版本信息
docker info          # docker的系统信息,包括镜像和容器的数量
docker 命令 --help   # 帮助命令

如果碰到不知道的可查看帮助文档:【https://docs.docker.com/engine/reference/commandline/build/】

镜像命令

docker images 查看所有本地主机上的镜像

【官方文档】

# 查看帮助 其他命令的用法类似 
[root@iZwz98zprwjrt7d2pp9g0zZ ~]# docker images --help

Usage:  docker images [OPTIONS] [REPOSITORY[:TAG]]

List images

Options:
  -a, --all             列出所有镜像
      --digests         显示摘要
  -f, --filter filter   根据提供的条件过滤输出
      --format string   指定返回值的模板文件
      --no-trunc        不截断输出,即显示完整的镜像信息
  -q, --quiet           只显示镜像ID
# 比较常用的是 -a -q


# 查看本地主机上的镜像
[root@admin ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              bf756fb1ae65        5 months ago        13.3kB
# 解释
REPOSITORY   镜像的仓库源
TAG          镜像的标签
IMAGE ID     镜像的id
CREATED      镜像的创建时间
SIZE         镜像的大小

docker search 搜索镜像

网页版可以通过 Docker Hub 搜索镜像,在Linux命令行里用 docker search搜索

Docker 2020最新版 超详细教程_第17张图片

[root@admin~]# docker search mysql
NAME                              DESCRIPTION                                     STARS               OFFICIAL            AUTOMA
TED
mysql                             MySQL is a widely used, open-source relation…   9621                [OK]                
mariadb                           MariaDB is a community-developed fork of MyS…   3495                [OK]                
# 可选项,通过收藏或其他来过滤检索结果
--filter=stars=3000  # 搜索出来的镜像就是STARS大于3000的
[root@admin~]# docker search mysql --filter=stars=8000
NAME                DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
mysql               MySQL is a widely used, open-source relation…   9621                [OK]                

docker pull 下载镜像

Docker 2020最新版 超详细教程_第18张图片

# 下载镜像 docker pull 镜像名[:tag]  tag可以指定版本,没有的话默认使用最新版
[root@admin ~]# docker pull mysql
Using default tag: latest   # 如果不写tag 默认就是最新版
latest: Pulling from library/mysql  
8559a31e96f4: Pull complete    # 分层下载,docker image 的核心,联合文件系统
d51ce1c2e575: Pull complete 
c2344adc4858: Pull complete 
fcf3ceff18fc: Pull complete 
16da0c38dc5b: Pull complete 
b905d1797e97: Pull complete 
4b50d1c6b05c: Pull complete 
c75914a65ca2: Pull complete 
1ae8042bdd09: Pull complete 
453ac13c00a3: Pull complete 
9e680cd72f08: Pull complete 
a6b5dc864b6c: Pull complete 
Digest: sha256:8b7b328a7ff6de46ef96bcf83af048cb00a1c86282bfca0cb119c84568b4caf6  # 签名信息
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest  # 真实地址 
# 即:docker pull mysql 和 docker pull docker.io/library/mysql:latest 是等价的

# 指定版本下载 版本不可乱写 可在 docker hub 上查看
[root@admin ~]# docker pull mysql:5.7  # 加上了tag标签后可指定版本下载
5.7: Pulling from library/mysql
8559a31e96f4: Already exists   # 可以看到这里会显示一些文件已存在
d51ce1c2e575: Already exists   # 这就是分层下载的好处 可以共用一些文件
c2344adc4858: Already exists 
fcf3ceff18fc: Already exists 
16da0c38dc5b: Already exists 
b905d1797e97: Already exists 
4b50d1c6b05c: Already exists 
d85174a87144: Pull complete 
a4ad33703fa8: Pull complete 
f7a5433ce20d: Pull complete 
3dcd2a278b4a: Pull complete 
Digest: sha256:32f9d9a069f7a735e28fd44ea944d53c61f990ba71460c5c183e610854ca4854
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

# 下载完成后查看镜像
[root@admin ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               5.7                 9cfcce23593a        4 days ago          448MB
mysql               latest              be0dbf01a0f3        4 days ago          541MB
hello-world         latest              bf756fb1ae65        5 months ago        13.3kB

docker rmi 删除镜像

可以通过IMAGE ID删除,也可以根据镜像名称来删除

# 删除指定id的镜像
[root@admin ~]# docker rmi -f 容器id  
# 删除多个镜像
[root@admin ~]# docker rmi -f 容器id 容器id 容器id 
# 删除全部镜像
[root@admin ~]# docker rmi -f $(docker images -aq)

容器命令

【说明】有了镜像才可以创建容器,要先下载一个centos镜像来测试学习 docker pull centos

docker run 启动容器

docker run [可选参数] image

# 常用参数说明
--name="NAME"  容器名字 用于区分容器
-d             后台方式运行
-it            使用交互方式运行,例如要进入容器查看内容
-p             指定容器端口 -p 8080
	-p ip:主机端口:容器端口
	-p 主机端口:容器端口 (常用)
	-p 容器端口
	容器端口    (直接写容器端口也行)
-P             大写的P 随机指定端口

# 测试一波 启动并进入容器
# 以交互模式启动 centos 并进入容器 同时指定使用 centos 里面的 bash 作为控制台进行交互
[root@admin ~]# docker run -it centos /bin/bash
# 这里可以看到命令行的前缀发生了变化 表示已经进入容器了
[root@67d60e7e973b /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
# 退出容器
[root@67d60e7e973b /]# exit
exit
[root@admin ~]# ls
mysql80-community-release-el7-3.noarch.rpm  shell

docker ps 查看正在运行的容器

-a    # 列出当前正在运行的容器 + 历史运行过的容器 就是全部容器
-n=?  # 显示最近创建的容器 ?表示个数
-q    # 只显示容器的编号

[root@admin ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[root@admin ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                          PORTS               NAMES
67d60e7e973b        centos              "/bin/bash"         15 minutes ago       Exited (0) 15 minutes ago                           keen_yalow
95b5db0ebd50        hello-world         "/hello"            4 hours ago          Exited (0) 4 hours ago                              wizardly_leavitt

退出容器

exit    # 直接停止容器并退出
ctrl + p + q  # 容器不停止退出

docker rm 删除容器

docker rm 容器id                 # 删除指定id的容器,不能删除正在运行的容器 rm -f 强制删除
docker rm -f $(docker ps -aq)    # 删除所有容器
docker ps -a -q|xargs docker rm  # 通过管道删除所有的容器

启动和停止容器

docker start 容器id      # 启动容器
docker restart 容器id    # 重启容器
docker stop 容器id       # 停止容器
docker kill 容器id       # 杀死容器

常用其他命令

docker run -d 后台启动容器

# docker run id 镜像名 后台启动
docker run -d  centos   # 后台启动
# 启动docker后ps 会发现centos停止了
# 常见的坑:docker 容器使用后台运行,就必须要有一个前台进程(就是交互)
# 如果 docker 发现没有应用,自己没有提供服务,就会立刻停止

docker logs 查看日志

docker logs -f -t --tail 容器id   # 查看容器运行日志

# 举例 在容器内运行脚本 while循环输出字符串
# 编写脚本
[root@admin ~]# docker run -d centos /bin/sh -c "while true;do echo hello;sleep 1;done"
c482e0df32230d949c36dd9047f93257c23702960298317830f1b8b2ad42e28c
# 查看容器
[root@admin ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
c482e0df3223        centos              "/bin/sh -c 'while t…"   11 seconds ago      Up 10 seconds                           ecstatic_dhawan
# 显示日志
[root@admin ~]# docker logs -tf --tail 10 c482e0df3223
2020-06-13T08:04:41.312351474Z hello
2020-06-13T08:04:42.314853838Z hello
2020-06-13T08:04:43.317313743Z hello
2020-06-13T08:04:44.319772893Z hello
2020-06-13T08:04:45.322357813Z hello
2020-06-13T08:04:46.324866999Z hello
2020-06-13T08:04:47.327293107Z hello
2020-06-13T08:04:47.327293107Z hello
2020-06-13T08:04:47.327293107Z hello
2020-06-13T08:04:47.327293107Z hello
...(直接输出10条日志后 后面还会继续增加 因为while循环一直在输出)
# 不加 --tail 10 就是显示全部日志

docker top 查看容器中进程信息

# 查看容器内的进程信息
docker top 容器id 

docker inspect 查看镜像的元数据(重要)

# 查看容器内所有信息
docker inspect 容器id
# 测试
[root@admin ~]# docker inspect 09b2534befb1
[
    {
        # 容器id 能发现命令中的id只是这里面的一小部分
        "Id": "09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26",、
        # 创建时间
        "Created": "2020-06-13T08:12:31.964868162Z",
        # 交互前台路径
        "Path": "/bin/bash",
        # 携带参数
        "Args": [],
        # 容器状态
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 2076,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2020-06-13T08:12:32.29364097Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        # 镜像来源
        "Image": "sha256:470671670cac686c7cf0081e0b37da2e9f4f768ddc5f6a26102ccd1c6954c1ee",
        "ResolvConfPath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/hostname",
        "HostsPath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/hosts",
        "LogPath": "/var/lib/docker/containers/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26/09b2534befb12a8c5297bbc4cbe26ae3b528478d713efbb23e7c5cdaf8400b26-json.log",
        "Name": "/compassionate_kepler",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        # 主机配置
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Capabilities": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae-init/diff:/var/lib/docker/overlay2/5108dfc3feeecdef61ca695a9c4b8c459eb743214117505341369d6c7f62fe53/diff",
                "MergedDir": "/var/lib/docker/overlay2/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae/merged",
                "UpperDir": "/var/lib/docker/overlay2/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae/diff",
                "WorkDir": "/var/lib/docker/overlay2/9d1b03a32ce30afcaf3da85e14de8412cad304c4e4815a6ff045883017a2e4ae/work"
            },
            "Name": "overlay2"
        },
        # 挂载信息
        "Mounts": [],
        # 基本配置
        "Config": {
            "Hostname": "09b2534befb1",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            # 环境变量 没有java的所以用不了java
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            # 命令行
            "Cmd": [
                "/bin/bash"
            ],
            "Image": "centos",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "org.label-schema.build-date": "20200114",
                "org.label-schema.license": "GPLv2",
                "org.label-schema.name": "CentOS Base Image",
                "org.label-schema.schema-version": "1.0",
                "org.label-schema.vendor": "CentOS",
                "org.opencontainers.image.created": "2020-01-14 00:00:00-08:00",
                "org.opencontainers.image.licenses": "GPL-2.0-only",
                "org.opencontainers.image.title": "CentOS Base Image",
                "org.opencontainers.image.vendor": "CentOS"
            }
        },
        # 网络设置
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "26ea3297b66bbae1ac7798d19783e51fcc1f88037409888db0137bf3549b18d3",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/26ea3297b66b",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "25b52bae99d2c591b03ba97e644dc3b3a26917209523d76bb78f3190b364a3a3",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
            	# 现在使用的网络工作模式 桥接模式
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "695424c9542cb18c12700a9df49559009382acf4ace4668dafc94367620a0522",
                    "EndpointID": "25b52bae99d2c591b03ba97e644dc3b3a26917209523d76bb78f3190b364a3a3",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

docker exec/attach 进入当前正在运行的容器

# 我们通常都是将容器放在后台运行,有时候就需要进入容器,修改一些配置

# 命令 1
docker exec -it 容器id bashShell
# 测试
[root@admin ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
09b2534befb1        centos              "/bin/bash"         11 minutes ago      Up 11 minutes                           compassionate_kepler
[root@admin ~]# docker exec -it 09b2534befb1 /bin/bash
[root@09b2534befb1 /]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:12 pts/0    00:00:00 /bin/bash
root        15     0  0 08:24 pts/1    00:00:00 /bin/bash
root        28    15  0 08:24 pts/1    00:00:00 ps -ef

# 命令 2
docker attach 容器id
# 测试
[root@admin ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
09b2534befb1        centos              "/bin/bash"         13 minutes ago      Up 13 minutes                           compassionate_kepler
[root@admin ~]# docker attach 09b2534befb1
[root@09b2534befb1 /]# 

两种命令的区别:
1、docker exec   是进入容器后【开启了一个新的终端】,可以在里面操作
2、docker attach 是进入容器【正在执行的终端】,不会启动新的进程

docker cp 从容器内拷贝文件到主机上

# 命令
docker cp 容器id:容器内路径 目的主机路径
# 测试
# 在容器内新建文件
[root@09b2534befb1 /]# cd /home
[root@09b2534befb1 home]# ls
[root@09b2534befb1 home]# touch hello.txt
# 拷贝文件可退出容器拷贝,即使停止了数据依然在
[root@09b2534befb1 home]# exit
exit
[root@admin ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
# 拷贝数据到主机的home目录下
[root@admin ~]# docker cp 09b2534befb1:/home/hello.txt /home
[root@admin ~]# cd /home
# 查看文件
[root@admin home]# ls
hello.txt  study

# 拷贝是一个手动过程,之后我们可以使用 -v 卷的技术,自动同步

要将所有命令全部敲一遍哦,这样才能加深印象

命令小结

Docker 2020最新版 超详细教程_第19张图片

Docker 的命令是十分多的,上面我们学习的那些都是最常用的容器和镜像的命令

  attach      Attach local standard input, output, and error streams to a running container    # 当前 shell 下 attach 连接指定运行镜像
  build       Build an image from a Dockerfile                                                 # 通过 Dokcerfile 定制镜像
  commit      Create a new image from a container's changes                                    # 提交当前容器为新的镜像
  cp          Copy files/folders between a container and the local filesystem                  # 从容器中拷贝指定文件或目录到宿主机中
  create      Create a new container                                                           # 创建一个新的容器,同 run 但是不启动容器
  diff        Inspect changes to files or directories on a container's filesystem              # 查看 Docker 容器变化
  events      Get real time events from the server                                             # 从 Docker 服务获取容器实时时间
  exec        Run a command in a running container                                             # 在已存在的容器上运行命令
  export      Export a container's filesystem as a tar archive                                 # 到处容器的内容流作为一个 tar 归档文件(对应 import)
  history     Show the history of an image                                                     # 显示镜像形成历史
  images      List images                                                                      # 列出系统当前镜像
  import      Import the contents from a tarball to create a filesystem image                  # 从 tar 包中的内容创建一个新的文件系统映像(对应 export)
  info        Display system-wide information                                                  # 显示系统相关信息
  inspect     Return low-level information on Docker objects                                   # 查看容器详细信息
  kill        Kill one or more running containers                                              # kill 指定 docker 容器
  load        Load an image from a tar archive or STDIN                                        # 从一个 tar 包中加载镜像(对应 save)
  login       Log in to a Docker registry                                                      # 注册或登录一个 docker 源服务器
  logout      Log out from a Docker registry                                                   # 从当前 Docker registry 退出
  logs        Fetch the logs of a container                                                    # 输出当前容器日志信息
  pause       Pause all processes within one or more containers                                # 暂停容器
  port        List port mappings or a specific mapping for the container                       # 查看映射端口对应的容器内部源端口
  ps          List containers                                                                  # 列出容器列表
  pull        Pull an image or a repository from a registry                                    # 从 docker 镜像源服务器拉去指定镜像或者库镜像
  push        Push an image or a repository to a registry                                      # 推送指定镜像或者库镜像至 docker 源服务器
  rename      Rename a container                                                               # 重命名一个容器
  restart     Restart one or more containers                                                   # 重启一个或多个容器
  rm          Remove one or more containers                                                    # 移除一个或多个容器
  rmi         Remove one or more images                                                        # 移除一个或多个镜像(无容器使用该镜像才可移除,否则需删除相关容器才可继续或 -f 强制执行)
  run         Run a command in a new container                                                 # 创建一个新的容器并运行一个命令
  save        Save one or more images to a tar archive (streamed to STDOUT by default)         # 保存一个镜像为一个 tar 包(对应load)
  search      Search the Docker Hub for images                                                 # 在 docker hub 中搜索镜像
  start       Start one or more stopped containers                                             # 启动容器
  stats       Display a live stream of container(s) resource usage statistics                  # 显示实时的容器资源使用情况统计流
  stop        Stop one or more running containers                                              # 停止容器
  tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE                            # 给源中的镜像打标签
  top         Display the running processes of a container                                     # 查看容器中运行的进程信息
  unpause     Unpause all processes within one or more containers                              # 取消暂停容器
  update      Update configuration of one or more containers                                   # 为容器更新配置
  version     Show the Docker version information                                              # 查看 docker 版本号
  wait        Block until one or more containers stop, then print their exit codes             # 阻塞直到一个或多个容器停止,然后打印其退出代码,即截取容器停止时的退出状态值

实战练习

作业1、使用 Docker 安装 Nginx (学习暴露端口)

1、 搜索镜像 search
[root@admin ~]# docker search --filter=stars=8000 nginx
NAME                DESCRIPTION                STARS               OFFICIAL            AUTOMATED
nginx               Official build of Nginx.   13326               [OK]                
2、 下载镜像 pull
[root@admin ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
8559a31e96f4: Pull complete 
8d69e59170f7: Pull complete 
3f9f1ec1d262: Pull complete 
d1f5ff4f210d: Pull complete 
1e22bfa8652e: Pull complete 
Digest: sha256:21f32f6c08406306d822a0e6e8b7dc81f53f336570e852e25fbe1e3e3d0d0133
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
3、 查看镜像 images
[root@admin ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              2622e6cca7eb        3 days ago          132MB
centos              latest              470671670cac        4 months ago        237MB
hello-world         latest              bf756fb1ae65        5 months ago        13.3kB
4、 启动 run  -d 后台运行  -p 指定端口 主机的3344映射到容器的80端口
[root@admin ~]# docker run -d --name nginx01 -p 3344:80 nginx
129642f969d4cff63d173ba822c096192a435830f64a6ffe77e8d6743f342767
5、 查看启动 ps
[root@admin ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
129642f969d4        nginx               "/docker-entrypoint.…"   4 seconds ago       Up 4 seconds        0.0.0.0:3344->80/tcp   nginx01
6、 测试
[root@admin ~]# curl localhost:3344
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

至此,如果使用阿里云服务器,且在安全组配置中打开了3344端口的访问,就可以直接打开nginx的页面
  • 端口暴露概念图

Docker 2020最新版 超详细教程_第20张图片

那么问题来了:难道我们每次改动 nginx 配置文件,都需要进入容器内部改动吗?那岂不是非常的麻烦,如果我能够在容器外部提供一个映射路径,让我做到在容器外部修改文件,容器内部就能够自动修改,这样就非常方便了。这就是之后要学的卷技术。

作业2、使用 docker 安装 tomcat (学习官方测试和进入容器查看内容)

Docker 2020最新版 超详细教程_第21张图片

Docker 2020最新版 超详细教程_第22张图片

Docker 2020最新版 超详细教程_第23张图片

# 官方的使用
# 我们之前的启动都是后台,停止了容器之后,容器还是可以查到
#  --rm 的意思是 用完即删 停止之后就将容器删除 一般用来测试
docker run -it --rm tomcat:9.0

# 但是我们要使用的还是要按照正常流程来使用

# 1、拉取 下载
docker pull tomcat
# 2、查看镜像
docker images
# 3、创建容器运行镜像
docker run -d -p 3355:8080 --name tomcat01 tomcat:9.0
# 4、查看
docker ps
# 5、测试访问 但是发现是404页面找不到
# 6、进去容器内一探究竟
docker exec -it tomcat01 /bin/bash
# 7、查看webapps文件夹
cd webapps
ls
# 8、发现目录是空的
# 9、原来这个官方镜像是阉割版的,很多的文件都是没有配置的
#    阿里云镜像的原因,默认是最小的镜像,所有不必要的都剔除了,保证做小可运行的环境
#      a. 少了一些Linux命令,例如 ll 命令
#      b. webapps 目录为空
#    解决办法: 容器内的根目录里面有一个 webapps.dist 文件夹,里面就包含了默认的例子
#              这样可以将文件夹里面的内容拷贝到webapps下 cd -r webapps.dist/* webapps

那么问题又来了:如果每次部署项目都要进入容器,就会非常的麻烦,那这个答案就很显而易见了,我们可以利用卷技术与外部自动同步,就不用每次都进入了。

作业3、部署 ES + kibana (学习查看容器状态和修改)

难点:
 1、es 需要暴露的端口非常多
 2、es 十分耗内存
 3、es 的数据一般需要放置到安全目录 ==> 可用挂在解决
# 1、老规矩搜索安装启动
#    --net somenetwork ? 网络配置 先去掉
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.7.1
# 启动之后 linux会非常卡 可以使用 docker stats 查看cpu状态
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
7d399bee6df4        elasticsearch       0.45%               1.229GiB / 3.7GiB   33.20%              0B / 0B             0B / 0B             50
c92ee0f0ac1c        tomcat01            0.15%               76.88MiB / 3.7GiB   2.03%               0B / 0B             0B / 0B             36
# 可以看到占用内存非常大
# 2、测试一下是否启动成功了
[root@admin ~]# curl localhost:9200
{
  "name" : "7d399bee6df4",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "BRfKhOPTS52FScc55t3vew",
  "version" : {
    "number" : "7.7.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "ad56dce891c901a492bb1ee393f12dfff473a423",
    "build_date" : "2020-05-28T16:30:01.040088Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

# 3、赶紧关闭,增加内存使用的限制  修改配置文件 -e 环境配置修改
#    就是再启动命令上增加参数,限制内存的使用
#    -e ES_JAVA_OPTS="-Xms64m Xmx512m"
docker run -d --name elasticsearch02 -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.7.1
  • 那么问题如期而至,该怎么使用kibana连接es呢?网络如何才能连接过去呢?首先要了解一个基本的模型,其次还要学习一些网络上面的知识。先为后面的内容埋下伏笔。【可以在 Docker 网络 那个章节找到答案】
    Docker 2020最新版 超详细教程_第24张图片

Docker 可视化

有两种工具:

  • portainer(先用着,不是最佳选择)
  • Rancher(CI/CD再用)

什么是 portainer ?

  • Docker 图形化界面管理工具,提供一个后台面板供我们操作
  • 安装:
# --restart=always 启动方式
# -v /var/run/docker.sock:/var/run/docker.sock 挂载
# --privileged=true 权限
docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
  • 访问测试:公网ip:8088 或者本地测试 curl localhost:8088

进入之后首先会让你创建用户,然后选择Local,就能进入一个控制面板
Docker 2020最新版 超详细教程_第25张图片

Docker 2020最新版 超详细教程_第26张图片

单词都比较简单,还是比较容易看得懂的。


Docker 镜像讲解

什么是镜像?

镜像是一种【轻量级、可执行的独立软件包】,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的【所有内容】,包括【代码、运行时、库、环境变量和配置文件】

所有的应用,直接打包docker镜像,就可以直接跑起来!

那么如何得到镜像呢?

  • 从远程仓库下载
  • 朋友拷贝
  • 自己制作一个镜像 Dockerfile

Docker 镜像加载原理

UnionFS(联合文件系统)

我们下载的时候看到的一层层的就是这个!

UnionFS(联合文件系统):是一种【分层、轻量级并且高性能】的文件系统,它支持对文件系统的修改,作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite directories into a single virtual filesystem)。UnionFS是Docker镜像的基础,镜像可以通过分层来继承,基于基础镜像(没有父镜像的镜像),可以制作各种具体的应用镜像

【特性】一次同时加载多个文件系统,但从外面看起来只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录

【例子】:比方说 mysql 和 tomcat 都需要 centos 环境,那我先安装了 mysql ,就有了 centos 的环境,那我要再安装 tomcat ,就可以共用这一层 centos ,不需要再下载 centos 。

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 等等。

Docker 2020最新版 超详细教程_第27张图片

那么问题来了,平时我们安装进虚拟机的 CentOS 都是好几个G,为什么Docker这里才200M?

Docker 2020最新版 超详细教程_第28张图片

那是因为对于一个精简的OS,RootFS可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用 Host 的 kernel,自己只需要提供 RootFS 就可以了,由此可见对于不同的 Linux 发行版, BootFS 基本是一致的,RootFS 会有差别,因此不同的发行版可以公用 BootFS。

这也就是虚拟机启动是分钟级别而容器是秒级启动的原因所在!

分层理解

分层的镜像

可以观察一下下载一个镜像的时候的日志输出,会发现是一层一层的在下载的
Docker 2020最新版 超详细教程_第29张图片

【问】为什么Docker镜像要采用这种分层的结构呢?

【答】最大的好处莫过于是资源共享了。比方说有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需要在磁盘上保留一份 base 镜像,同时内存中也只需要加载一份 base 镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。

我们可以通过 docker inspect 命令从 查看镜像分层

Docker 2020最新版 超详细教程_第30张图片

【加深理解】
所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上创建新的镜像层。

【例子】第一层镜像:基于 Ubuntu Linux 16.04 创建一个新的镜像;如果在这个镜像中添加 Python 包,就会在基础镜像层上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层,如下图。

Docker 2020最新版 超详细教程_第31张图片

在添加额外的镜像层的同时,镜像始终保持时当前所有镜像的组合,理解这一点非常重要,下图中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件

Docker 2020最新版 超详细教程_第32张图片

上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。

下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有六个文件,这是因为最上层中的文件 7 是 文件 5 的一个更新版本
Docker 2020最新版 超详细教程_第33张图片

这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。

Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。

Linux 上可用的存储引擎有 AUFS、OverLay2、Device Mapper、Btrfs、以及 ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。

Docker 在 Windows 上仅支持 windows filter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和 CoW

下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
Docker 2020最新版 超详细教程_第34张图片

特点

Docker 镜像都是【只读】的,当容器启动时,一个新的可写层被加载到镜像的顶部!

这一层就是我们通常说的【容器层】,容器之下的都叫【镜像层】
Docker 2020最新版 超详细教程_第35张图片

commit 镜像

docker commit    提交容器成为一个新的副本

docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名[tag]

测试:
前面说到 tomcat 容器的 webapps 下是没有文件的
那每次启动的时候都要将 webapps.dist 目录下的内容拷贝到 webapps 目录下
我现在自己拷贝进去了一些基本的应用   => cp -r webapps.dist/* webapps/

那我们自己打包一个镜像,方便日后使用
docker commit -a="hello223123" -m="add webapps app" 6f36f6f81aa3 tomcatnew:1.0

查看镜像
docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
tomcatnew             1.0                 78e7ed5717e7        3 seconds ago       652MB
redis                 latest              235592615444        3 days ago          104MB
tomcat                9.0                 2eb5a120304e        3 days ago          647MB
tomcat                latest              2eb5a120304e        3 days ago          647MB
可以看到我们自己生成的要大一些

要先理解概念,即使模棱两可,然后一定要去实践,最后实践和理论相结合,总结搞定这个知识点

  • 如果想要保存当前容器的状态,就可以通过 commit 来提交,获得一个镜像,下次直接使用这个镜像启动容器即可,就好比使用虚拟机时候的快照

容器数据卷

什么是容器数据卷?

【需求】

  • 很多时候我们的应用会有很多数据,如果数据放在容器里面,只要容器删除了,数据就会丢失,这个就很容易让人删库跑路了,所以我们希望【数据可以持久化】,也就是【数据存储在本地而不是容器
  • 这样就催生出容器之间的数据共享的技术,Docker 容器中产生的数据可以同步到本地!
  • 这就是卷技术,也就是数据的挂载,将容器内的目录,挂载到主机上

使用卷技术就是为了容器的持久化和同步操作!容器间的数据也可以共享!

使用数据卷

方式一:直接使用命令来挂载 (方式二在 《初始 DockerFile》)

docker run -it -v 主机目录:容器内目录

测试:将容器的home目录挂载到主机的home目录下的ceshi目录
docker run -it -v /home/ceshi:/home centos /bin/bash

进入容器后,进入 home 目录,新建一个文件
touch hello.txt
ls 查看目录下的内容,能看到这个文件
这个时候切换到主机,或者再开一个终端查看主机的 home 目录下的 ceshi 目录
cd /home/ceshi
ls 查看目录下的内容,发现 hello.txt 文件已经同步过来了

那我们再来测试,当我们关掉容器之后数据还能同步吗?
1、先 exit 退出容器
2、docker ps 查看容器是否真的停止了
3、主机上 vim /home/ceshi/hello.txt 修改文件
4、docker start 启动容器
5、容器内 cat /home/hello.txt 查看文件内容
能够发现文件内容跟之前修改的是一样的,表示文件已经同步过来了

Docker 2020最新版 超详细教程_第36张图片

  • 这个的好处就是以后修改只需要在本地修改即可,容器内会【自动同步】

MySQL 同步数据

1、先获取 mysql 镜像 这里使用 5.7 版本
docker pull mysql:5.7
2、运行容器 挂载数据和配置文件 【注意】mysql启动需要配置密码
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
3、启动成功之后,可以使用navicat来连接一下mysql 或者 docker exec -it 进入容器 mysql -p 进入 mysql 控制台
4、创建一个数据库,再查看主机上映射的路径,就能查看到刚刚新创建的数据库文件
5、来波刺激的,删除调这个容器 docker rm -f mysql01
6、再去查看刚刚的路径 cd /home/mysql/data 然后 ls 查看文件
7、能发现文件依然存在,没有丢失,这就实现了【容器数据持久化】的功能

具名和匿名挂载

  • 匿名挂载
-v 容器内路径   【不去写主机上的路径,会自动创建一个路径】
-P  【大写的P,随机指定端口】
docker run -d -P --name nginx01 -v /etc/nginx nginx
安装之后可以通过 docker volume 查看卷
可以通过 docker volume --help 查看可选项
docker volume ls 显示卷列表
能够发现卷的名称是一串字符,这种就是匿名挂载
我们在 -v 的时候只写了容器内的路径,没有写容器外的路径

Docker 2020最新版 超详细教程_第37张图片

  • 具名挂载
如果路径前面有 / 则代表是绝对路径,如果没有,就代表只是一个名字,而不是一个目录
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx

再查看卷列表 docker volume ls
就能看到我们命名为 juming-nginx 的卷

通过 -v 卷名:容器内路径 来为卷命名

那我们也可以通过这个卷名来查看路径
docker volume inspect juming-nginx

Docker 2020最新版 超详细教程_第38张图片

  • 所有的 docker 容器内的卷,没有指定目录的情况下都是在:【/var/lib/docker/volumes/xxxx/_data
  • 我们通过具名挂载可以方便的找到我们的一个卷,大多数情况也是在使用【具名挂载】

那么问题来了,我们该如何确定是具名挂载还是匿名挂载,亦或是指定路径挂载呢?

-v 容器内路径     =>  匿名挂载
-v 卷名:容器内路径     =>  具名挂载
-v /宿主机路径:容器内路径     =>  指定路径挂载 会有一个 / 表示绝对路径

【拓展】:

  • 有时候我们会遇到这样的一个命令,路径后面跟着一个 ro 或 rw
    docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
    docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
  • 这个可以用来改变读写权限 【read-only】 和 【read-write】,一旦设置了容器的权限,容器对我们挂载出来的内容就有限定了
  • ro 只要看到 ro 就说明这个路径只能通过宿主机来操作,容器内部是无法操作的,默认为 rw

初识 DockerFile

Docker File 就是用来【构建 docker 镜像】的构建文件。是一段【命令脚本】,可以通过这个脚本生成镜像。

镜像是一层一层的,那么对应的,这个脚本就是一个个的命令,每个命令就是镜像的一层。

方式二:在自己搭建的镜像中设置 启动镜像的时候就自动挂载

我们先来做个小测试:
Docker 2020最新版 超详细教程_第39张图片

在这里插入图片描述

  • 那我们来启动一下这个镜像,查看一下内容

Docker 2020最新版 超详细教程_第40张图片

  • 既然是数据卷,那么在外部就一定有一个同步的目录,另外,因为我们【只写了容器内的目录,所以这是个匿名挂载】,那我们找的话,卷名应该是一个不规则的字符串。

  • 还记得我们前面的 inspect 命令吗,它能查看镜像的详细信息,其中有一个 Mounts 节点,保存了挂载点的信息,我们就可以在这里去查看挂载点的信息【先docker ps查看镜像ID,再docker inspect 镜像ID查看】
    Docker 2020最新版 超详细教程_第41张图片

  • 我们再创建一个文件试试
    Docker 2020最新版 超详细教程_第42张图片

  • 这种方式以后使用会十分多,因为我们通常会构建自己的镜像,加入构建镜像的时候没有挂在卷,要手动挂载【-v 卷名:容器内路径】

数据卷容器

  • 【使用场景】:多个mysql 同步数据

Docker 2020最新版 超详细教程_第43张图片

【上手操作】

通过我们刚才生成的镜像来启动两个容器 docker01 和 docker02
docker run -it --name docker01 centosdemo
docker run -it --name docker02 --volumes-from docker01 centosdemo
然后进入 docker01 中,进入 volume01 目录下,新建一个 docker01 文件
再进入 docker02 中,进入 volume01 目录下,查看目录内容
能发现两个容器之间的数据已经同步了

Docker 2020最新版 超详细教程_第44张图片

Docker 2020最新版 超详细教程_第45张图片

  • 那也许有人要问了:啊,那我能不能再来个 docker03 再去挂载 docker01 呢?那就来尝试一下!
    Docker 2020最新版 超详细教程_第46张图片

  • 所以我们只要通过 --volumes-from 创建容器,就能实现容器之间的数据共享。【注意】这个数据共享是即使创建这个文件的容器被删了,文件依旧存在。

  • 更像是一种引用,数据卷容器挂载到宿主机的目录中,然后其他的容器挂载到数据卷容器时,使用的是对该文件的引用(数据地址的拷贝),如果是将文件进行拷贝,不仅效率低,而且浪费空间。

多个MySQL实现数据共享

docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7

docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7

小结

  • 容器之间配置信息的传递可以通过数据卷进行集中管理,或者实现复用
  • 数据卷容器的生命周期会一直持续到没有容器使用位置,但是一旦你持久化到了本地,这个时候,本地的数据是不会删除的
  • 本章主要讲解了两种挂载方式,一个是通过 -v 命令来挂载,另一个就是我们再构建 DockerFile 的时候将挂载目录写入镜像中。
  • 其中,挂载有两种方式:具名挂载和匿名挂载
  • 然后,还介绍了容器之间进行数据共享的方式【 --volume-from 挂载到数据卷容器】

Docker File

DockerFile 介绍

DockerFile 的核心是用来构建 docker 镜像的文件,是一个命令参数脚本

构建步骤:
1、编写一个 dockerfile 文件
2、docker build 构建成为一个镜像
3、docker run 运行镜像
4、docker push 发布镜像(DockerHub、阿里云镜像仓库)

  • 我们可以查看官方是怎么做的:
    Docker 2020最新版 超详细教程_第47张图片
    Docker 2020最新版 超详细教程_第48张图片

  • 很多官方镜像都是基础包,很多功能是没有的,通常我们都会自己搭建一个镜像!

DockerFile 构建过程

基础知识

  • 每个保留关键字(指令)必须是大写字母

  • 指令是从上到下 顺序执行

  • #号表示注释

  • 每一个指令都会创建提交一个新的镜像层
    Docker 2020最新版 超详细教程_第49张图片

  • dockerfile 是面向开发的,我们以后发布项目,做镜像,就需要编写 dockerfile 文件,这个文件十分简单

相关名词解释

Dockerfile:构建文件,定义了一切的步骤,相当于源代码
DockerImages:通过 Dockerfile 构建生成的镜像,最终发布和运行的产品,相当于原来的 war包、jar包
Docker容器:容器就是镜像运行起来提供服务的

DockerFile 指令

FROM           基础镜像 例如 centos/ubuntu  一切从这里开始构建
MAINTAINER     镜像是谁写的,一般是姓名+邮箱
RUN            镜像构建的时候需要运行的命令
ADD            步骤 比如说我们要添加tomcat,那就需要一个tomcat的压缩包,这个压缩包就是要添加的内容
WORKDIR        镜像的工作目录
VOLUME         挂载的目录
EXPOSE         指定暴露端口
CMD            指定这个容器启动的时候要运行的命令,只有最后一个会生效,会被替代
ENTRYPOINT     指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD        当构建一个被继承的 dockerfile 这个时候就会运行 ONBUILD 指令
COPY           类似 ADD 命令,将文件拷贝到镜像中
ENV            构建的时候设置环境变量

Docker 2020最新版 超详细教程_第50张图片

  • 接下来我们就可以写一个自己的镜像啦!

实战:构建一个自己的CentOS

  • 我们先看一下官方的 CentOS 的 DockerFile
    Docker 2020最新版 超详细教程_第51张图片

  • DockerHub 中绝大部分的镜像都是从这个基础镜像过来的 FROM scratch ,然后配置需要的软件和配置来进行构建

  • 那我们就基于官方的这个镜像,再加一些我们需要的环境

1、先进入一个目录,创建一个 dockerfile 目录迎来存放一些文件
   cd /home
   mkdir dockerfile
2、创建一个dockerfile文件,命名就随便,这里命名为 mydockerfile
   vim mydockerfile
3、开始写指令
   FROM centos      # 基础镜像
   MAINTAINER hey<[email protected]>    # 作者信息

   ENV MYPATH /usr/local    # 创建一个变量 存放一个值
   WORKDIR $MYPATH    # 启动后的工作目录,就是进入容器后的默认目录

   RUN yum -y install vim    # 执行指令安装 vim
   RUN yum -y install net-tools    # 执行指令安装 net-tools

   EXPOSE 80    # 暴露端口

   CMD echo $MYPATH    # 输出 MYPATH 变量
   CMD echo "------end---------"    # 输出信息
   CMD /bin/bash    # 启动后用bash命令行
4、构建镜像
   docker build -f mydockerfile -t mycentos:0.1 .
5、构建成功
   Successfully built fa1d3cda51dc
   Successfully tagged mycentos:0.1
6、测试运行
   docker run -it mycentos:0.1
7、我们可以通过 history 命令查看一下运行过的指令
   docker history 镜像ID

CMD 和 ENTRYPOINT 的区别

  • 【CMD】指定这个容器启动的时候要运行的命令,只有最后一个会生效,会被替代
  • 【ENTRYPOINT】指定这个容器启动的时候要运行的命令,可以追加命令

以下步骤建议上手操作一下

1、创建一个 dockerfile 
   FROM centos
   CMD ["ls","-a"]
2、构建镜像
   docker build -f dockercmd -t cmdtest .
3、直接运行镜像
   docker run a9d76c1b34d2
4、能发现 ls -a 命令生效了
5、我们现在想追加一个 -l 命令,就是执行 ls -al 命令
   根据之前的内容,我们可以在 docker run 命令后面加上 -l 命令作为参数
   即 docker run a9d76c1b34d2 -l
   但是当我们确认执行过后,却发现报错了
   docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: 
   starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
   ERRO[0000] error waiting for container: context canceled 
   为什么呢?因为 CMD 的情况下,-l 替换了 CMD ["ls","-a"] 命令,但是 -l 又不是命令,所以就报错了。
   那我们可以直接加上完整的命令进行追加【docker run a9d76c1b34d2 ls -al】这样就能执行成功了,不过非常麻烦
6、引入 ENTRYPOINT 进行操作,重新创建一个 dockerfile
   FROM centos
   ENTRYPOINT ["ls","-a"]
7、重新构建运行
   docker build -f dockerentrypoint -t entrypointtest .
   docker run 7855e875f6e1
   截至到这一步为止,结果都跟使用CMD没有什么差别
8、docker run 命令后追加 -l
   docker run 7855e875f6e1 -l
   会发现结果不仅没报错,而且还跟执行 ls -al 一样,这样就能看出这两个命令之间的差距
   表示这里 -l 命令是直接拼接在 ENTRYPOINT 命令的后面的

Dockerfile 中很多命令都是十分相似的,我们需要了解他们的区别,最好的学习方法就是对比学习然后测试效果。

实战:制作一个 Tomcat 镜像

  1. 准备镜像文件:tomcat 安装包、jdk 安装包(tar包)
  2. 编写 dockerfile 文件,官方命名【Dockerfile】,build 会自动寻找这个文件,就不需要 -f 指定了
FROM centos   # 基本镜像
MAINTAINER hey<[email protected]>    # 作者信息

COPY readme.txt /usr/local/readme.txt    # 复制readme到容器内部的这个路径
ADD /home/dockerfile/jdk-8u11-linux-x64.tar.gz /usr/local/    # ADD 命令会自动解压 将tar包解压到这个路径
ADD /home/dockerfile/apache-tomcat-9.0.22.tar.gz /usr/local/

RUN yum -y install vim    # 安装一些基本命令

ENV MYPATH /usr/local     # 设置默认的工作目录
WORKDIR $MYPATH

ENV JAVA_HOME /usr/local/jdk1.8.0_11    # java 环境变量
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/toos.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22    # tomcat 环境变量
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.22
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
# 暴露 tomcat 端口
EXPOSE 8080
# 启动 tomcat 可以通过 && 拼接一些参数  tail -F 显示文件新追加的内容
CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.22/bin/logs/catalina.out
  1. 构建镜像【doocker build -t diytomcat .】文件命名为Dockerfile后可以不用 -f 指定文件,能自动匹配
  2. 构建完后就运行【docker run -d -p 9090:8080 --name testtomcat -v /home/tomcat/test:/usr/local/apache-tomcat-9.0.22/webapps diytomcat
  3. 访问测试:进入容器内查看目录【docker exec -it 容器id /bin/bash】【ls -l
  4. 发布项目,因为我们将 webapps 目录挂载在宿主机的一个目录,所以可以直接在本地编写项目
  5. 访问 tomcat 服务器内的应用,测试查看是否成功

发布自己的镜像到 DockerHub

地址:https://hub.docker.com/

  1. 要先注册一个账号
  2. 确定这个账号可以登录
  3. 在我们的服务器上提交自己的镜像
1、先在命令行上登录 输入命令后会让你输入密码,出现 Login Succeeded 表示登录成功
   docker login -u flow11
2、提交镜像 docker push 不带版本号就是提交最新版 latest 
   这里就要求我们在构建镜像的时候就带上账号名,以防混乱
   docker push flow11/mycentos
   如果没带上 tag,默认是 latest,推荐加上tag
   重新打 tag 的命令:  docker tag flow11/mycentos flow11/mycentos:1.0

发布到阿里云镜像仓库上

  1. 登录阿里云

  2. 找到容器镜像服务
    Docker 2020最新版 超详细教程_第52张图片

  3. 创建命名空间,为了隔离防止镜像冲突
    在这里插入图片描述

  4. 创建镜像仓库:填写信息,选择【本地仓库】
    Docker 2020最新版 超详细教程_第53张图片

  5. 浏览一下这个仓库信息【点击蓝色的仓库名】
    Docker 2020最新版 超详细教程_第54张图片

  6. 跟着操作指南来操作,提交镜像,【参考官方文档即可】
    docker push flow11/mycentos:latest

小结

  • 一个流程图能很好的梳理这一章的知识点
    Docker 2020最新版 超详细教程_第55张图片

Docker 网络

理解【Docker0】

  • Docker 网络的核心就是 【Docker0】

  • 我们可以用 ip addr 查看一下宿主机的 ip 信息
    Docker 2020最新版 超详细教程_第56张图片

  • 【问题】 docker 是如何处理容器网络访问的?

  • 比如说现在有两个容器(A 容器和B 容器),那么现在 A 容器的 Tomcat 里面的应用要访问 B 容器的 MySql,这里是怎么进行连接的呢?是用上面三个地址的哪一个地址进行访问的呢?

  • 前面我们配置 ES 的时候也提出过这样的问题,怎么样让 kibana 连接上 ES 呢?

1、我们先运行一个 tomcat 镜像,再查看一下网卡
   docker run -d -P --name tomcat01 tomcat
   docker exec -it tomcat01 ip addr
   我们能发现容器启动的时候会得到一个 eth0@if81 这样的标识,这是 docker 分配的
   ----- 运行结果 -----
   1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
       link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
       inet 127.0.0.1/8 scope host lo
          valid_lft forever preferred_lft forever
   80: eth0@if81: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
       link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
       inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
          valid_lft forever preferred_lft forever
   -------------------
2、我们来试一试能不能 ping 容器内部
   ping 172.17.0.2
   发现能够 ping
  • 【分析】

    • 我们看回前面的 docker0 的地址,发现是 172.17.0.1 ,能发现这个跟我们平时路由器的地址 192.168.0.1 很像
    • 当我们用手机连上这个路由器的 wifi 的时候,分配的 ip 地址就是 192.168.0.3 之类的,这两个 ip 在一个网段内,就能互相 ping 通
    • 同样的道理,我们测试的容器的 ip 地址是 172.17.0.2 ,这个跟 172.17.0.1 是同一个网段的,那肯定能 ping 通了
    • 每当我们启动一个 docker 容器,docker 就会给容器分配一个 ip ,我们只要安装了 docker,就会有一个网卡 docker0
    • 这个网卡使用的是桥接模式,使用的技术是 veth-pair 技术
  • 当我们创建完容器之后再使用 ip addr 查看网卡
    Docker 2020最新版 超详细教程_第57张图片

  • 有没有发现出什么!!??

  • 我们创建完容器之后查看到的网卡中有一个是【80: eth0@if81】,刚好就和上图的网卡是相反的

  • 那我们再启动一个 tomcat 容器呢?
    Docker 2020最新版 超详细教程_第58张图片

  • 发现又多了一对网卡,注意,是【一对】!这种一对一对出现的网卡就是因为使用了 veth-pair 技术

  • veth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一端连着协议,一端彼此相连

  • 正因为有这个特性,通常用这个技术来充当桥梁,连接各种虚拟网络设备

  • 例如 OpenStack Docker容器之间的连接 ovs的连接 都是使用了 veth-pair 技术

  • 我们来试试在 tomcat01 ping tomcat02
    Docker 2020最新版 超详细教程_第59张图片

  • 所以容器和容器之间是可以 ping 通的!
    Docker 2020最新版 超详细教程_第60张图片

  • 当我们在 2 去 ping 3 的时候,或先通过 veth-pair技术 从 261 转发到 262 上,1 再通过保存的端口注册信息,再从 264 发到 263 ,这样就完成了一次数据通信,而不是直接从 2 发送到 3 上面,这就是桥接的概念。

  • 这里有两种机制,一个类似路由转发,直接转发到特定的端口上面;一个是广播,散发出去,查看那个端口接收处理了。

  • 如果学过《计算机网络》的会比较好理解,不懂的话建议自己去找一些资料看一下。

  • 所有容器在不指定网络的情况下,都是 docker0 路由的,docker 会给我们的容器分配一个默认的可用 ip

  • 可以使用 -net 指定网络

小结

  • Docker 使用的是 Linux 的桥接,宿主机中是一个 Docker 容器的网桥【docker0】
  • Docker 中的所有的网络接口都是虚拟的,而且虚拟的转发效率高!
  • 只要容器删除了,对应的一对网桥就没了
    Docker 2020最新版 超详细教程_第61张图片

容器互联 --link 命令

思考一个场景,我们编写了一个微服务,需要通过一个URL连接数据库,那每次启动容器,就会重新分配 IP,IP就会变动,URL地址也就会失效了。

  • 在学习 springcloud 的时候,学过 feign ,feign 调用是通过服务名来调用的,就是后面的 IP 不管怎么变,我都能通过固定的服务名来调用
  • 那么类似的,如果我们能够通过【容器名】来 ping 通,就能做到即使 IP 改变了,我依然能够连接上去
  • 这样我们也就能实现【高可用】了
1、我们先来尝试一下能不能通过容器名 ping[root@admin ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known
发现不能 ping 通

2、新创建一个 tomcat03 容器 使用 --link 
[root@admin ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
d6f626ab5aa0f56f48e56ae2c2603444d19c2c96e7e56a5fa15bcbbf692d0869

3、在 tomcat03 使用容器名 ping tomcat02 
[root@admin ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.121 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.078 ms
发现 ping 通了!!!

4、但是,当我们在 tomcat02 使用容器名 ping tomcat03 时,结果却不如我们所愿
[root@admin ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
结果却 ping 不通了
  • 这是因为在 tomcat02 中没有配置,我们在 tomcat03 中配置了 --link ,但是在 02 中却没有配置

【探究】

  • 我们来查看一下 docker 的网络的元数据信息
1、查看 docker 网络列表
   docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
7bc4edf2a0e8        bridge              bridge              local
de44533d6de3        host                host                local
32983fccf962        none                null                local

2、我们查看一下第一个
   docker network inspect 7bc4edf2a0e8

Docker 2020最新版 超详细教程_第62张图片

  • 继续往下翻,看看后面还有什么

Docker 2020最新版 超详细教程_第63张图片

  • 这里我们能看到三个容器的网络信息

  • 接下来我们查看一下我们刚刚创建的 tomcat03 容器的配置信息,其中在 HostConfig 里面有一个 Links
    在这里插入图片描述

  • 根据 DNS解析 的流程,当我们访问一个域名/地址,会首先去查看 host 文件,那我们也能查看一下 tomcat03 的 host 文件
    Docker 2020最新版 超详细教程_第64张图片

  • 这就代表着我们只要请求 tomcat02,就会直接转发到 172.17.0.3,所以就可以 ping 通

  • --link 就是我们在 host 配置中增加了 tomcat02 的映射

  • 当 IP 变了,就表示容器重启了,那么--link重新链接,配置文件也就自动改了

  • 我们可以再看一下 tomcat02 没有配置的容器的 host 文件和镜像元数据中的连接配置信息
    Docker 2020最新版 超详细教程_第65张图片
    在这里插入图片描述

  • 可以发现,tomcat02 里面就没有这些配置

  • 其实这个的本质就是 host 映射,这种方式太笨了,【不建议使用】,我们需要更高级的

  • 我们需要的是【自定义网络】,不适用【docker0】,docker0 是官方的网桥,是局限的

  • 比方说 docker0 【不支持容器名连接访问】

  • 所以进阶的操作是使用【自定义网络】

自定义网络

查看所有的 docker 网络

在这里插入图片描述

  • 网络模式

    • bridge :桥接模式【 docker上搭桥(默认)】
    • host:主机模式【和宿主机共享网络】
    • none:不配置网络
    • container:容器内网络连通【局限大,用的少,不推荐】
  • 测试

之前我们直接启动的命令 默认是加上 --net bridge 的 这个就是我们的 docker0
docker run -d -P --name tomcat01 --net bridge tomcat

docker0 是默认的,域名不能访问的 我们可以使用 --link 可以打通连接

我们可以自定义一个网络
--driver bridge  表示使用桥接模式
--subnet 192.168.0.0/16  表示子网ip   可以分配 192.168.0.2 到 192.168.255.255
--gateway 192.168.0.1  表示网关
mynet  表示网络名
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet

创建好过后我们可以查看一下

Docker 2020最新版 超详细教程_第66张图片

Docker 2020最新版 超详细教程_第67张图片

我们可以使用我们创建的自定义网络来启动一个容器
docker run -d -P --name tomcat-mynet --net mynet tomcat
docker run -d -P --name tomcat-mynet-02 --net mynet tomcat

再查看 mynet 的元数据就能看见两个容器
docker network inspect mynet

Docker 2020最新版 超详细教程_第68张图片

  • 那我们为什么要使用自定义的网络呢?
  • 我们再测试一下去 ping 容器

Docker 2020最新版 超详细教程_第69张图片

  • 我们不使用 --link 也可以通过容器名来 ping 了
  • 我们自定义的网络,docker都已经帮我们维护好了对应的关系,推荐我们以后这样来使用网络
  • 这样的好处就是 以后我们搭建集群的时候,不同的集群可以使用不同的网络,从而保证集群是安全和健康的

Docker 2020最新版 超详细教程_第70张图片

网路连通

  • 像前面说的那种情况,假设我的Redis集群中的一个Redis想要访问Tomcat,那该怎么办呢?

  • 两个不同的网段之间直接 ping 是肯定不能通的

  • 那我们可以查看一下 docker network 里面的命令
    Docker 2020最新版 超详细教程_第71张图片

  • 那我们来使用命令测试一下 打通 tomcat-mynet,这里我们先要用正常的方法先创建两个容器 tomcat01 和 tomcat02

docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat02 tomcat

Docker 2020最新版 超详细教程_第72张图片

Docker 2020最新版 超详细教程_第73张图片

Docker 2020最新版 超详细教程_第74张图片

  • 就是连通之后,将 tomcat01 放到了 mynet 网络下

  • 就是【一个容器,两个地址】,就好像我们的阿里云服务器那样,一个公网ip,一个私网ip

  • 那我们再来 ping 一下试试
    Docker 2020最新版 超详细教程_第75张图片

  • 想让 tomcat02 能 ping 通 tomcat-mynet,只需将 tomcat02 connect 到 mynet 上即可

实战:部署 Redis 集群

Docker 2020最新版 超详细教程_第76张图片

1、创建 redis 的网络
   docker network create redis --subnet 172.38.0.0/16
2、使用 shell 脚本创建 6 个 redis 容器,并配置
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379 
bind 0.0.0.0
cluster-enabled yes 
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
3、使用配置文件创建 6 个 redis 容器
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

docker run -p 6372:6379 -p 16372:16379 --name redis-2 \
-v /mydata/redis/node-2/data:/data \
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

docker run -p 6373:6379 -p 16373:16379 --name redis-3 \
-v /mydata/redis/node-3/data:/data \
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

docker run -p 6374:6379 -p 16374:16379 --name redis-4 \
-v /mydata/redis/node-4/data:/data \
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

docker run -p 6375:6379 -p 16375:16379 --name redis-5 \
-v /mydata/redis/node-5/data:/data \
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

4、创建集群
先进入 redis-01,redis 官方镜像没有 bash ,要用 sh
docker exec -it redis-01 /bin/sh
输入命令 配置集群
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172
.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
显示OK即可
进入 集群
redis-cli -c
查看结点
cluster nodes
可以看到三个 master 主机;三个 slave 从机
5、测试 我们来设个值,然后存值的那个主机停掉,再看看能不能获取到那个值
插值
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.38.0.13:6379
OK
在宿主机停掉 172.38.0.13 对应的 redis-3
[root@admin ~]# docker stop redis-3
redis-3
再去获取值
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b"
获取成功了!能看到拿到值的地方是 172.38.0.14 ,这个就是 redis-3 的从机,他顶替了 redis-3

6、我们再使用 cluster nodes 查看结点信息
可以看到 172.38.0.13 显示 master,fail
再看到 172.38.0.14 之前是显示 slave 的,但是现在却显示 master,就说明 redis-4 顶替了 redis-3 成为了主机

Docker 2020最新版 超详细教程_第77张图片

SpringBoot 微服务打包 Docker 镜像

总共来说就是五个步骤:

  1. 构建 springboot 项目
  2. 打包应用(这里是 jar 包 / war 包)
  3. 编写 dockerfile(可以直接在 idea 上面写,只需要安装一个插件 Docker integration)
    Docker 2020最新版 超详细教程_第78张图片
  4. 构建镜像
    将项目 jar 包和刚刚编写的 dockerfile 上传到服务器上,然后 build 构建镜像
  5. 发布运行
    docker run
  6. 测试容器

你可能感兴趣的:(Docker 2020最新版 超详细教程)