Video Reference:Docker最新超详细版教程通俗易懂

学前准备

  • Linux 基本操作
  • 计算机网络基础

Docker 的学习

  • Docker 概述
  • Docker 安装
  • Docker 命令
    • 镜像命令
    • 容器命令
    • 仓库命令
    • 操作命令
    • ……
  • Docker 镜像
  • 容器的数据卷
  • Dockerfile
  • Docker 网络
  • IDEA 整合 Docker
  • Docker Compose
  • Docker Swarm ( 简化版的 Docker )
  • CICD( Jenkins )

授人以鱼不如授人以渔(学习思想!)即使再小的帆也能远航。

1 Docker 概述

1.1 Docker 为什么会出现?

一款产品:开发 - 上线,三套环境(开发,测试,生产),应用配置

开发人员,运维人员。问题:我在我的电脑上可以运行的!版本更新,导致服务不可用,对于运维来说,考验就十分大了?开发即运维!

环境配置是十分的麻烦,每一个机器都是要部署环境(集群 Redis,ES,Hadoop……)!费时费力

发布一个项目的 Jar (Redis,MySQL,JDK,ES)包。项目能不能带上环境安装打包!

之前在服务器配置一个应用的环境Redis MySQL jdk ES Hadoop,配置超麻烦了,不能够跨平台。Windows 最后分布到Linux!

  • 传统:开发 jar,运维来做!
  • 现在:开发打包部署上线,一套流程做完!

Java —- Apk —- 分布(应用商店)—- 同学使用 Apk —- 安装即可用!

Java —- Jar(环境) —- 打包项目带上环境(镜像)—-( Docker仓库:商店)—- 下载我们发布的就像 —- 直接运行即可!

Docker 给以上的问题,提出了解决方案!

一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的迭代之后,不同版本环境的兼容,对运维人员是极大的考验!

环境配置如此麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。解决开发人员说的”在我的机器上可正常工作”的问题。

之前在服务器配置一个应用的运行环境,要安装各种软件,就拿一个基本的工程项目的环境来说吧,Java/Tomcat/MySQLJDBC驱动包等。安装和配置这些东西有多麻烦就不说了,它还不能跨平台。假如我们是在Windows上安装的这些环境,到了Linux又得重新装。况且就算不跨操作系统,换另一台同样操作系统的服务器,要移植应用也是非常麻烦的。

传统上认为,软件编码开发/恻试结束后,所产出的成果即是程序或是能够编译执行的二进制字节码文件等(Java为例)。而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让维运团队得以部署应用程式,开发需要清楚的告诉运维部署团队用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。

Docker之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案。

Docker镜像的设计,使得Docker得以打破过去「程序即应用」的观念。通过Docker镜像 ( images )将应用程序所需要的系统环境,由下而上打包,达到应用程序跨平台间的无缝接轨运作。

💫1 Docker最新超详细版教程通俗易懂 - 图1

Docker 的思想就来自于集装箱!

JRE —- 多个应用(端口冲突) —- 原来都是交叉的!

隔离:Docker 核心思想!打包装箱!每个箱子都是相互隔离的。Docker 通过隔离机制,可以将服务器的性能利用到极致

本质:所有的技术都是因为出现了一些问题,我们需要去解决,才去学习!

1.2 Docker 的历史

2010年,几个搞IT的年轻人,在美国旧金山成立了一家名叫“dotCloud”的公司。

这家公司主要提供基于PaaS的云计算技术服务。具体来说,是和LXC有关的容器技术。后来,dotCloud公司将自己的容器技术进行了简化和标准化,并命名为——Docker。

Docker技术诞生之后,并没有引起行业的关注。而dotCloud公司,作为一家小型创业企业,在激烈的竞争之下,也步履维艰。正当他们快要坚持不下去的时候,脑子里蹦出了“开源”的想法。

什么是开源?开源,就是开放源代码。也就是将原来内部保密的程序源代码开放给所有人,然后让大家一起参与进来,贡献代码和意见。

有的软件是一开始就开源的。也有的软件,是混不下去,创造者又不想放弃,所以选择开源。自己养不活,就吃”百家饭”嘛。2013年3月,dotCloud公司的创始人之一,Docker之父,28岁的Solomon Hykes正式决定,将Docker项目开源。

不开则已,一开惊人。

越来越多的IT工程师发现了Docker的优点,然后蜂拥而至,加入Docker开源社区。Docker的人气迅速攀升,速度之快,令人瞠目结舌。

开源当月,Docker 0.1版本发布。此后的每一个月,Docker都会发布一个版本。到2014年6月9日,Docker 1.0版本正式发布

此时的Docker,已经成为行业里人气最火爆的开源技术,没有之一。甚至像Google、微软、Amazon、VMware这样的巨头,都对它青睐有加,表示将全力支持。I

Docker和容器技术为什么会这么火爆?说白了,就是因为它“轻”。

在容器技术之前,业界的网红是虚拟机。虚拟机技术的代表,是VMWare和Openstack。

相信很多人都用过虚拟机。虚拟机,就是在你的操作系统里面,装一个软件,然后通过这个软件,再模拟一台甚至多台”子电脑”出来。在”子电脑’里,你可以和正常电脑一样运行程序,例如开QQ。如果你愿意,你可以变出好几个”子电脑”,里面都开上QQ。”子电脑”和“子电脑”之间,是相互隔离的,互不影响。

虚拟机属于虚拟化技术。而Docker这样的容器技术,也是虚拟化技术,属于轻量级的虚拟化。

虚拟机虽然可以隔离出很多”子电脑”,但占用空间更大,启动更慢,虚拟机软件可能还要花钱(例如VMWare)。而容器技术恰好没有这些缺点。它不需要虚拟出整个操作系统,只需要虚拟一个小规模的环境(类似”沙箱”)。

它启动时间很快,几秒钟就能完成。而且,它对资源的利用率很高(一台主机可以同时运行几千个Docker容器)。此外,它占的空间很小,虚拟机一般要几GB到几十GB的空间,而容器只需要MB级甚至KB级。

正因为如此,容器技术受到了热烈的欢迎和追捧,发展迅速。

:::color1 VMware:Linux CentOS原生镜像(一台电脑!)隔离:需要开启多个虚拟机!—> nG 几分钟

Docker:隔离,镜像(最核心的环境 4M + JDK + MySQL)十分的小巧,运行镜像就可以了!小巧!—> nM/KB 秒级启动

:::

到现在,所有的开发人员和运维人员必须都要会 Docker!

Docker 三要素:容器,镜像,仓库

聊聊 Docker

Docker 是基于 Golang 语言开发的!开源项目!

官网:https://www.docker.com/

文档地址:https://docs.docker.com/ (Docker 的文档是非常详细的!)

仓库地址:https://docs.docker.com/

1.3 Docker 能干嘛

虚拟化技术分全虚拟、半虚拟、容器技术三大类

:::color1 传统的虚拟化技术

虚拟机(virtual machine)就是带环境安装的一种解决方案。

它可以在一种操作系统里面运行另一种操作系统,比如在Windows系统里面运行Linux系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。

:::

画板

虚拟机技术缺点:

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

:::color1 容器化技术

由于前面虚拟机存在这些缺点,Linux发展出了另一种虚拟化技术: Linux容器(Linux Containers,缩写为LXC).

Linux容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能妊终如一地运行。

容器化技术不是模拟的一个完整的操作系统。

:::

画板

💫1 Docker最新超详细版教程通俗易懂 - 图4

比较Docker 和虚拟机技术的不同:

  • ·传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程
  • 而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
  • 每个容器之间互相隔离,每个容器有自己的文件系统,容器之间进程不会相互影响,能区分计算资源。

:::color1 开发/运维(DevOps)

更快速的应用交付和部署:

传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂的配置才能正常运行。Docker化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间。

更便捷的升级和扩缩容:

随着微服务架构和Docker的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个Docker容器将变成一块”积木”,应用的升级将变得非常容易。当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒级。

更简单的系统运维:

应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复。

更高效的计算资源利用:

Docker是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的Hypervisor [管理程序]支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的CPU和内存的利用率。

:::

2 Docker 安装

2.1 Docker 的基本组成

💫1 Docker最新超详细版教程通俗易懂 - 图5

  • 镜像(Image):

Docker 镜像就好比是一个模板,可以通过这个模板来创建容器服务,Tomcat 镜像 —> run —> tomcat01容器(提供服务)。通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器重的)。

  • 容器(Container):

Docker 利用容器技术,独立运行一个或者一个组应用,通过镜像来创建的。

启动、停止、删除、基本命令!

目前就可以把这个容器理解为就是一个简易的Linux系统项目

  • 仓库(Repository):

仓库就是存放镜像的地方。仓库分为公有仓库和私有仓库!

公有仓库:Docker Hub(默认是国外的)。阿里云、网易云都有容器服务器(配置镜像加速!)

2.2 安装 Docker

环境准备

  1. 需要会一部分Linux的基础
  2. CentOS 7
  3. 我们可以使用远程连接工具连接远程服务器进行操作!

环境查看

  1. #系统内核是 3.10 以上的
  2. $ uname -r
  3. 3.10.0-1160.el7.x86_64
  1. #系统版本
  2. $ cat /etc/os-release
  3. NAME="CentOS Linux"
  4. VERSION="7 (Core)"
  5. ID="centos"
  6. ID_LIKE="rhel fedora"
  7. VERSION_ID="7"
  8. PRETTY_NAME="CentOS Linux 7 (Core)"
  9. ANSI_COLOR="0;31"
  10. CPE_NAME="cpe:/o:centos:centos:7"
  11. HOME_URL="https://www.centos.org/"
  12. BUG_REPORT_URL="https://bugs.centos.org/"
  13. CENTOS_MANTISBT_PROJECT="CentOS-7"
  14. CENTOS_MANTISBT_PROJECT_VERSION="7"
  15. REDHAT_SUPPORT_PRODUCT="centos"
  16. REDHAT_SUPPORT_PRODUCT_VERSION="7"

安装Docker

帮助文档:

  1. #1.卸载旧的版本
  2. $ sudo yum remove docker \
  3. docker-client \
  4. docker-client-latest \
  5. docker-common \
  6. docker-latest \
  7. docker-latest-logrotate \
  8. docker-logrotate \
  9. docker-engine
  10. #2.需要的安装
  11. $ sudo yum install -y yum-utils
  12. #3.设置镜像的仓库
  13. $ sudo yum-config-manager \
  14. --add-repo \
  15. https://download.docker.com/linux/centos/docker-ce.repo #默认是国外的
  16. $ sudo yum-config-manager \
  17. --add-repo \
  18. https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo #推荐使用阿里云的,速度快
  19. #4.更新yum软件包的索引
  20. $ yum makecache fast
  21. #5.安装Docker相关的 Docker-ce社区版,ee则是企业版
  22. $ sudo yum install -y \
  23. docker-ce docker-ce-cli containerd.io docker-compose-plugin
  24. #6.启动Docker
  25. $ systemctl start docker
  26. #7.查看Docker的信息
  27. $ docker version
  28. #判断Docker是否安装成功
  29. #8.运行helloworld
  30. $ sudo docker run hello-world

💫1 Docker最新超详细版教程通俗易懂 - 图6

💫1 Docker最新超详细版教程通俗易懂 - 图7

  1. #9.查看镜像是否存在hello-world镜像
  2. $ docker images
  3. REPOSITORY TAG IMAGE ID CREATED SIZE
  4. hello-world latest feb5d9fea6a5 11 months ago 13.3kB
  5. #10.卸载Docker
  6. #卸载依赖
  7. yum remove -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
  8. #删除资源
  9. sudo rm -rf /var/lib/docker
  10. #/var/lib/docker是Docker默认的工作路径
  11. #最新版
  12. sudo rm -rf /var/lib/containerd

2.3 阿里云镜像加速

  1. 登录阿里云找到容器服务
  2. 找到镜像加速地址

💫1 Docker最新超详细版教程通俗易懂 - 图8

  1. 配置使用
  1. sudo mkdir -p /etc/docker
  2. sudo tee /etc/docker/daemon.json <<-'EOF'
  3. {
  4. "registry-mirrors": ["https://po13h3y1.mirror.aliyuncs.com"]
  5. }
  6. EOF
  7. sudo systemctl daemon-reload
  8. sudo systemctl restart docker

2.4 回顾HelloWorld流程

画板

2.5 Docker 底层原理

2.5.1 Docker 是怎么工作的?

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

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

画板

Docker 为什么比 VM 快?

  1. Docker 有着比虚拟机更少的抽象层
  2. Docker 利用的是宿主机的内核,VM需要的是Guest OS

💫1 Docker最新超详细版教程通俗易懂 - 图11

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

💫1 Docker最新超详细版教程通俗易懂 - 图12

3 Docker的常用命令

3.1 帮助命令

  1. docker version #显示Docker的版本信息
  2. docker info #显示Docker的系统信息,包括镜像和容器的数量
  3. docker [ command ] --help #万能命令

帮助文档的地址:https://docs.docker.com/engine/reference/commandline/docker/

3.2 镜像命令

3.2.1 docker images

查看所有本地的本机上的镜像

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. hello-world latest feb5d9fea6a5 11 months ago 13.3kB
  4. #解释
  5. REPOSITORY 镜像的仓库源
  6. TAG 镜像的表情
  7. IMAGE ID 镜像的ID
  8. CREATED 镜像的创建时间
  9. SIZE 镜像的大小
名称,速记 默认 描述
—all,-a 显示所有图像(默认隐藏中间图像)
—digests 显示摘要
—filter,-f 基于所提供条件的滤波器输出
—format 使用 Go 模板打印漂亮的图像
—no-trunc 不截断输出
—quiet,-q 仅显示图像 ID

3.2.2 docker search

搜索镜像

  1. $ docker search mysql
  2. NAME DESCRIPTION STARS OFFICIAL AUTOMATED
  3. mysql MySQL is a widely used, open-source relation 13053 [OK]
  4. mariadb MariaDB Server is a high performing open sou 4995 [OK]
  5. #可选项,通过收藏进行过滤
  6. --filte=STARS=3000 #搜索出来的镜像就是 STARS 大于3000的镜像
  7. $ docker search --filter=STARS=3000 mysql
  8. NAME DESCRIPTION STARS OFFICIAL AUTOMATED
  9. mysql MySQL is a widely used, open-source relation 13053 [OK]
  10. mariadb MariaDB Server is a high performing open sou 4995 [OK]

3.2.3 docker pull

下载镜像

  1. #下载镜像 docker pull 镜像名[:tag]
  2. $ docker pull mysql
  3. Using default tag: latest #如果不写Tag,默认就是 latest
  4. latest: Pulling from library/mysql
  5. 72a69066d2fe: Pull complete #分层下载,docker image的核心,联合文件系统
  6. 93619dbc5b36: Pull complete
  7. 99da31dd6142: Pull complete
  8. 626033c43d70: Pull complete
  9. 37d5d7efb64e: Pull complete
  10. ac563158d721: Pull complete
  11. d2ba16033dad: Pull complete
  12. 688ba7d5c01a: Pull complete
  13. 00e060b6d11d: Pull complete
  14. 1c04857f594f: Pull complete
  15. 4d7cfa90e6ea: Pull complete
  16. e0431212d27d: Pull complete
  17. Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709 #签名
  18. Status: Downloaded newer image for mysql:latest
  19. docker.io/library/mysql:latest #真实的地址
  20. $ docker pull mysql
  21. #等价于
  22. $ docker pull docker.io/library/mysql:latest
  23. #指定镜像版本下载
  24. $ docker pull mysql:5.7
  25. 5.7: Pulling from library/mysql
  26. 72a69066d2fe: Already exists
  27. 93619dbc5b36: Already exists
  28. 99da31dd6142: Already exists
  29. 626033c43d70: Already exists
  30. 37d5d7efb64e: Already exists
  31. ac563158d721: Already exists
  32. d2ba16033dad: Already exists
  33. 0ceb82207cd7: Pull complete
  34. 37f2405cae96: Pull complete
  35. e2482e017e53: Pull complete
  36. 70deed891d42: Pull complete
  37. Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
  38. Status: Downloaded newer image for mysql:5.7
  39. docker.io/library/mysql:5.7
  40. #查看镜像
  41. $ docker images
  42. REPOSITORY TAG IMAGE ID CREATED SIZE
  43. mysql 5.7 c20987f18b13 8 months ago 448MB
  44. mysql latest 3218b38490ce 8 months ago 516MB
  45. hello-world latest feb5d9fea6a5 11 months ago 13.3kB

3.2.4 docker rmi

删除镜像

  1. #删除指定的镜像
  2. docker rmi <Image_ID>
  3. #删除多个镜像
  4. docker rmi <Image_ID> <Image_ID> <Image_ID> <Image_ID>
  5. #删除全部的镜像
  6. docker rmi -f $(docker images -aq)

3.3 容器命令

说明:我们有了镜像才可以创建容器,Linux,下载一个CentOS 7.9.2009 镜像来测试学习

  1. $ docker pull centos:7.9.2009

新建容器并启动

  1. $ docker run [可选参数] image
  2. #参数说明
  3. --name="Name" 容器名字 tomcat01 tomcat02,用来区分容器
  4. -d 后台运行
  5. -it 使用交互方式运行,进入容器查看内容
  6. -p 指定容器的端口 -p 8080:8080
  7. -p ip:主机端口:容器端口
  8. -p 主机端口:容器端口(常用)
  9. -p 容器端口
  10. 容器端口
  11. -P 随机分配端口
  12. #测试
  13. $ docker run -it --name centos-node01 centos:7.9.2009 /bin/bash
  14. #查看容器内的CentOS,基础环境,很多命令是不完善的
  15. [root@094cce7271cd /]# ls
  16. anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
  17. #从容器中退回主机
  18. [root@094cce7271cd /]# exit
  19. exit

列出所有运行的容器

  1. #docker ps 命令
  2. #默认是列出当前正在运行的容器
  3. -a #列出当前正在运行的容器+带出历史运行过的容器
  4. -n=? #显示最近创建的容器
  5. -q #只显示容器的编号
  6. $ docker ps -a
  7. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  8. 094cce7271cd centos:7.9.2009 "/bin/bash" 2 minutes ago Up 2 minutes centos-node01
  9. 495be2b73cf1 feb5d9fea6a5 "/hello" 24 hours ago Exited (0) 24 hours ago thirsty_faraday

退出容器

  1. exit #直接容器停止并退出
  2. Ctrl+P+Q #容器不停止退出

删除容器

  1. #删除指定的容器,不能删除正在运行的容器,如果要强制删除 docker rm -f
  2. docker rm 容器ID
  3. #删除所有的容器
  4. docker rm -f $(docker ps -aq)
  5. docker ps -aq | xargs docker rm -f

启动和停止容器的操作

  1. #启动容器
  2. docker start 容器ID
  3. #重启容器
  4. docker restart 容器ID
  5. #停止当前正在运行容器
  6. docker stop 容器ID
  7. #强制停止当前容器
  8. docker kill 容器ID

3.4 常用其他命令

后台启动容器

  1. #命令 docker run -d 镜像名!
  2. docker run -d centos:7.9.2009
  3. #问题 docker ps ,发现 centos:7.9.2009 停止了
  4. #常见的坑,docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
  5. #nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了。
  6. #测试
  7. docker run -d -it --name centos-node02 centos:7.9.2009 /bin/bash

查看日志

  1. $ docker logs -f -t --tail [指定条目数] 容器
  2. #显示日志
  3. -tf :显示日志(-f动态显示,-t时间戳显示)
  4. --tail :要显示日志的条目数
  5. #启动容器时指定相应的命令
  6. $ docker run -itd \
  7. --name centos-node03 \
  8. --restart=always \
  9. centos:7.9.2009 /bin/bash -c "while true;do echo hello,world;done;sleep 1"
  10. $ docker ps -l
  11. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  12. 707f6716e19c centos:7.9.2009 "/bin/bash -c 'while…" 38 seconds ago Up 37 seconds centos-node03
  13. $ docker logs -f -t --tail 10 centos-node03

查看容器中进程信息

  1. #命令 docker top 容器ID
  2. $ docker top centos-node03
  3. UID PID PPID C STIME TTY TIME CMD
  4. root 15876 15855 82 22:37 ? 00:03:18 /bin/bash -c while true;do echo hello,world;done;sleep 1

查看容器的元数据

  1. #命令
  2. docker inspect 容器ID
  3. #测试
  4. $ docker inspect centos-node03
  5. [
  6. {
  7. "Id": "707f6716e19ceea875cdd61dc2969b3542aed5523ffa78bb8f65c460050d08ed",
  8. "Created": "2022-08-23T14:37:13.462762788Z",
  9. "Path": "/bin/bash",
  10. "Args": [
  11. "-c",
  12. "while true;do echo hello,world;done;sleep 1"
  13. ],
  14. "State": {
  15. "Status": "running",
  16. "Running": true,
  17. "Paused": false,
  18. "Restarting": false,
  19. "OOMKilled": false,
  20. "Dead": false,
  21. "Pid": 15876,
  22. "ExitCode": 0,
  23. "Error": "",
  24. "StartedAt": "2022-08-23T14:37:13.81511042Z",
  25. "FinishedAt": "0001-01-01T00:00:00Z"
  26. },
  27. "Image": "sha256:eeb6ee3f44bd0b5103bb561b4c16bcb82328cfe5809ab675bb17ab3a16c517c9",
  28. "ResolvConfPath": "/var/lib/docker/containers/707f6716e19ceea875cdd61dc2969b3542aed5523ffa78bb8f65c460050d08ed/resolv.conf",
  29. "HostnamePath": "/var/lib/docker/containers/707f6716e19ceea875cdd61dc2969b3542aed5523ffa78bb8f65c460050d08ed/hostname",
  30. "HostsPath": "/var/lib/docker/containers/707f6716e19ceea875cdd61dc2969b3542aed5523ffa78bb8f65c460050d08ed/hosts",
  31. "LogPath": "/var/lib/docker/containers/707f6716e19ceea875cdd61dc2969b3542aed5523ffa78bb8f65c460050d08ed/707f6716e19ceea875cdd61dc2969b3542aed5523ffa78bb8f65c460050d08ed-json.log",
  32. "Name": "/centos-node03",
  33. "RestartCount": 0,
  34. "Driver": "overlay2",
  35. "Platform": "linux",
  36. "MountLabel": "",
  37. "ProcessLabel": "",
  38. "AppArmorProfile": "",
  39. "ExecIDs": null,
  40. "HostConfig": {
  41. "Binds": null,
  42. "ContainerIDFile": "",
  43. "LogConfig": {
  44. "Type": "json-file",
  45. "Config": {}
  46. },
  47. "NetworkMode": "default",
  48. "PortBindings": {},
  49. "RestartPolicy": {
  50. "Name": "always",
  51. "MaximumRetryCount": 0
  52. },
  53. "AutoRemove": false,
  54. "VolumeDriver": "",
  55. "VolumesFrom": null,
  56. "CapAdd": null,
  57. "CapDrop": null,
  58. "CgroupnsMode": "host",
  59. "Dns": [],
  60. "DnsOptions": [],
  61. "DnsSearch": [],
  62. "ExtraHosts": null,
  63. "GroupAdd": null,
  64. "IpcMode": "private",
  65. "Cgroup": "",
  66. "Links": null,
  67. "OomScoreAdj": 0,
  68. "PidMode": "",
  69. "Privileged": false,
  70. "PublishAllPorts": false,
  71. "ReadonlyRootfs": false,
  72. "SecurityOpt": null,
  73. "UTSMode": "",
  74. "UsernsMode": "",
  75. "ShmSize": 67108864,
  76. "Runtime": "runc",
  77. "ConsoleSize": [
  78. 0,
  79. 0
  80. ],
  81. "Isolation": "",
  82. "CpuShares": 0,
  83. "Memory": 0,
  84. "NanoCpus": 0,
  85. "CgroupParent": "",
  86. "BlkioWeight": 0,
  87. "BlkioWeightDevice": [],
  88. "BlkioDeviceReadBps": null,
  89. "BlkioDeviceWriteBps": null,
  90. "BlkioDeviceReadIOps": null,
  91. "BlkioDeviceWriteIOps": null,
  92. "CpuPeriod": 0,
  93. "CpuQuota": 0,
  94. "CpuRealtimePeriod": 0,
  95. "CpuRealtimeRuntime": 0,
  96. "CpusetCpus": "",
  97. "CpusetMems": "",
  98. "Devices": [],
  99. "DeviceCgroupRules": null,
  100. "DeviceRequests": null,
  101. "KernelMemory": 0,
  102. "KernelMemoryTCP": 0,
  103. "MemoryReservation": 0,
  104. "MemorySwap": 0,
  105. "MemorySwappiness": null,
  106. "OomKillDisable": false,
  107. "PidsLimit": null,
  108. "Ulimits": null,
  109. "CpuCount": 0,
  110. "CpuPercent": 0,
  111. "IOMaximumIOps": 0,
  112. "IOMaximumBandwidth": 0,
  113. "MaskedPaths": [
  114. "/proc/asound",
  115. "/proc/acpi",
  116. "/proc/kcore",
  117. "/proc/keys",
  118. "/proc/latency_stats",
  119. "/proc/timer_list",
  120. "/proc/timer_stats",
  121. "/proc/sched_debug",
  122. "/proc/scsi",
  123. "/sys/firmware"
  124. ],
  125. "ReadonlyPaths": [
  126. "/proc/bus",
  127. "/proc/fs",
  128. "/proc/irq",
  129. "/proc/sys",
  130. "/proc/sysrq-trigger"
  131. ]
  132. },
  133. "GraphDriver": {
  134. "Data": {
  135. "LowerDir": "/var/lib/docker/overlay2/72f30a5ccf50446ae62fd2722e318bea1a2dabc9eef978a37d60a7739350f8d8-init/diff:/var/lib/docker/overlay2/a37fa3541021205d4cc6fe5a51d6bda9a627c860fe9e0d6b23b317fd03e02dd8/diff",
  136. "MergedDir": "/var/lib/docker/overlay2/72f30a5ccf50446ae62fd2722e318bea1a2dabc9eef978a37d60a7739350f8d8/merged",
  137. "UpperDir": "/var/lib/docker/overlay2/72f30a5ccf50446ae62fd2722e318bea1a2dabc9eef978a37d60a7739350f8d8/diff",
  138. "WorkDir": "/var/lib/docker/overlay2/72f30a5ccf50446ae62fd2722e318bea1a2dabc9eef978a37d60a7739350f8d8/work"
  139. },
  140. "Name": "overlay2"
  141. },
  142. "Mounts": [],
  143. "Config": {
  144. "Hostname": "707f6716e19c",
  145. "Domainname": "",
  146. "User": "",
  147. "AttachStdin": false,
  148. "AttachStdout": false,
  149. "AttachStderr": false,
  150. "Tty": true,
  151. "OpenStdin": true,
  152. "StdinOnce": false,
  153. "Env": [
  154. "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
  155. ],
  156. "Cmd": [
  157. "/bin/bash",
  158. "-c",
  159. "while true;do echo hello,world;done;sleep 1"
  160. ],
  161. "Image": "centos:7.9.2009",
  162. "Volumes": null,
  163. "WorkingDir": "",
  164. "Entrypoint": null,
  165. "OnBuild": null,
  166. "Labels": {
  167. "org.label-schema.build-date": "20201113",
  168. "org.label-schema.license": "GPLv2",
  169. "org.label-schema.name": "CentOS Base Image",
  170. "org.label-schema.schema-version": "1.0",
  171. "org.label-schema.vendor": "CentOS",
  172. "org.opencontainers.image.created": "2020-11-13 00:00:00+00:00",
  173. "org.opencontainers.image.licenses": "GPL-2.0-only",
  174. "org.opencontainers.image.title": "CentOS Base Image",
  175. "org.opencontainers.image.vendor": "CentOS"
  176. }
  177. },
  178. "NetworkSettings": {
  179. "Bridge": "",
  180. "SandboxID": "7e286039cc7562f9fe6b6174ef9dae698a6dc049414cfc56d666ca471689a78d",
  181. "HairpinMode": false,
  182. "LinkLocalIPv6Address": "",
  183. "LinkLocalIPv6PrefixLen": 0,
  184. "Ports": {},
  185. "SandboxKey": "/var/run/docker/netns/7e286039cc75",
  186. "SecondaryIPAddresses": null,
  187. "SecondaryIPv6Addresses": null,
  188. "EndpointID": "4b0a08726af85c28a4a6181d335491dd61c0e0b5530248f6be9adfc8ed9f6f41",
  189. "Gateway": "172.17.0.1",
  190. "GlobalIPv6Address": "",
  191. "GlobalIPv6PrefixLen": 0,
  192. "IPAddress": "172.17.0.4",
  193. "IPPrefixLen": 16,
  194. "IPv6Gateway": "",
  195. "MacAddress": "02:42:ac:11:00:04",
  196. "Networks": {
  197. "bridge": {
  198. "IPAMConfig": null,
  199. "Links": null,
  200. "Aliases": null,
  201. "NetworkID": "ab0a6a7d99f1b549817749b69872806ed8989be4f03d330498f0f6becdd2b010",
  202. "EndpointID": "4b0a08726af85c28a4a6181d335491dd61c0e0b5530248f6be9adfc8ed9f6f41",
  203. "Gateway": "172.17.0.1",
  204. "IPAddress": "172.17.0.4",
  205. "IPPrefixLen": 16,
  206. "IPv6Gateway": "",
  207. "GlobalIPv6Address": "",
  208. "GlobalIPv6PrefixLen": 0,
  209. "MacAddress": "02:42:ac:11:00:04",
  210. "DriverOpts": null
  211. }
  212. }
  213. }
  214. }
  215. ]

进入当前正在运行的容器

  1. #通常容器都是使用后台方式运行的,需要进入容器,修改一些配置
  2. #命令
  3. docker attach 容器ID
  4. docker exec -it 容器ID bashShell
  5. #测试
  6. $ docker exec -it centos-node03 /bin/bash
  7. [root@707f6716e19c /]# ps -ef
  8. UID PID PPID C STIME TTY TIME CMD
  9. root 1 0 85 14:37 pts/0 00:09:17 /bin/bash -c while true;do echo hello,world;done;sleep 1
  10. root 7 0 0 14:47 pts/1 00:00:00 /bin/bash
  11. root 21 7 0 14:48 pts/1 00:00:00 ps -ef
  12. [root@707f6716e19c /]# exit
  13. exit
  14. #测试
  15. $ docker attach centos-node03
  16. hello,world
  17. hello,world
  18. hello,world
  19. #正在执行当前的代码
  20. #docker exec #进入容器后开启一个新的终端,可以在里面操作(常用)
  21. #docker attach #进入容器正在执行的终端,不会启动新的进程

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

  1. docker cp 容器ID:容器内路径 宿主机路径
  2. #宿主机创建一个文件
  3. $ echo "hello,docker" > host.txt
  4. #将宿主机的文件传到容器内
  5. $ docker cp host.txt centos-node03:/tmp
  6. $ docker exec -it centos-node03 cat /tmp/host.txt #容器内有该文件
  7. hello,docker
  8. #容器内创建文件
  9. $ docker exec -it centos-node03 /bin/bash
  10. [root@707f6716e19c /]# cd /tmp/
  11. [root@707f6716e19c tmp]# echo "hello,dockerhost" > docker.txt
  12. [root@707f6716e19c tmp]# exit
  13. #容器内的文件传到宿主机中
  14. $ docker cp centos-node03:/tmp/docker.txt /tmp
  15. $ cat /tmp/docker.txt
  16. hello,dockerhost
  17. #拷贝是一个手动过程,未来我们使用 -v 卷的技术,可以实现

3.5 总结

💫1 Docker最新超详细版教程通俗易懂 - 图13

  • attachAttach to a running container —- 当前Shell下attach连接指定运行容器
  • buildBuild an image from a Dockerfile —- 通过Dockerfile定制镜像
  • commitCreate a new image from a container’s changes —- 提交当前容器为新的镜像
  • cpCopy files/folders from the containers filesystem to the host past —- 从容器中拷贝指定文件或者目录到宿主机中
  • createCreate a new container —- 创建一个新的容器,同run,但是不启动容器
  • diffInspect changes on a container’s filesystem —- 查看 docker 容器的变化
  • eventsGet real time events from the server —- 从 docker 服务获取容器实时事件
  • execRun a command in a running container —- 在已存在的容器上运行命令
  • exportStream the contents of a container as a tar archive —- 导出容器的内容流作为一个 tar 归档文件[对应 import]
  • historyShow the history of an image —- 展示一个镜像形成历史
  • imagesList images —- 列出本地系统当前镜像
  • infoDisplay system-wide information —- 显示系统相关信息
  • inspect Return low-level information on Docker objects —- 查看容器详细信息
  • killKill a runnning containers —- kill 指定 docker 容器
  • loadLoad an image from a tar archive —- 从一个 tar 包中加载一个镜像 [ 对应 save ]
  • loginLog in to a Docker registry —- 注册或者登陆一个 docker 源服务器
  • logoutLog out from a Docker registry —- 从当前 Docker Registry 退出
  • logsFetch the logs of a container —- 输出当前容器日志信息
  • portList port mappings or a specific mapping for the container —- 查看映射端口对应的容器内部源端口
  • pausePause all processes within one or more containers —- 暂停容器
  • psList containers —- 列出容器列表
  • pullPull an image or a repository from a registry —- 从docker镜像源服务器拉取指定镜像或者库镜像
  • pushPush an image or a repository to a registry —- 推送指定镜像或者库镜像至docker镜像源服务器
  • restartRestart one or more containers —- 重启运行的容器
  • rmRemove one or more containers —- 移除一个或者多个容器
  • rmiRemove one or more images —- 移除一个或者多个镜像[无容器使用该镜像才可输出,否则需要输出相关容器才能可以继续或者 -f 强制删除]
  • runRun a command in a new container —- 创建一个新的容器并运行一个命令
  • saveSave one or more images to a tar archive —- 保存一个镜像为一个 tar 包[ 对应 load ]
  • searchSearch the Docker Hub for images —- 在 DockerHub 中搜索镜像
  • startStart one or more stopped containers —- 启动容器
  • stopStop one or more running containers —- 停止容器
  • statsDisplay a live stream of container(s) resource usage statistics —- 显示容器资源使用统计的实时流
  • tagTag an image into a repository —- 给源中镜像打标签
  • topDisplay the running processes of a container —- 查看容器中运行的进程信息
  • unpauseUnpause a pause container —- 取消暂停容器
  • versionShow the Docker version information —- 查看 Docker 版本号
  • waitBlock until one or more containers stop, then print their exit codes —- 截取容器停止时的退出状态值

docker 的命令十分多,以上是最常用的容器和镜像的命令。

3.6 作业练习

3.6.1 部署Nginx

  1. #1.搜索nginx镜像 docker search 建议去dockerhub搜索,可以看到帮助文档
  2. #2.下载镜像 docker pull
  3. $ docker pull nginx:1.23.1-alpine
  4. $ docker images nginx:1.23.1-alpine
  5. REPOSITORY TAG IMAGE ID CREATED SIZE
  6. nginx 1.23.1-alpine 804f9cebfdc5 13 days ago 23.5MB
  7. #3.启动容器
  8. #-d 后台运行
  9. #--name 给容器命名
  10. #-p 宿主机端口:容器内部端口
  11. $ docker run -itd --name nginx01 -p 3344:80 --restart=always nginx:1.23.1-alpine
  12. $ docker ps -l
  13. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  14. d0edd598caa1 nginx:1.23.1-alpine "/docker-entrypoint.…" 6 minutes ago Up 6 minutes 0.0.0.0:3344->80/tcp, :::3344->80/tcp nginx01
  15. $ curl localhost:3344
  16. $ docker port nginx01
  17. 80/tcp -> 0.0.0.0:3344
  18. 80/tcp -> :::3344

💫1 Docker最新超详细版教程通俗易懂 - 图14

端口暴露的概念

画板

:::color1 思考问题:我们每次改动Nginx配置,都需要进入容器内部?十分的麻烦,要是可以在容器外部提供一个映射路径,达到在容器修改文件名,容器内部就可以自动修改?

解决方法:-v 数据卷

:::

3.6.2 部署 Tomcat

  1. #官方的使用
  2. $ docker run -it --rm tomcat:9.0
  3. #之前的启动都是后台,停止了容器之后,容器还可以 docker ps 查到
  4. #docker run -it --rm 一般用来测试,用完就删除容器
  5. #下载在启动
  6. $ docker pull tomcat:9.0
  7. $ docker run -it -p 3355:8080 --name tomcat01 -d tomcat:9.0
  8. #测试访问没有问题
  9. #进入容器
  10. $ docker exec -it tomcat01 /bin/bash
  11. root@2cf8573ed4e2:/usr/local/tomcat# cp -av webapps.dist/* webapps/
  12. root@2cf8573ed4e2:/usr/local/tomcat# exit
  13. exit
  14. #发现问题:
  15. #1.Linux命令少了
  16. #2.webapps目录下没有web文件。镜像的原因,默认是最小的镜像,所有不必要的都剔除掉。
  17. #保证最小可运行的环境
  18. $ docker ps -l
  19. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  20. 2cf8573ed4e2 tomcat:9.0 "catalina.sh run" 4 minutes ago Up 4 minutes 0.0.0.0:3355->8080/tcp, :::3355->8080/tcp tomcat01
  21. $ docker port tomcat01
  22. 8080/tcp -> 0.0.0.0:3355
  23. 8080/tcp -> :::3355

💫1 Docker最新超详细版教程通俗易懂 - 图16

:::color1 思考问题:我们以后要部署项目,如果每次都要进入容器是不是十分麻烦?要是可以在容器外部提供一个映射路径,webapps,我们在外部放置项目,就自动同步到内部就好了!

:::

3.6.3 部署 ES+Kibana

ES暴露的端口很多

ES十分的消耗内存

ES的数据一般需要放置到安全目录!挂载

  1. $ docker network create somenetwork
  2. #启动 elasticsearch 容器
  3. $ docker run -d --name elasticsearch \
  4. --net somenetwork \
  5. -p 9200:9200 \
  6. -p 9300:9300 \
  7. -e "discovery.type=single-node" elasticsearch:7.17.5
  8. #启动了 elasticsearch容器后,docker stats 查看CPU状态
  9. $ docker stats --no-stream elasticsearch
  10. CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
  11. 90e93d93b0b0 elasticsearch 1.41% 1.226GiB / 3.682GiB 33.28% 2.38kB / 0B 0B / 1.02MB 45
  12. #测试ES是否成功了,增加内存的设置
  13. $ curl localhost:9200
  14. {
  15. "name" : "90e93d93b0b0",
  16. "cluster_name" : "docker-cluster",
  17. "cluster_uuid" : "W4bf0m9_TU2e5TxRgifsqg",
  18. "version" : {
  19. "number" : "7.6.2",
  20. "build_flavor" : "default",
  21. "build_type" : "docker",
  22. "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
  23. "build_date" : "2020-03-26T06:34:37.794943Z",
  24. "build_snapshot" : false,
  25. "lucene_version" : "8.4.0",
  26. "minimum_wire_compatibility_version" : "6.8.0",
  27. "minimum_index_compatibility_version" : "6.0.0-beta1"
  28. },
  29. "tagline" : "You Know, for Search"
  30. }
  31. #设置容器的内存限制,修改配置文件-e环境配置修改
  32. $ docker run -d --name elasticsearch \
  33. --net somenetwork \
  34. -p 9200:9200 \
  35. -p 9300:9300 \
  36. -e "discovery.type=single-node" \
  37. -e ES_JAVA_OPTS="-Xms256m -Xmx512m" elasticsearch:7.17.5
  38. #再次查看 docker stats 容器资源状态
  39. $ docker stats --no-stream elasticsearch
  40. CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
  41. c4479d160c62 elasticsearch 1.52% 364.6MiB / 3.682GiB 9.67% 656B / 0B 0B / 849kB 46
  42. $ curl localhost:9200
  43. {
  44. "name" : "c4479d160c62",
  45. "cluster_name" : "docker-cluster",
  46. "cluster_uuid" : "xj_fi6P2Tc2DldYqSCz2dw",
  47. "version" : {
  48. "number" : "7.6.2",
  49. "build_flavor" : "default",
  50. "build_type" : "docker",
  51. "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
  52. "build_date" : "2020-03-26T06:34:37.794943Z",
  53. "build_snapshot" : false,
  54. "lucene_version" : "8.4.0",
  55. "minimum_wire_compatibility_version" : "6.8.0",
  56. "minimum_index_compatibility_version" : "6.0.0-beta1"
  57. },
  58. "tagline" : "You Know, for Search"
  59. }

画板

  1. #安装Kibana
  2. $ mkdir -p /mydata/elasticsearch/kibana/config/
  3. $ cat > /mydata/elasticsearch/kibana/config/kibana.yml <<EOF
  4. #
  5. # ** THIS IS AN AUTO-GENERATED FILE **
  6. #
  7. # Default Kibana configuration for docker target
  8. server.host: "0.0.0.0"
  9. server.shutdownTimeout: "5s"
  10. elasticsearch.hosts: [ "http://10.0.0.101:9200" ]
  11. monitoring.ui.container.elasticsearch.enabled: true
  12. EOF
  13. $ docker run -d \
  14. --name kibana \
  15. --net somenetwork \
  16. -v /mydata/elasticsearch/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml \
  17. -p 5601:5601 kibana:7.17.5
  18. $ docker ps -l
  19. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  20. aa2b70cdab6f kibana:7.17.5 "/bin/tini -- /usr/l…" 2 minutes ago Up 2 minutes 0.0.0.0:5601->5601/tcp, :::5601->5601/tcp kibana

💫1 Docker最新超详细版教程通俗易懂 - 图18

3.7 可视化面板

Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便的管理Docker环境,包括单机环境和集群环境(毫不犹豫直接使用 K8S)。用于监控和统计

Portainer 官方站点

官网:https://www.portainer.io/ 安装文档:Install Portainer with Docker on Linux - Portainer Documentation + Rancher (CI/CD使用) + Portainer 先执行使用 Docker图形化界面管理工具!提供一个后台面板供我们操作! bash $ docker volume create portainer_data $ docker run -d -p 8000:8000 -p 9443:9443 --name portainer \ --restart=always \ -v /var/run/docker.sock:/var/run/docker.sock \ -v portainer_data:/data \ --privileged=true \ portainer/portainer-ce:2.9.3 #访问测试: #外网:http://IP地址:9443 $ curl -k https://localhost:9443 可以查看相关的Docker面板情况 💫1 Docker最新超详细版教程通俗易懂 - 图19 # 4 Docker 镜像 ## 4.1 Docker 镜像是什么 + Docker 镜像是什么? 镜像是一种轻量级,可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码,运行时需要的库,环境变量和配置文件等),这个打包好的运行环境就是 image 镜像文件。 只有通过这个镜像文件才能生成 Docker 容器实例(类似Java中 new 出来一个对象) 未来所有的应用,直接打包成 Docker 镜像,就可以直接跑起来。 如何得到镜像? 1. 从远程仓库下载 2. 朋友同学拷贝得到 3. 自己制作镜像Dockerfile + 分层的镜像 在使用 pull 为例,在下载的过程中可以看到 docker 的镜像好像是在一层一层的在下载。 bash $ docker pull tomcat 💫1 Docker最新超详细版教程通俗易懂 - 图20 ## 4.2 Docker 镜像加载原理 + UnionFS(联合文件系统) UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层一层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(Unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。<font style="color:#E8323C;">镜像可以通过分层来进行继承</font>,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。 特性:一次同时加载多个文件系统,但是从外面看起来,只能看到一个文件系统,<font style="color:#E8323C;">联合加载会把各种文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录</font>。 联合文件系统:分层,轻量,并且能够聚合的一种镜像基础。 + Docker 镜像加载原理 Docker 镜像加载原理: Docker 的镜像实际上是由一层一层的文件系统组成的,这种层级的文件系统UnionFS。 bootfs(boot file system)主要包含 bootloader(根加载) 和 kernel(Linux内核),bootloader 主要是引导加载 kernel,Linux 刚启动时会加载 bootfs文件系统(主要引导Linux内核),<font style="color:#E8323C;">在Docker 镜像的最底层时引导文件系统 bootfs</font>。这一层与我们典型的Linux/Unix系统是一样的,包含 boot加载器和内核。当 boot 加载完成之后整个内核都在内存中,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs。 rootfs(root file system),在 bootfs 之上,包含的就是典型 Linux 系统中的 /dev,/proc,/bin,/etc等标准目录和文件。rootfs 就是各种不同的操作系统发行版,比如 Ubuntu,CentOS 等等。 :::warning 总结: 1. bootfs:系统启动的时候需要 bootloader 引导器加载内核 kernel,bootfs 就会卸载,将内存试用权给kernel。 2. rootfs:Linux系统的标准目录和文件,类似于一个小型阉割版的虚拟机环境 3. 系统由三个部分组成,bootfs + rootfs + 应用组成,其中 bootfs 非常大,但是在 Docker 中是公用的,所有镜像中不包含 bootfs ,镜像主要由 rootfs + 应用组成,所以比较小。 ::: 💫1 Docker最新超详细版教程通俗易懂 - 图21 💫1 Docker最新超详细版教程通俗易懂 - 图22 :::color1 平时我们安装虚拟机的 CentOS 都是好几个G,为什么 Docker 这里才 200M? 答案:对于一个精简的OS,rootfs 可以很小,只需要包括最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就行了。由此可见对于不同的 Linux 发行版,bootfs 基本上是一致的,rootfs 会有差别,因此不同的发行版可以共用 bootfs。 也是 虚拟机是分钟级的,容器是秒级的原因。容器只是运行命令和运行应用的环境,底层调用的是宿主机的内核。 ::: properties ~ docker images centos:centos7.9.2009 REPOSITORY TAG IMAGE ID CREATED SIZE centos centos7.9.2009 eeb6ee3f44bd 11 months ago 204MB ## 4.3 分层理解 > 底层的镜像 > 我们可以去下载一个镜像,注岚观察下或的日志输出,可以看到是一层一层的在下载! 💫1 Docker最新超详细版教程通俗易懂 - 图23 + 为什么 Docker 镜像要采用着这种分层结构呢? Docker 镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。 比如说有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。 查看镜像分层的方式可以通过 docker image inspect 命令 bash ~ docker image inspect redis ...省略部分输出... "RootFS": { "Type": "layers", "Layers": [ "sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f", "sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb", "sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1", "sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372", "sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed", "sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952" ] }, "Metadata": { "LastTagTime": "0001-01-01T00:00:00Z" } } ] 理解: 所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。 举一个简单的例子,假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层;如果在该境像中添加 Python包,就会需要在基础镜像层之上增加和修改和新的内容并提交 commit 创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。 该镜像当前已经包含3个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。 💫1 Docker最新超详细版教程通俗易懂 - 图24 在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下因中举了一个简单的例子,每个镜像.层包含3个文件,而境像包含了来白两个境像层的6个文件。 💫1 Docker最新超详细版教程通俗易懂 - 图25 上图中的镜猕层银之前图中的路有区别,主要目的是使于展示文件。 下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本。 💫1 Docker最新超详细版教程通俗易懂 - 图26 这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件,这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。Docker通过存储引擎(新版本采用快照机制》的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统. Linux上可用的存储引擎有AUF5、Overlay2、Device Mapper、Btrfs 以及.zZFS。顾名思义,每种存储引擎都基于Linux 中对应的.文件系统或者块设备技术,并且每种存储引挛都有其独有的性能特点。 Docker在Windows上仪支持 windowstilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW[1]。 下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。 💫1 Docker最新超详细版教程通俗易懂 - 图27 :::warning 特点: Docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部! 这一层就是我们通常说的容器层,容器之下的都叫镜像层! ::: 重点理解 Docker 镜像层都是只读的,容器层是可写的。 当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称为“容器层”,“容器层”之下的都叫镜像层。 所有对容器的改动,无论添加、删除、还是修改文件都只会发生在容器中。只有容器层时可写的,容器层下面的所有镜像层都是只读的。 💫1 Docker最新超详细版教程通俗易懂 - 图28 ## 4.4 如何提交一个自己的镜像 ### 4.4.1 commit 镜像 bash docker commit 提交容器成为一个新的版本 #命令和Git的原理类似 docker commit -m "提交的描述信息" -a "作者" 容器ID 目标镜像名:[TAG] 范例:
  1. #启动一个默认的Tomcat
  2. $ docker run -it -d -p 8080:8080 --name tomcat01 tomcat
  3. #发现这个默认的Tomcat新版本是没有webapps应用,是由于镜像的原因:官方的新镜像默认Tomcat下的webapps目录下是没有文件的
  4. $ docker exec -it tomcat01 /bin/bash
  5. #将webapps.dist目录下的文件拷贝到webapps中
  6. root@82b9be0838cd:/usr/local/tomcat# cp -av webapps.dist/ webapps
  7. root@82b9be0838cd:/usr/local/tomcat# cd webapps
  8. root@82b9be0838cd:/usr/local/tomcat/webapps# ls
  9. ROOT docs examples host-manager manager
  10. root@82b9be0838cd:/usr/local/tomcat/webapps# exit
  11. exit
  12. $ curl localhost:8080
  13. #将操作过的容器通过 commit 提交为一个镜像
  14. $ docker commit \
  15. -a "zhongzhiwei <zhongzhiwei@kubesphere.io>" \
  16. -m "拷贝webapps.dist目录下的内容到webapps" \
  17. tomcat01 kube-tomcat:1.0
  18. #查看提交的镜像
  19. $ docker images kube-tomcat:1.0
  20. REPOSITORY TAG IMAGE ID CREATED SIZE
  21. kube-tomcat 1.0 0cf24bc18be1 4 seconds ago 684MB
  22. $ docker history kube-tomcat:1.0
  23. IMAGE CREATED CREATED BY SIZE COMMENT
  24. 0cf24bc18be1 3 minutes ago catalina.sh run 4.43MB 拷贝webapps.dist目录下的内容到webapps
  25. fb5657adc892 8 months ago /bin/sh -c #(nop) CMD ["catalina.sh" "run"] 0B
  26. <missing> 8 months ago /bin/sh -c #(nop) EXPOSE 8080 0B
  27. <missing> 8 months ago /bin/sh -c set -eux; nativeLines="$(catalin… 0B
  28. <missing> 8 months ago /bin/sh -c set -eux; savedAptMark="$(apt-m 20.2MB
  29. <missing> 8 months ago /bin/sh -c #(nop) ENV TOMCAT_SHA512=c2d2ad5… 0B
  30. <missing> 8 months ago /bin/sh -c #(nop) ENV TOMCAT_VERSION=10.0.14 0B
  31. <missing> 8 months ago /bin/sh -c #(nop) ENV TOMCAT_MAJOR=10 0B
  32. <missing> 8 months ago /bin/sh -c #(nop) ENV GPG_KEYS=A9C5DF4D22E9… 0B
  33. <missing> 8 months ago /bin/sh -c #(nop) ENV LD_LIBRARY_PATH=/usr/… 0B
  34. <missing> 8 months ago /bin/sh -c #(nop) ENV TOMCAT_NATIVE_LIBDIR=… 0B
  35. <missing> 8 months ago /bin/sh -c #(nop) WORKDIR /usr/local/tomcat 0B
  36. <missing> 8 months ago /bin/sh -c mkdir -p "$CATALINA_HOME" 0B
  37. <missing> 8 months ago /bin/sh -c #(nop) ENV PATH=/usr/local/tomca… 0B
  38. <missing> 8 months ago /bin/sh -c #(nop) ENV CATALINA_HOME=/usr/lo… 0B
  39. <missing> 8 months ago /bin/sh -c #(nop) CMD ["jshell"] 0B
  40. <missing> 8 months ago /bin/sh -c set -eux; arch="$(dpkg --print-… 343MB
  41. <missing> 8 months ago /bin/sh -c #(nop) ENV JAVA_VERSION=11.0.13 0B
  42. <missing> 8 months ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B
  43. <missing> 8 months ago /bin/sh -c #(nop) ENV PATH=/usr/local/openj… 0B
  44. <missing> 8 months ago /bin/sh -c { echo '#/bin/sh'; echo 'echo "$J' 27B
  45. <missing> 8 months ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/local/… 0B
  46. <missing> 8 months ago /bin/sh -c set -eux; apt-get update; apt-g… 11.3MB
  47. <missing> 8 months ago /bin/sh -c apt-get update && apt-get install… 152MB
  48. <missing> 8 months ago /bin/sh -c set -ex; if ! command -v gpg > /… 18.9MB
  49. <missing> 8 months ago /bin/sh -c set -eux; apt-get update; apt-g… 10.7MB
  50. <missing> 8 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
  51. <missing> 8 months ago /bin/sh -c #(nop) ADD file:c03517c5ddbed4053… 124MB
  52. #使用我们修改提交的新镜像,那么就会发现在 webapps 目录下有相应的文件
  53. $ docker run -it -p 8085:8080 --name mytomcat01 -d kube-tomcat:1.0
  54. $ docker exec -it mytomcat01 /bin/bash
  55. root@6b8f51a773ee:/usr/local/tomcat# ls webapps
  56. ROOT docs examples host-manager manager
  57. root@6b8f51a773ee:/usr/local/tomcat# exit
  58. exit
  59. $ curl localhost:8085

:::warning 学习方式说明:理解概念,可以比较模糊,但是一定要实践,最后实践和理论相结合。

如果想保存当前容器的状态,就可以通过 commit 方式来提交镜像,获取一个镜像。类似于虚拟机的快照功能。

:::

5 容器数据卷

5.1 什么是容器数据卷

docker 的理念回顾

将应用和运行的环境打包形成容器运行,运行可以伴随着容器,但是我们对于数据的要求,是希望能够持久化的!就好比,你安装一个MySQL,结果你把容器删了,就相当于删库跑路了,这TM也太扯了吧!

所以我们希望容器之间有可能可以共享数据,Docker容器产生的数据,如果不通过docker commit生成新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了!这样是行不通的!

为了能保存数据在Docker中我们就可以使用卷!让数据挂载到我们本地!这样数据就不会因为容器删除而丢失了!

数据?如果数据都在容器中,那么容器删除,数据就会丢失!需求:数据可以持久化

MySQL,容器删除了,数据则删除,删库跑路。需求:MySQL的数据存放到本地!

容器之间可以有一个数据共享的技术!Docker 容器中产生的数据,同步到本地!

这就是数据卷技术!目录的挂载,将我们容器内的目录,挂载到Linux系统指定的目录下。

作用:

卷就是目录或者文件,存在一个或者多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System,提供一些用于持续存储或共享数据的特性:

卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。

特点:

1、数据卷可在容器之间共享或重用数据

2、卷中的更改可以直接生效

3、数据卷中的更改不会包含在镜像的更新中

4、数据卷的生命周期一直持续到没有容器使用它为止

画板

总结:容器的数据持久化和同步操作!容器间也是可以数据共享的!

5.2 使用数据卷

方式一:直接使用命令挂载 -v 参数

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

:::

范例:

  1. $ docker run -it -d \
  2. -v /data/ubuntu01:/home \
  3. --privileged=true \
  4. --name ubuntu01 ubuntu:20.04 /bin/bash
  5. #查看容器的详细信息显示
  6. #通过docker inpsect 容器ID 查看
  7. #值得注意的是,主机目录会直接覆盖掉容器中要挂载的目录
  8. $ docker inspect ubuntu01 | grep -A 9 "Mounts"
  9. "Mounts": [
  10. {
  11. "Type": "bind",
  12. "Source": "/data/ubuntu01", #主机内的目录路径
  13. "Destination": "/home", #Docker容器内的目录路径
  14. "Mode": "",
  15. "RW": true,
  16. "Propagation": "rprivate"
  17. }
  18. ],
  19. $ docker exec -it ubuntu01 /bin/bash
  20. root@9afa7b8ee434:/# cd /home/
  21. root@9afa7b8ee434:/home# mkdir host ; ls
  22. host
  23. root@9afa7b8ee434:/home# echo "Hello World!" > /home/docker.txt
  24. root@9afa7b8ee434:/home# exit
  25. $ ls /data/ubuntu01/
  26. docker.txt host
  27. #再次测试
  28. #删除原有的ubuntu01容器,启动ubuntu02容器进行挂载该目录
  29. $ docker rm -f ubuntu01
  30. $ docker run -it -d \
  31. --name ubuntu02 \
  32. --privileged=true \
  33. -v /data/ubuntu01:/home \
  34. ubuntu:20.04 /bin/bash
  35. $ docker exec -it ubuntu02 /bin/bash
  36. root@2ac37ab7725a:/# ls /home/
  37. docker.txt host
  38. root@2ac37ab7725a:/# cat /home/docker.txt
  39. Hello World!

:::warning 数据卷好处:我们以后修改只需要修改本地修改即可,容器内会自动同步。类似共享文件是占用一份的存储。

:::

5.3 实战:安装MySQL

思考:MySQL 的数据持久化的问题。

  1. #获取并下载MySQL镜像
  2. $ docker pull mysql:5.7.36
  3. #官方测试 $ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql-root -d mysql:tag
  4. #运行mysql容器,需要做数据挂载
  5. #安装启动mysql,需要配置密码,注意事项
  6. #-d 后台运行
  7. #-p 端口映射
  8. #-v 数据卷挂载
  9. #-e 环境变量设置
  10. #--name 容器名字
  11. $ docker run -it -d \
  12. --name mysql01 \
  13. -p 3316:3306 \
  14. -e MYSQL_ROOT_PASSWORD=123456 \
  15. -v /data/docker/mysql01/conf:/etc/mysql/conf.d \
  16. -v /data/docker/mysql01/data:/var/lib/mysql \
  17. mysql:5.7.36
  18. #如果在挂载时,外部是新建目录或空目录会将容器中映射目录也变成空目录
  19. $ cat > /data/docker/mysql01/conf/my.cnf <<EOF
  20. [client]
  21. default-character-set=utf8mb4
  22. [mysql]
  23. default-character-set=utf8mb4
  24. [mysqld]
  25. character-set-server=utf8mb4
  26. collation-server=utf8mb4_unicode_ci
  27. EOF
  28. $ ls /data/docker/mysql01/conf/
  29. my.cnf
  30. $ ls /data/docker/mysql01/data/
  31. auto.cnf client-cert.pem ib_buffer_pool ib_logfile1 performance_schema server-cert.pem
  32. ca-key.pem client-key.pem ibdata1 ibtmp1 private_key.pem server-key.pem
  33. ca.pem ib_logfile0 mysql public_key.pem sys
  34. #默认 -v 是先主机挂载覆盖了,然后 MySQL 程序启动才自动创建的数据,会直接生成/var/lib/mysql/目录下的数据库数据
  35. $ docker restart mysql01
  36. mysql01
  37. #启动成功之后,在本地使用Navicat等数据库连接工具进行连接即可。
  38. #Navicat 连接到服务器 3316 --> 3316 端口是和容器的内部的 3306 映射,这个时候就可以连接上了
  39. #在本地创建一个测试数据库dbtest,可以在挂载目录下查看到相应的目录
  40. $ ls -l /data/docker/mysql01/data/dbtest/
  41. total 4
  42. -rw-r----- 1 polkitd input 67 Aug 25 17:10 db.opt

💫1 Docker最新超详细版教程通俗易懂 - 图30

:::color1 假设我们将容器删除了。

发现,我们挂载到本地的数据卷依旧没有丢失,这就实现了容器的数据持久化的功能!

:::

5.4 具名挂载和匿名挂载

  1. #匿名挂载
  2. -v 容器内路径
  3. $ docker run -it -d -P --name nginx01 -v /etc/nginx nginx
  4. #查看所有 volume 的情况
  5. $ docker volume ls
  6. DRIVER VOLUME NAME
  7. local b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9
  8. #这里发现,这种就是匿名挂载,我们在 -v 只写了容器内的路径,没有写容器外的路径
  9. $ docker inspect nginx01 | grep -A 9 "Mounts"
  10. "Mounts": [
  11. {
  12. "Type": "volume",
  13. "Name": "b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9",
  14. "Source": "/var/lib/docker/volumes/b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9/_data",
  15. "Destination": "/etc/nginx",
  16. "Driver": "local",
  17. "Mode": "",
  18. "RW": true,
  19. "Propagation": ""
  20. $ cd /var/lib/docker/volumes/b5c1c38d5935b1e1adc829c3eb28af15188368169745afe75f230858969b6df9/_data
  21. $ ls
  22. conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
  23. #具名挂载
  24. $ docker run -it -d -P --name nginx02 -v nginx-volume:/etc/nginx nginx
  25. 62c42ad0e4ca2374ee30af5b18b48a40e197d41126225446716c241639c3dbf3
  26. #通过 -v 卷名:容器内路径
  27. #查看所有 volume 的情况
  28. $ docker volume ls
  29. DRIVER VOLUME NAME
  30. local nginx-volume
  31. #查看一下这个具名卷
  32. $ docker volume inspect nginx-volume
  33. [
  34. {
  35. "CreatedAt": "2022-08-25T20:41:56+08:00",
  36. "Driver": "local",
  37. "Labels": null,
  38. "Mountpoint": "/var/lib/docker/volumes/nginx-volume/_data",
  39. "Name": "nginx-volume",
  40. "Options": null,
  41. "Scope": "local"
  42. }
  43. ]

💫1 Docker最新超详细版教程通俗易懂 - 图31

:::color1 所有的 Docker 容器内的卷,没有指定目录的情况下都是在 “/var/lib/docker/volumes/xxx/_data

我们通过具名挂载可以方便的让我们找到对应的数据卷名,大多数情况是使用”具名挂载”

:::

思考:如何请是具名挂载还是匿名挂载,还是指定路径挂载!

  • -v 容器内路径 #匿名挂载
  • -v 卷名:容器内路径 #具名挂载
  • -v /宿主机路径:容器内路径 #指定路径挂载

拓展:

  1. #通过 -v 容器内路径:ro rw 改变读写权限
  2. ro readonly #只读
  3. rw readwrite #可读可写
  4. #一旦这个设置了容器,容器对我们挂载出来的内容就有限定了!
  5. $ docker run -it -d -P --name nginx03 -v nginx-volume:/etc/nginx:ro nginx
  6. $ docker run -it -d -P --name nginx03 -v nginx-volume:/etc/nginx:rw nginx
  7. #默认是rw,如果是 ro 就说明只能通过宿主机来操作,容器内部是无法操作的

5.5 初识 Dockerfile

Dockerfile就是用来构建docker镜像的构建文件!命令脚本!先体验一下!

通过这个脚本可以生成镜像,镜像是一层一层的,对应的就是脚本一个个的。每一个命令都是一层!

  1. $ mkdir -pv /root/docker/docker-volume ; cd /root/docker/docker-volume
  2. #创建一个Dockerfile文件,名字可以随机 建议使用Dockerfile
  3. #文件中的内容指令(大写) 参数
  4. $ vim Dockerfile
  5. FROM ubuntu:20.04
  6. #匿名挂载
  7. VOLUME ["/volume01", "/volume02"]
  8. RUN echo "-----> Success ......"
  9. CMD /bin/bash
  10. #这里的每一个命令,就是镜像的一层
  11. $ docker build -t kube-ubuntu:1.0 -f Dockerfile .
  12. Sending build context to Docker daemon 2.048kB
  13. Step 1/4 : FROM ubuntu:20.04
  14. ---> ba6acccedd29
  15. Step 2/4 : VOLUME ["/volume01", "/volume02"]
  16. ---> Running in f77e4a3d3e7f
  17. Removing intermediate container f77e4a3d3e7f
  18. ---> 91315a9d10d0
  19. Step 3/4 : RUN echo "-----> Success ......"
  20. ---> Running in fa440b3de704
  21. -----> Success ......
  22. Removing intermediate container fa440b3de704
  23. ---> ffe4ed184001
  24. Step 4/4 : CMD /bin/bash
  25. ---> Running in f27e036a6862
  26. Removing intermediate container f27e036a6862
  27. ---> 1f2a4e5e429d
  28. Successfully built 1f2a4e5e429d
  29. Successfully tagged kube-ubuntu:1.0
  30. $ docker images kube-ubuntu:1.0
  31. REPOSITORY TAG IMAGE ID CREATED SIZE
  32. kube-ubuntu 1.0 1f2a4e5e429d About a minute ago 72.8MB
  33. $ docker run -itd --name ku1 kube-ubuntu:1.0 /bin/bash
  34. $ docker exec -it ku1 /bin/bash
  35. #这个目录就是我们生成镜像的时候自动挂载的,数据卷目录
  36. root@6a6138c411dd:/# echo "Hello volume01" > /volume01/container01.txt
  37. root@6a6138c411dd:/# echo "Hello volume02" > /volume02/container02.txt
  38. root@6a6138c411dd:/# ls -l /volume0*
  39. /volume01:
  40. total 4
  41. -rw-r--r-- 1 root root 15 Aug 25 13:08 container01.txt
  42. /volume02:
  43. total 4
  44. -rw-r--r-- 1 root root 15 Aug 25 13:08 container02.txt
  45. root@6a6138c411dd:/# exit
  46. exit
  47. #这个数据卷和外部一定有一个同步的目录
  48. $ docker inspect ku1 | grep -A 21 "Mounts"
  49. "Mounts": [
  50. {
  51. "Type": "volume",
  52. "Name": "604c9c1d68d41958287107effbd1dcfbd0bbd875c9cb7dd8b35982f446e35f68",
  53. "Source": "/var/lib/docker/volumes/604c9c1d68d41958287107effbd1dcfbd0bbd875c9cb7dd8b35982f446e35f68/_data",
  54. "Destination": "/volume01",
  55. "Driver": "local",
  56. "Mode": "",
  57. "RW": true,
  58. "Propagation": ""
  59. },
  60. {
  61. "Type": "volume",
  62. "Name": "f6262a1ec8581c5b6a6b78e3beacd4881b435a5d331f5c33d88e5c9e0b8bfb89",
  63. "Source": "/var/lib/docker/volumes/f6262a1ec8581c5b6a6b78e3beacd4881b435a5d331f5c33d88e5c9e0b8bfb89/_data",
  64. "Destination": "/volume02",
  65. "Driver": "local",
  66. "Mode": "",
  67. "RW": true,
  68. "Propagation": ""
  69. }
  70. ],
  71. #测试一下,刚才的文件是否同步了!
  72. #这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像!
  73. $ cat /var/lib/docker/volumes/604c9c1d68d41958287107effbd1dcfbd0bbd875c9cb7dd8b35982f446e35f68/_data/container01.txt
  74. Hello volume01
  75. $ cat /var/lib/docker/volumes/f6262a1ec8581c5b6a6b78e3beacd4881b435a5d331f5c33d88e5c9e0b8bfb89/_data/container02.txt
  76. Hello volume02

💫1 Docker最新超详细版教程通俗易懂 - 图32

假设构建镜像时候没有挂载卷,要手动镜像挂载 -v 卷名:容器内路径!

5.6 数据卷容器

画板

  1. #启动3个容器,通过我们刚才自己写的镜像启动
  2. $ docker images kube-ubuntu:1.0
  3. REPOSITORY TAG IMAGE ID CREATED SIZE
  4. kube-ubuntu 1.0 317c07f88b30 30 minutes ago 72.8MB
  5. #写/bin/bash是规定镜像启动时执行这条指令,centos镜像启动时默认会执行/bin/bash,因此可以不写
  6. $ docker run -itd --name docker01 kube-ubuntu:1.0 /bin/bash
  7. $ docker run -itd --name docker02 --volumes-from docker01 kube-ubuntu:1.0 /bin/bash
  8. #--volumes-from 实际上多个容器挂载的是同一个数据卷
  9. #--volumes-from 就可以实现容器间的数据共享了
  10. #在docker01容器中创建测试文件,检验docker02是否也有其测试文件
  11. #docker01创建测试文件
  12. $ docker exec -it docker01 /bin/bash
  13. root@51ae030c5f2a:/# echo "Hello docker02 volume01" > /volume01/test01.txt
  14. root@51ae030c5f2a:/# echo "Hello docker02 volume02" > /volume02/test02.txt
  15. #docker02查看测试文件
  16. $ docker exec -it docker02 /bin/bash
  17. root@9807aeded26e:/# cat /volume01/test01.txt
  18. Hello docker02 volume01
  19. root@9807aeded26e:/# cat /volume02/test02.txt
  20. Hello docker02 volume02
  21. #docker03继承docker02的数据卷信息,并创建测试文件
  22. $ docker run -it --name docker03 --volumes-from docker02 kube-ubuntu:1.0 /bin/bash
  23. root@bdc47c45e893:/# echo "Hello I am docker03" > /volume01/docker03.txt
  24. root@bdc47c45e893:/# echo "Hello I am docker03 Here" > /volume02/docker03.txt
  25. #docker01可以查看到docker03的测试文件
  26. $ docker exec -it docker01 /bin/bash
  27. root@51ae030c5f2a:/# cat /volume01/docker03.txt
  28. Hello I am docker03
  29. root@51ae030c5f2a:/# cat /volume02/docker03.txt
  30. Hello I am docker03 Here

💫1 Docker最新超详细版教程通俗易懂 - 图34

  1. #测试:可以删除 docker01,查看一下docker02和docker03是否还可以访问这个文件
  2. #测试依旧可以访问
  3. $ docker rm -f docker01
  4. #三个容器都是挂载到宿主机的同一个目录下的,如果删掉宿主机下的这个目录的文件,三个容器的数据都会消失
  5. #可以发现三个容器的匿名数据卷挂载的路径是一样的。

💫1 Docker最新超详细版教程通俗易懂 - 图35

💫1 Docker最新超详细版教程通俗易懂 - 图36

已验证,多个 MySQL 实现数据共享失败。原因后续继承该MySQL 配置和数据的方式依旧是父MySQL,会导致后续的MySQL 启动失败。数据卷容器 只能使用在 无状态服务。注意两个mysql虽然共用一个数据,但是只能同时在线一个MySQL容器,否则另外一个连接不上。

:::color1 结论:

容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。

但是一旦数据持久化到本地,这个时候,本地的数据是不会删除的!

数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除数据卷。如果需要在删除容器的同时移除数据卷,可以在删除容器的时候使用 docker rm -v 这个参数。

:::

6 Dockerfile

6.1 Dockerfile 介绍

Dockerfile 是用来构建 Docker 镜像的文件!命令参数脚本!

构建步骤:

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

查看官方 CentOS Dockerfile 镜像文件

  1. FROM scratch
  2. ADD centos-7-x86_64-docker.tar.xz /
  3. LABEL \
  4. org.label-schema.schema-version="1.0" \
  5. org.label-schema.name="CentOS Base Image" \
  6. org.label-schema.vendor="CentOS" \
  7. org.label-schema.license="GPLv2" \
  8. org.label-schema.build-date="20201113" \
  9. org.opencontainers.image.title="CentOS Base Image" \
  10. org.opencontainers.image.vendor="CentOS" \
  11. org.opencontainers.image.licenses="GPL-2.0-only" \
  12. org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
  13. CMD ["/bin/bash"]

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

6.2 Dockerfile 构建过程

基础知识:

  1. 每个保留关键字(指令)都是必须是大写字母
  2. 执行从上到下顺序执行
  3. 表示注释

  4. 每一个指令都会创建提交一个新的镜像层,并提交!

:::color1 Dockerfile 是面向开发的,以后要发布项目,做镜像,就需要编写 Dockerfile 文件,这个文件十分简单!

:::

Docker 镜像 —> SpringBoot 。镜像逐渐成为企业交付的标准,必须掌握。

步骤:开发,部署,上线运维 …… 缺一不可!

  • Dockerfile:构建文件,定义了一切的步骤,源代码。
  • DockerImage:是通过 Dockerfile 构建生成的镜像,最终发布和运行的产品。
  • Docker Container:容器就是镜像运行起来提供服务。

💫1 Docker最新超详细版教程通俗易懂 - 图37

6.3 Dockerfile 的指令

:::color1 FROM #基础镜镜像,一切从这里开始构建

MAINTAINER #镜像是谁写的,姓名+邮箱

RUN #镜像构建的时候需要运行的命令

ADD #步骤: tomcat镜像,这个tomcat压缩包!添加内容

WORKDIR #镜像的工作目录

VOLUME #挂载的目录

EXPOSE #保留端口配置

CMD #指定这个容器的时候要运行的要求,只有最后一个会生效,可被替代

ENTRYPOINT #指定这个容器的时候要运行的要求,ENTRYPOINT 后的指令会作为参数进行传入

ONBUILD #当构建一个被继承 Dockerfile 这个时候就会运行 ONBUILD 的指令,才会触发指令

COPY # 类似 ADD,将文件拷贝到镜像中

ENV #构建的时候设置环境变量

:::

:::warning

参考 tomcat 8 的 dockerfile 入门 —> https://github.com/docker-library/tomcat DockerHub 使用 docker run 运行的镜像,进行反解析之后就是 Dockerfile ::: dockerfile # https://github.com/docker-library/tomcat/blob/master/10.0/jdk8/corretto-al2/Dockerfile # # NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" # # PLEASE DO NOT EDIT IT DIRECTLY. # FROM amazoncorretto:8-al2-jdk ENV CATALINA_HOME /usr/local/tomcat ENV PATH $CATALINA_HOME/bin:$PATH RUN mkdir -p "$CATALINA_HOME" WORKDIR $CATALINA_HOME # let "Tomcat Native" live somewhere isolated ENV TOMCAT_NATIVE_LIBDIR $CATALINA_HOME/native-jni-lib ENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR # see https://www.apache.org/dist/tomcat/tomcat-10/KEYS # see also "versions.sh" (https://github.com/docker-library/tomcat/blob/master/versions.sh) ENV GPG_KEYS A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 ENV TOMCAT_MAJOR 10 ENV TOMCAT_VERSION 10.0.23 ENV TOMCAT_SHA512 0e0263e8280f2ccfb4bef916444a6105fef689a3d95c334c8a7bfe59f1e3966d48ea624727f1818a4df331a603f1ac5e21b908dda3cae676ddc1aef90c2d12ab RUN set -eux; \ \ # http://yum.baseurl.org/wiki/YumDB.html if ! command -v yumdb > /dev/null; then \ yum install -y --setopt=skip_missing_names_on_install=False yum-utils; \ yumdb set reason dep yum-utils; \ fi; \ # a helper function to "yum install" things, but only if they aren't installed (and to set their "reason" to "dep" so "yum autoremove" can purge them for us) _yum_install_temporary() { ( set -eu +x; \ local pkg todo=''; \ for pkg; do \ if ! rpm --query "$pkg" > /dev/null 2>&1; then \ todo="$todo $pkg"; \ fi; \ done; \ if [ -n "$todo" ]; then \ set -x; \ yum install -y --setopt=skip_missing_names_on_install=False $todo; \ yumdb set reason dep $todo; \ fi; \ ) }; \ _yum_install_temporary gzip tar; \ \ ddist() { \ local f="$1"; shift; \ local distFile="$1"; shift; \ local mvnFile="${1:-}"; \ local success=; \ local distUrl=; \ for distUrl in \ # https://issues.apache.org/jira/browse/INFRA-8753?focusedCommentId=14735394#comment-14735394 "https://www.apache.org/dyn/closer.cgi?action=download&filename=$distFile" \ # if the version is outdated (or we're grabbing the .asc file), we might have to pull from the dist/archive :/ "https://downloads.apache.org/$distFile" \ "https://www-us.apache.org/dist/$distFile" \ "https://www.apache.org/dist/$distFile" \ "https://archive.apache.org/dist/$distFile" \ # if all else fails, let's try Maven (https://www.mail-archive.com/users@tomcat.apache.org/msg134940.html; https://mvnrepository.com/artifact/org.apache.tomcat/tomcat; https://repo1.maven.org/maven2/org/apache/tomcat/tomcat/) ${mvnFile:+"https://repo1.maven.org/maven2/org/apache/tomcat/tomcat/$mvnFile"} \ ; do \ if curl -fL -o "$f" "$distUrl" && [ -s "$f" ]; then \ success=1; \ break; \ fi; \ done; \ [ -n "$success" ]; \ }; \ \ ddist 'tomcat.tar.gz' "tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz" "$TOMCAT_VERSION/tomcat-$TOMCAT_VERSION.tar.gz"; \ echo "$TOMCAT_SHA512 *tomcat.tar.gz" | sha512sum --strict --check -; \ ddist 'tomcat.tar.gz.asc' "tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc" "$TOMCAT_VERSION/tomcat-$TOMCAT_VERSION.tar.gz.asc"; \ export GNUPGHOME="$(mktemp -d)"; \ for key in $GPG_KEYS; do \ gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key"; \ done; \ gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz; \ tar -xf tomcat.tar.gz --strip-components=1; \ rm bin/*.bat; \ rm tomcat.tar.gz*; \ command -v gpgconf && gpgconf --kill all || :; \ rm -rf "$GNUPGHOME"; \ \ # https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html#Default_web_applications mv webapps webapps.dist; \ mkdir webapps; \ # we don't delete them completely because they're frankly a pain to get back for users who do want them, and they're generally tiny (~7MB) \ nativeBuildDir="$(mktemp -d)"; \ tar -xf bin/tomcat-native.tar.gz -C "$nativeBuildDir" --strip-components=1; \ _yum_install_temporary \ apr-devel \ gcc \ make \ openssl11-devel \ ; \ ( \ export CATALINA_HOME="$PWD"; \ cd "$nativeBuildDir/native"; \ aprConfig="$(command -v apr-1-config)"; \ ./configure \ --libdir="$TOMCAT_NATIVE_LIBDIR" \ --prefix="$CATALINA_HOME" \ --with-apr="$aprConfig" \ --with-java-home="$JAVA_HOME" \ --with-ssl \ ; \ nproc="$(nproc)"; \ make -j "$nproc"; \ make install; \ ); \ rm -rf "$nativeBuildDir"; \ rm bin/tomcat-native.tar.gz; \ \ # mark any explicit dependencies as manually installed find "$TOMCAT_NATIVE_LIBDIR" -type f -executable -exec ldd '{}' ';' \ | awk '/=>/ && $(NF-1) != "=>" { print $(NF-1) }' \ | xargs -rt readlink -e \ | sort -u \ | xargs -rt rpm --query --whatprovides \ | sort -u \ | tee "$TOMCAT_NATIVE_LIBDIR/.dependencies.txt" \ | xargs -r yumdb set reason user \ ; \ \ # clean up anything added temporarily and not later marked as necessary yum autoremove -y; \ yum clean all; \ rm -rf /var/cache/yum; \ \ # sh removes env vars it doesn't support (ones with periods) # https://github.com/docker-library/tomcat/issues/77 find ./bin/ -name '*.sh' -exec sed -ri 's|^#!/bin/sh$|#!/usr/bin/env bash|' '{}' +; \ \ # fix permissions (especially for running as non-root) # https://github.com/docker-library/tomcat/issues/35 chmod -R +rX .; \ chmod 777 logs temp work; \ \ # smoke test catalina.sh version # verify Tomcat Native is working properly RUN set -eux; \ nativeLines="$(catalina.sh configtest 2>&1)"; \ nativeLines="$(echo "$nativeLines" | grep 'Apache Tomcat Native')"; \ nativeLines="$(echo "$nativeLines" | sort -u)"; \ if ! echo "$nativeLines" | grep -E 'INFO: Loaded( APR based)? Apache Tomcat Native library' >&2; then \ echo >&2 "$nativeLines"; \ exit 1; \ fi EXPOSE 8080 CMD ["catalina.sh", "run"] ### 6.3.1 FROM 基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是 from

6.3.2 MAINTAINER

镜像维护者的姓名和邮箱地址

6.3.3 RUN

容器构建时需要运行的命令(容器启动前做的一下预操作),在 docker build 命令构建镜像的时候,就会执行RUN的部分

两种格式

  • Shell 格式
  1. RUN <命令行命令>
  2. # <命令行命令> 等同于,在终端操作的 shell 指令
  3. # 例如:
  4. RUN yum install -y vim
  • exec 格式
  1. RUN ["可执行文件", "参数1", "参数2"]
  2. # 例如:
  3. # RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

RUN 是在 docker build 时运行

6.3.4 EXPOSE

EXPOSE [/…]
EXPOSE 定义说明里面的服务端口。 该指令通知 Docker 容器在运行时侦听指定的网络端口。您可以指定端口是在 TCP 还是 UDP 上侦听,如果未指定协议,则默认值为 TCP。EXPOSE。 该指令实际上并不发布端口。它充当构建映像的人员和运行容器的人员之间的一种文档类型,有关要发布哪些端口。若要在运行容器时实际发布端口,请使用 标志 on 发布和映射一个或多个端口,或使用标志发布所有公开的端口并将其映射到高阶端口。EXPOSE-pdocker run-P

6.3.5 WORKDIR

WORKDIR /path/to/workdir
指定在创建容器后,终端默认登录的进来工作目录,一个落脚点

6.3.6 USER

指定该镜像以什么样的用户去执行,如果都不指定,默认是 root

6.3.7 ENV

用来在构建镜像过程中设置环境变量

ENV MY_PATH /usr/mytest

这个环境变量可以在后续的任何 RUN 指令中使用,这就如同在命令前面指定了环境变量前缀一样;

也可以在其他指令中直接使用这些环境变量

比如:WORKDIR $MY_PATH

6.3.8 ADD

ADD [—chown=:] ADD [—chown=:] [““,… ““]
将宿主机目录下的文件拷贝进镜像并且会自动处理 URL 和解压 tar 压缩包。该功能仅在用于构建 Linux 容器的 Dockerfiles 上受支持,在 Windows 容器上不起作用。由于用户和组所有权概念不会在 Linux 和 Windows 之间转换,因此使用 和 用于将用户和组名称转换为 ID 会将此功能限制为仅适用于基于 Linux 操作系统的容器。 如果 是可识别的压缩格式(标识、gzip、bzip2 或 xz)的本地 tar 存档,则将其解压缩为目录。来自远程 URL 的资源不会解压缩。当复制或解压缩目录时,它的行为与 相同,结果是:tar -x

ADD 命令支持将远程URL的资源,但是 Docker 官方不建议直接用远程url,所以还是先下载到主机

是 COPY 的升级版。

6.3.9 COPY

类似 ADD,拷贝文件和目录到镜像中 将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的<目标路径>位置

:::warning COPY src dest

COPY [“src”, “dest”]

:源文件或者源目录

:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建

:::

6.3.10 VOLUME

VOLUME [“/data”]
容器数据卷,用于数据保存和持久化工作 该指令创建具有指定名称的装入点,并将其标记为保存来自本机主机或其他容器的外部装入卷。该值可以是 JSON 数组,也可以是具有多个参数(如 或 )的纯字符串。有关通过 Docker 客户端的更多信息/示例和挂载说明,请参阅通过卷共享目录文档。
VOLUMEVOLUME [“/var/log/“]VOLUME /var/logVOLUME /var/log /var/db

6.3.11 CMD

**<font style="color:#E8323C;">指定容器启动(docker run)后的要干的事情</font>**

CMD 容器启动命令

CMD 指令的格式和 RUN 相似,也是两种格式:

  • shell 格式:CMD <命令>
  • exec 格式:CMD [“可执行文件”, “参数1”, “参数2”, ……]
  • 参数列表格式:CMD[“参数1”, “参数2” ……]。在指定了 ENTRYPOINT 指定后,用 CMD 指定具体的参数

注意

Dockerfile 中可以有很多个 CMD 指令,<font style="color:#E8323C;">但是只有最后一个生效,CMD会被docker run 之后的参数替换</font>

参考官网Tomcat 的 dockerfile 演示介绍

  1. # 官网Dockerfile文件内容
  2. ......
  3. EXPOSE 8080
  4. CMD ["catalina.sh", "run"]
演示覆盖操作
  1. $ docker run -it -p 8080:8080 -d billygoo/tomcat8-jdk8:latest /bin/bash
  2. # 浏览器将无法访问8080 Tomcat 默认网页

它和前面 RUN 命令的区别

**<font style="color:#E8323C;">CMD 是在 docker run 时运行</font>**

**<font style="color:#E8323C;">RUN 是在 docker build 时运行</font>**

6.3.12 ENTRYPOINT

也是用来指定一个容器启动时要运行的命令

类似于 CMD 指令,但是 <font style="color:#E8323C;">ENTRYPOINT 不会被 docker run 后面的命令覆盖,而且这些命令行参数会被当做参数送给 ENTRYPOINT 指令指定的程序</font>

命令格式和案例说明

  1. 命令格式:
  2. ENTRYPOINT ["executable", "param1", "param2"]
  3. ENTRYPOINT 可以和 CMD 一起使用,一般是"变参"才会使用 CMD,这里的 CMD 等于是在给 ENTRYPOINT 传参
  4. 当指定了 ENTRYPOINT 后,CMD 的含义就发生了变化,
  5. 不再是直接运行其命令而是将 CMD 的内容作为参数传递给 ENTRYPOINT 指令,它两个组合会变成 <ENTRYPOINT> "<CMD>"
  6. 案例如下:假设已通过Dockerfile 构建了 "nginx:test 镜像"
  7. FROM nginx
  8. EXPOSE 80
  9. ENTRYPOINT ["nginx", "-c"] # 定参
  10. CMD ["/etc/nginx/nginx.conf"] # 变参
是否传参 按照dockerfile编写执行 传参运行
Docker命令 docker run nginx:test docker run nginx:test /etc/nginx/new.conf
衍生出的实际命令 nginx -c /etc/nginx/nginx.conf nginx -c /etc/nginx/new.conf

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

CMD 和 ENTRYPOINT 区别

:::warning CMD # 指定这个容器启动的时候要运行的命令,不可以追加命令

ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令

:::

💫1 Docker最新超详细版教程通俗易懂 - 图38

6.3.13 小总结

💫1 Docker最新超详细版教程通俗易懂 - 图39

💫1 Docker最新超详细版教程通俗易懂 - 图40

💫1 Docker最新超详细版教程通俗易懂 - 图41

6.4 实战测试

Docker Hub 中 99% 镜像都是从这个基础镜像过来的 FROM scratch ,然后配置需要的软件和配置来进行的构建

6.4.1 创建自己的CentOS镜像

  1. $ mkdir -pv /root/docker/mycentos01 && cd /root/docker/mycentos01
  2. #1.编写Dockerfile的文件
  3. $ vim Dockerfile
  4. #设置基础镜像
  5. FROM centos:centos7.9.2009
  6. #设置镜像作者
  7. MAINTAINER zhongzhiwei <zhongzhiwei@kubesphere.com>
  8. #配置环境变量
  9. ENV MYPATH="/data"
  10. #设置工作目录
  11. WORKDIR $MYPATH
  12. #安装 vim net-tools 软件
  13. RUN yum makecache fast && \
  14. yum install -y vim net-tools
  15. #清空yum缓存
  16. RUN yum clean all
  17. #提示暴露端口
  18. EXPOSE 80
  19. #docker build运行的提示
  20. RUN echo MYPATH is $MYPATH
  21. RUN echo "---> Success ......"
  22. #挂载卷
  23. VOLUME /data
  24. #设置容器启动的命令
  25. CMD /bin/bash
  26. #2.通过这个文件构建镜像
  27. #命令:docker build -f Dockerfile <文件路径> -t 镜像名:[TAG]
  28. $ docker build -t kube-centos:2.0 -f Dockerfile .
  29. #3.测试运行
  30. $ docker run -it --rm kube-centos:2.0 /bin/bash
  31. [root@3fe81eda9f21 data]# pwd
  32. /data
  33. [root@3fe81eda9f21 data]# ifconfig
  34. eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
  35. inet 172.17.0.11 netmask 255.255.0.0 broadcast 172.17.255.255
  36. ether 02:42:ac:11:00:0b txqueuelen 0 (Ethernet)
  37. RX packets 6 bytes 516 (516.0 B)
  38. RX errors 0 dropped 0 overruns 0 frame 0
  39. TX packets 0 bytes 0 (0.0 B)
  40. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
  41. lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
  42. inet 127.0.0.1 netmask 255.0.0.0
  43. loop txqueuelen 1000 (Local Loopback)
  44. RX packets 0 bytes 0 (0.0 B)
  45. RX errors 0 dropped 0 overruns 0 frame 0
  46. TX packets 0 bytes 0 (0.0 B)
  47. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
  48. [root@3fe81eda9f21 data]# vim --version | head -n 1
  49. VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Dec 15 2020 16:44:08)

我们可以列出本地镜像的变更历史

💫1 Docker最新超详细版教程通俗易懂 - 图42

6.4.2 CMD 和 ENTRYPOINT 区别

测试 CMD

  1. #编写Dockerfile文件
  2. $ cat dockerfile1
  3. FROM centos:centos7.9.2009
  4. CMD ls -al
  5. #构建镜像
  6. $ docker build -t cmdtest:1.0 -f dockerfile1 .
  7. #docker run 运行,发现我们的 ls -al 命令生效
  8. $ docker run cmdtest:1.0
  9. total 12
  10. drwxr-xr-x 1 root root 6 Aug 26 03:07 .
  11. drwxr-xr-x 1 root root 6 Aug 26 03:07 ..
  12. -rwxr-xr-x 1 root root 0 Aug 26 03:07 .dockerenv
  13. -rw-r--r-- 1 root root 12114 Nov 13 2020 anaconda-post.log
  14. lrwxrwxrwx 1 root root 7 Nov 13 2020 bin -> usr/bin
  15. drwxr-xr-x 5 root root 340 Aug 26 03:07 dev
  16. drwxr-xr-x 1 root root 66 Aug 26 03:07 etc
  17. drwxr-xr-x 2 root root 6 Apr 11 2018 home
  18. lrwxrwxrwx 1 root root 7 Nov 13 2020 lib -> usr/lib
  19. lrwxrwxrwx 1 root root 9 Nov 13 2020 lib64 -> usr/lib64
  20. ...省略部分输出...
  21. #想追加一个命令参数 -h 使其想变成 ls -alh
  22. $ docker run -it cmdtest:1.0 -h
  23. docker: Error response from daemon: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "-h": executable file not found in $PATH: unknown.
  24. #docker run 后的cmd 中的-h 会完全替换掉 CMD ls -al 命令,-h不是命令所以报错

测试 ENTRYPOINT

  1. $ cat dockerfile2
  2. FROM centos:centos7.9.2009
  3. ENTRYPOINT ls -al
  4. #构建镜像
  5. $ docker build -t cmdtest:2.0 -f dockerfile2 .
  6. #docker run 运行,发现我们的 ls -al 命令生效
  7. $ docker run -it cmdtest:2.0
  8. total 12
  9. drwxr-xr-x 1 root root 6 Aug 26 03:14 .
  10. drwxr-xr-x 1 root root 6 Aug 26 03:14 ..
  11. -rwxr-xr-x 1 root root 0 Aug 26 03:14 .dockerenv
  12. -rw-r--r-- 1 root root 12114 Nov 13 2020 anaconda-post.log
  13. lrwxrwxrwx 1 root root 7 Nov 13 2020 bin -> usr/bin
  14. drwxr-xr-x 5 root root 360 Aug 26 03:14 dev
  15. drwxr-xr-x 1 root root 66 Aug 26 03:14 etc
  16. drwxr-xr-x 2 root root 6 Apr 11 2018 home
  17. lrwxrwxrwx 1 root root 7 Nov 13 2020 lib -> usr/lib
  18. lrwxrwxrwx 1 root root 9 Nov 13 2020 lib64 -> usr/lib64
  19. ...省略部分输出...
  20. #想追加一个命令参数 -h 使其想变成 ls -alh
  21. #可以正常使用
  22. #追加的命令,是直接拼接在我们的 ENTRYPOINT 命令的后缀
  23. $ docker run -it cmdtest:2.0 -h
  24. total 12
  25. drwxr-xr-x 1 root root 6 Aug 26 03:15 .
  26. drwxr-xr-x 1 root root 6 Aug 26 03:15 ..
  27. -rwxr-xr-x 1 root root 0 Aug 26 03:15 .dockerenv
  28. -rw-r--r-- 1 root root 12114 Nov 13 2020 anaconda-post.log
  29. lrwxrwxrwx 1 root root 7 Nov 13 2020 bin -> usr/bin
  30. drwxr-xr-x 5 root root 360 Aug 26 03:15 dev
  31. drwxr-xr-x 1 root root 66 Aug 26 03:15 etc
  32. drwxr-xr-x 2 root root 6 Apr 11 2018 home
  33. lrwxrwxrwx 1 root root 7 Nov 13 2020 lib -> usr/lib
  34. lrwxrwxrwx 1 root root 9 Nov 13 2020 lib64 -> usr/lib64

:::warning Dockerfile 中很多命令都十分的相似,我们需要了解它们的区别,对比他们测试效果。

:::

6.4.3 实战:Tomcat 镜像

  1. 准备镜像文件 Tomcat 镜像,JDK 压缩包
  1. $ mkdir -pv /root/dockerfile/mytomcat01 && cd /root/dockerfile/mytomcat01
  2. $ wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.22/bin/apache-tomcat-9.0.22.tar.gz
  3. #上传相应的JDK包
  4. $ ls /root/dockerfile/mytomcat01/
  5. apache-tomcat-9.0.22.tar.gz jdk-8u11-linux-x64.tar.gz
  1. 编写Dockerfile文件
  1. #官方命名为 Dockerfile,docker build 会自动寻找这个文件,就不需要 -f 指定dockerfile文件了
  2. $ vim /root/dockerfile/mytomcat01/Dockerfile
  3. $ cat /root/dockerfile/mytomcat01/readme.txt <<EOF
  4. #构建 tomcat 镜像
  5. docker build -t mytomcat:1.0 -f Dockerfile .
  6. EOF
  7. $ vim Dockerfile
  8. #设置基础镜像
  9. FROM centos:centos7.9.2009
  10. #设置镜像作者
  11. MAINTAINER zhongzhiwei <zhongzhiwei@kubesphere.com>
  12. #配置环境变量
  13. ENV MYPATH="/usr/local/"
  14. #设置工作目录
  15. WORKDIR $MYPATH
  16. #拷贝 & 压缩文件
  17. COPY readme.txt $MYPATH
  18. ADD apache-tomcat-9.0.22.tar.gz $MYPATH
  19. ADD jdk-8u11-linux-x64.tar.gz $MYPATH
  20. #设置环境变量
  21. ENV JAVA_HOME="/usr/local/jdk1.8.0_11"
  22. ENV JRE_HOME="/usr/local/jdk1.8.0_11/jre"
  23. ENV CLASSPATH="$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar"
  24. ENV CATALINA_HOME="/usr/local/apache-tomcat-9.0.22"
  25. ENV CATALINA_BASE="/usr/local/apache-tomcat-9.0.22"
  26. ENV PATH="$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$CATALINA_BASE/bin:$PATH"
  27. #设置成阿里云源
  28. RUN rm -f /etc/yum.repos.d/* && \
  29. curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
  30. #提示暴露端口
  31. EXPOSE 8080
  32. #docker build运行的提示
  33. RUN echo MYPATH is $MYPATH
  34. RUN echo "---> Success ......"
  35. #挂载卷
  36. VOLUME /data
  37. #设置容器启动的命令
  38. CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.22/logs/catalina.out
  1. 构建镜像
  1. $ docker build -t mytomcat:1.0 .
  2. $ docker run -it -d \
  3. --name tomcat01 -p 8080:8080 \
  4. -v /data/docker/mytomcat01/test:/usr/local/apache-tomcat-9.0.22/webapps/test \
  5. -v /data/docker/mytomcat01/logs:/usr/local/apache-tomcat-9.0.22/logs \
  6. mytomcat:1.0
  1. 访问测试
  1. $ curl localhost:8080

💫1 Docker最新超详细版教程通俗易懂 - 图43

  1. 发布项目(由于做了卷挂载,我们直接在本地编写项目)
  1. $ mkdir -p /data/docker/mytomcat01/test/WEB-INFO
  2. $ vim /data/docker/mytomcat01/test/WEB-INFO/web.xml
  3. <?xml version="1.0" encoding="UTF-8"?>
  4. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns="http://java.sun.com/xml/ns/javaee"
  6. xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  7. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  8. id="WebApp_ID" version="2.5">
  9. </web-app>
  10. #WEB-INFO需要和index.jsp在同一级目录。
  11. $ cat > /data/docker/mytomcat01/test/index.jsp <<EOF
  12. <%@ page language="java" contentType="text/html; charset=UTF-8"
  13. pageEncoding="UTF-8"%>
  14. <!DOCTYPE html>
  15. <html>
  16. <head>
  17. <meta charset="utf-8">
  18. <title>QingCloud (www.kubesphere.com)</title>
  19. </head>
  20. <body>
  21. <%-- 该部分注释在网页中不会被显示--%>
  22. <p>
  23. Hello , World
  24. </p>
  25. </body>
  26. </html>
  27. EOF
  28. $ curl 10.0.0.101:8080/test/
  29. <!DOCTYPE html>
  30. <html>
  31. <head>
  32. <meta charset="utf-8">
  33. <title>QingCloud (www.kubesphere.com)</title>
  34. </head>
  35. <body>
  36. <p>
  37. Hello , World
  38. </p>
  39. </body>
  40. </html>

💫1 Docker最新超详细版教程通俗易懂 - 图44

发现,项目部署完成,可以直接访问OK!

以后开发的步骤,需要掌握 Dockerfile 的编写!我们之前的一切都是使用 Docker 镜像来发布运行的!

7 发布自己的镜像

7.1 发布镜像到 Dockerhub

DockerHub

  1. DockerHub 地址 https://hub.docker.com/ 注册自己的账号
  2. 确定这个账号可以登录
  3. 在我们服务器上提交自己的镜像
  1. $ docker login --help
  2. Usage: docker login [OPTIONS] [SERVER]
  3. Log in to a Docker registry.
  4. If no server is specified, the default is defined by the daemon.
  5. Options:
  6. -p, --password string Password
  7. --password-stdin Take the password from stdin
  8. -u, --username string Username
  1. 登录完毕后就可以提交镜像了,就是一步 docker push 镜像名:[TAG]

范例:

  1. $ docker login -u "dragonzw" -p "SZzhongaislf"
  2. WARNING! Using --password via the CLI is insecure. Use --password-stdin.
  3. WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
  4. Configure a credential helper to remove this warning. See
  5. https://docs.docker.com/engine/reference/commandline/login/#credentials-store
  6. Login Succeeded
  7. #设置镜像的标签
  8. $ docker tag mytomcat:1.0 dragonzw/tomcat:1.0
  9. #推送镜像到DockerHub
  10. $ docker push dragonzw/tomcat:1.0

💫1 Docker最新超详细版教程通俗易懂 - 图45

7.2 发布镜像到 阿里云

  1. $ sudo docker login --username=dragon志伟 registry.cn-shenzhen.aliyuncs.com
  2. Password:
  3. WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
  4. Configure a credential helper to remove this warning. See
  5. https://docs.docker.com/engine/reference/commandline/login/#credentials-store
  6. Login Succeeded
  7. #重新镜像打标签
  8. #$ docker tag tomcat:[镜像版本号] registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:[镜像版本号]
  9. $ docker tag mytomcat:1.0 registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:1.0
  10. #推送镜像
  11. #$ docker push registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:[镜像版本号]
  12. $ docker push registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:1.0
  13. #$ docker pull registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:[镜像版本号]
  14. $ docker pull registry.cn-shenzhen.aliyuncs.com/dragonzw_personal_images/tomcat:1.0

在阿里云个人的镜像仓库就可以查看到 tomcat 的镜像版本。

💫1 Docker最新超详细版教程通俗易懂 - 图46

:::warning 阿里云容器镜像就可以参考阿里云官方文档即可。

:::

7.3 小结

💫1 Docker最新超详细版教程通俗易懂 - 图47

8 Docker 网络

8.1 理解 docker0

清空所有的 Docker 环境。

  1. $ ip addr show
  2. #本地回环网卡
  3. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
  4. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  5. inet 127.0.0.1/8 scope host lo
  6. valid_lft forever preferred_lft forever
  7. inet6 ::1/128 scope host
  8. valid_lft forever preferred_lft forever
  9. #本机的内网IP地址
  10. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
  11. link/ether 00:0c:29:16:73:35 brd ff:ff:ff:ff:ff:ff
  12. inet 10.0.0.101/24 brd 10.0.0.255 scope global eth0
  13. valid_lft forever preferred_lft forever
  14. inet6 fe80::20c:29ff:fe16:7335/64 scope link
  15. valid_lft forever preferred_lft forever
  16. #docker服务生成的docker0网卡
  17. 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
  18. link/ether 02:42:fc:db:df:7e brd ff:ff:ff:ff:ff:ff
  19. inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
  20. valid_lft forever preferred_lft forever
  21. inet6 fe80::42:fcff:fedb:df7e/64 scope link
  22. valid_lft forever preferred_lft forever

三个网络:

:::warning 问题:docker 是如何处理容器网络访问的?

:::

  1. $ docker run -d -P --name tomcat01 tomcat:8.5.81
  2. #查看容器的内部网络地址 ip addr show
  3. #发现容器启动的时候会得到一个 eth0@xxx ip地址,docker DHCP分配
  4. $ docker exec -it tomcat01 /bin/bash
  5. root@f408a9817eee:/usr/local/tomcat# apt update
  6. root@f408a9817eee:/usr/local/tomcat# apt install -y iproute2 iputils-ping &> /dev/null
  7. root@767f7355e9eb:/usr/local/tomcat# ip addr
  8. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
  9. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  10. inet 127.0.0.1/8 scope host lo
  11. valid_lft forever preferred_lft forever
  12. 77: eth0@if78: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
  13. link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  14. inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
  15. valid_lft forever preferred_lft forever
  16. #思考:Linux能不能ping通容器内部
  17. $ ping -c1 -W1 172.17.0.2
  18. PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
  19. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.034 ms
  20. #Linux 可以ping通 docker容器内部

原理:

  1. 每启动一个 docker 容器,docker 就会给 docker 容器分配一个 IP 地址,安装了docker,就会又一个网卡 docker0

桥接模式,使用的技术是 veth-pair 技术

再次测试宿主机 ip addr

💫1 Docker最新超详细版教程通俗易懂 - 图48

  1. 在启动一个 tomcat 容器测试,发现又多了一对网卡!
  1. $ docker run -d -P --name tomcat02 tomcat
  2. $ docker exec -it tomcat02 /bin/bash
  3. root@15eb260c7094:/usr/local/tomcat# apt update
  4. root@15eb260c7094:/usr/local/tomcat# apt install -y iproute2 iputils-ping &> /dev/null
  5. root@15eb260c7094:/usr/local/tomcat# ip addr
  6. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
  7. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  8. inet 127.0.0.1/8 scope host lo
  9. valid_lft forever preferred_lft forever
  10. 79: eth0@if80: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
  11. link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
  12. inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
  13. valid_lft forever preferred_lft forever

💫1 Docker最新超详细版教程通俗易懂 - 图49

:::warning 我们发现这个容器带来的网卡,都是一对一对的。

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

正因为有这个特性,veth-pair 充当了一个桥接,链接各种虚拟网络设备

OpenStack Docker容器之间连接 OVS 连接 都是使用 veth-pair 技术。

:::

  1. 我们来测试一下 tomcat01 和 tomcat02 是否可以ping 通
  1. $ docker exec -it tomcat01 /bin/bash
  2. root@767f7355e9eb:/usr/local/tomcat# ping -c 1 -W 1 172.17.0.3
  3. PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
  4. 64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.069 ms
  5. --- 172.17.0.3 ping statistics ---
  6. 1 packets transmitted, 1 received, 0% packet loss, time 0ms
  7. rtt min/avg/max/mdev = 0.069/0.069/0.069/0.000 ms
  8. #结论:容器和容器之间是可以相互ping通的!

画板

:::warning 路由的定义是指路由寻址功能,跨网段的时候会生成一个路由表,转发到对应的接口属于三层;

二层交换只能在局域网(同网段)连接。

:::

:::warning 结论:tomcat01 和 tomcat02 是拥有一个公用的路由器 docker0

所有的容器不指定网络的情况下,都是 docker0 路由的,Docker 会给我们的容器分配一个默认的可用的IP地址

Docker 默认的网段是 172.17.0.0/16 B类地址默认可以存放 65535 个地址

:::

小结:

Docker中 docker0 使用的是 Linux 的桥接技术,docker0 和 容器之间是使用 veth-pair 虚拟接口技术。

即宿主机中是一个 Docker 的网桥:docker0。Docker 中的所有的网络接口都是虚拟的。虚拟的转发效率高(内网传递数据)。

只要容器删除,对应的网桥就没了(即虚拟接口 veth-pair 也会消失)。

画板

  1. docker network inspect bridge

💫1 Docker最新超详细版教程通俗易懂 - 图52

8.2 —link

思考一个场景:我们编写了一个微服务,database url=ip,项目不重启,数据库IP地址掉了,我们希望可以处理这个问题,可以通过名字来访问容器?

  1. $ docker exec -it tomcat01 ping tomcat02
  2. ping: tomcat02: Name or service not known
  3. #默认是ping 不通名字
  4. #如何进行解决呢?
  5. #使用--link就可以解决网络连通问题
  6. $ docker run -it -d --name tomcat03 --link tomcat02 tomcat
  7. #tomcat03 ping tomcat02
  8. $ docker exec -it tomcat03 ping -c 1 -W 1 tomcat02
  9. PING tomcat02 (172.17.0.3) 56(84) bytes of data.
  10. 64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.121 ms
  11. --- tomcat02 ping statistics ---
  12. 1 packets transmitted, 1 received, 0% packet loss, time 0ms
  13. rtt min/avg/max/mdev = 0.121/0.121/0.121/0.000 ms
  14. #tomcat02 ping tomcat03
  15. $ docker exec -it tomcat02 ping tomcat03
  16. ping: tomcat03: Name or service not known
  17. #查看bridge网络模式下的详细信息
  18. $ docker network inspect bridge

探究 docker network inspect

💫1 Docker最新超详细版教程通俗易懂 - 图53

  1. $ docker inspect tomcat03 | grep -A 3 "Links"
  2. "Links": [
  3. "/tomcat02:/tomcat03/tomcat02"
  4. ],
  5. #说明了tomcat03 和 tomcat02 的额绑定关系,=/接收容器名/源容器名或者别名

其实这个 tomcat03 就是在本地配置了 tomcat02 的信息配置

  1. #登录tomcat03 的容器
  2. $ docker exec -it tomcat03 /bin/bash
  3. root@868350cec9a0:/usr/local/tomcat# cat /etc/resolv.conf
  4. # Generated by NetworkManager
  5. nameserver 114.114.114.114
  6. nameserver 8.8.8.8
  7. root@868350cec9a0:/usr/local/tomcat# cat /etc/hosts
  8. 127.0.0.1 localhost
  9. ::1 localhost ip6-localhost ip6-loopback
  10. fe00::0 ip6-localnet
  11. ff00::0 ip6-mcastprefix
  12. ff02::1 ip6-allnodes
  13. ff02::2 ip6-allrouters
  14. 172.17.0.3 tomcat02 15eb260c7094
  15. 172.17.0.4 868350cec9a0
  16. root@868350cec9a0:/usr/local/tomcat# ping -c 1 -W 1 tomcat02
  17. PING tomcat02 (172.17.0.3) 56(84) bytes of data.
  18. 64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.096 ms
  19. --- tomcat02 ping statistics ---
  20. 1 packets transmitted, 1 received, 0% packet loss, time 0ms
  21. rtt min/avg/max/mdev = 0.096/0.096/0.096/0.000 ms
  22. #--link 就是在容器的 /etc/hosts 配置中增加了一个 172.17.0.3 tomcat02 15eb260c7094 的条目信息
  23. #探究:--link 本质就是容器中 /etc/hosts 新增相应的条目。

现在 Docker 已经不建议使用 —link 了;目前推荐使用自定义网络。

默认 docker0 问题,不支持容器名连接访问

8.3 自定义网络

容器互联技术:—link 和 自定义网络

  1. #查看所有的Docker网络信息
  2. $ docker network ls
  3. NETWORK ID NAME DRIVER SCOPE
  4. 4490c3a848b4 bridge bridge local
  5. 2373b4b3186d host host local
  6. 5536bc79b4b6 none null local

Docker 网络模式

网络模型 简介
bridge 为每一个容器分配、设置IP等,并将容器连接到一个<font style="color:#E8323C;">docker0</font>虚拟网桥,默认为该模式
host 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口
none 容器有独立的 Network Namespace(网络名称空间),但是并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP等。(基本上不会使用)
container 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定的容器共享IP,端口范围等

docker network 主流的是 bridge、host、none,常用的是 bridge、host。

:::warning bridge:桥接 docker (默认,自定义网络也是 bridge 模式)

none:不配置网络

host:和宿主机共享网络

container:容器网络联通(局限性很大)

:::

测试:

  1. #直接启动命令默认会添加--net bridge,而这个就是docker0
  2. $ docker run -it -d -P --name tomcat01 tomcat
  3. #等价于
  4. $ docker run -it -d -P --name tomcat01 --net bridge tomcat
  5. #docker0特点:默认,容器间的域名是不通,--link可以打通连通
  6. #可以创建自定义网络
  7. #--driver bridge :管理网络的驱动程序(默认为“桥接”)
  8. #--subnet 192.168.0.0/16 :CIDR格式的子网,表示一个网段
  9. #--gateway 192.168.0.1 :主子网的IPv4或IPv6网关
  10. $ docker network create \
  11. --driver bridge \
  12. --subnet 192.168.0.0/16 \
  13. --gateway 192.168.0.1 mynet
  14. $ docker network ls
  15. NETWORK ID NAME DRIVER SCOPE
  16. e79095f379fb bridge bridge local
  17. 993e748549d7 host host local
  18. 3e91d8f383af mynet bridge local
  19. 4bf3eb064757 none null local

自定义网络就已经创建完毕了

💫1 Docker最新超详细版教程通俗易懂 - 图54

  1. $ docker run -it -d --name alpine01 --network mynet alpine
  2. $ docker run -it -d --name alpine02 --network mynet alpine
  3. $ docker inspect alpine02 | grep -A 18 "Networks"
  4. "Networks": {
  5. "mynet": {
  6. "IPAMConfig": null,
  7. "Links": null,
  8. "Aliases": [
  9. "348e6354072b"
  10. ],
  11. "NetworkID": "5aa4b631f9ef3b6189496fd2bdccf673f18dcee69232b09cb4095870eef20387",
  12. "EndpointID": "64fdcb4b0dd537b2aeef757b8f2114ed84187aec7c99ec3ea611d375b0646c2a",
  13. "Gateway": "192.168.0.1",
  14. "IPAddress": "192.168.0.3",
  15. "IPPrefixLen": 16,
  16. "IPv6Gateway": "",
  17. "GlobalIPv6Address": "",
  18. "GlobalIPv6PrefixLen": 0,
  19. "MacAddress": "02:42:c0:a8:00:03",
  20. "DriverOpts": null
  21. }
  22. }
  23. #再次测试 ping 连接
  24. #首先使用alpine01 ping alpine02的IP地址
  25. $ docker exec -it alpine01 ping -c 1 -W 1 192.168.0.3
  26. PING 192.168.0.3 (192.168.0.3): 56 data bytes
  27. 64 bytes from 192.168.0.3: seq=0 ttl=64 time=0.094 ms
  28. --- 192.168.0.3 ping statistics ---
  29. 1 packets transmitted, 1 packets received, 0% packet loss
  30. round-trip min/avg/max = 0.094/0.094/0.094 ms
  31. #再次直接使用 alpine01 ping alpine02
  32. #现在不使用--link也可以 ping 名字!!!
  33. $ docker exec -it alpine01 ping -c 1 -W 1 alpine02
  34. PING alpine02 (192.168.0.3): 56 data bytes
  35. 64 bytes from 192.168.0.3: seq=0 ttl=64 time=0.127 ms
  36. --- alpine02 ping statistics ---
  37. 1 packets transmitted, 1 packets received, 0% packet loss
  38. round-trip min/avg/max = 0.127/0.127/0.127 ms

自定义的网络 Docker 都已经帮运维/开发 维护好了对应的关系,推荐使用自定义网络。

原理就是这两个容器都是连的自定义网络,连接在同一个自定义网络的容器之间端口会自动相互暴露,而且不会向以外的显示任何端口,这样就更好的进行了容器见相互通信和隔离

好处:不同的集群使用不同的自定义网络,保证集群是安全和健康的。

8.4 网络连通

画板

  1. $ docker run -it --name alpine01-docker0 --network bridge -d alpine
  2. $ docker run -it --name alpine02-docker0 --network bridge -d alpine
  3. $ docker run -it -d --name alpine01 --network mynet alpine
  4. $ docker run -it -d --name alpine02 --network mynet alpine
  5. #默认不同的网络命名空间是不能够相互通信的
  6. $ docker exec -it alpine01-docker0 ping -c 1 -W 1 192.168.0.2
  7. PING 192.168.0.2 (192.168.0.2): 56 data bytes
  8. --- 192.168.0.2 ping statistics ---
  9. 1 packets transmitted, 0 packets received, 100% packet loss
  10. $ docker exec -it alpine01-docker0 ping alpine01
  11. ping: bad address 'alpine01'

💫1 Docker最新超详细版教程通俗易懂 - 图56

  1. #测试打通alpine01-docker0 - alpine01
  2. $ docker network connect mynet alpine01-docker0
  3. #联通之后,就是将 alpine01-docker0 放到了 mynet 网络下
  4. $ docker inspect alpine01-docker0
  5. #一个容器两个IP地址

💫1 Docker最新超详细版教程通俗易懂 - 图57

  1. #再次测试
  2. $ docker exec -it alpine01-docker0 ping -c 1 -W 1 192.168.0.2
  3. PING 192.168.0.2 (192.168.0.2): 56 data bytes
  4. 64 bytes from 192.168.0.2: seq=0 ttl=64 time=0.088 ms
  5. --- 192.168.0.2 ping statistics ---
  6. 1 packets transmitted, 1 packets received, 0% packet loss
  7. round-trip min/avg/max = 0.088/0.088/0.088 ms
  8. $ docker exec -it alpine01-docker0 ping -c 1 -W 1 alpine01
  9. PING alpine01 (192.168.0.2): 56 data bytes
  10. 64 bytes from 192.168.0.2: seq=0 ttl=64 time=0.117 ms
  11. --- alpine01 ping statistics ---
  12. 1 packets transmitted, 1 packets received, 0% packet loss
  13. round-trip min/avg/max = 0.117/0.117/0.117 ms
  14. #alpine02-docker0 还是依旧无法联通alpine01 alpine02

结论:假设要跨网络操作其他容器,就需要使用 docker network connect 联通了!

8.5 实战:部署 Redis 集群

画板

  1. #创建网卡
  2. $ docker network create redis --subnet 172.38.0.0/16
  3. #批量创建六个 Redis的Docker容器
  4. for port in {1..6} ;do
  5. mkdir -pv /data/redis/node-${port}/conf
  6. touch /data/redis/node-${port}/conf/redis.conf
  7. cat << EOF > /data/redis/node-${port}/conf/redis.conf
  8. port 6379
  9. bind 0.0.0.0
  10. cluster-enabled yes
  11. cluster-config-file nodes.conf
  12. cluster-node-timeout 5000
  13. cluster-announce-ip 172.38.0.1${port}
  14. cluster-announce-port 6379
  15. cluster-announce-bus-port 16379
  16. appendonly yes
  17. EOF
  18. done
  19. for port in {1..6} ; do
  20. docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
  21. -v /data/redis/node-${port}/data:/data \
  22. -v /data/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
  23. -d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 \
  24. /usr/local/bin/redis-server /etc/redis/redis.conf
  25. done
  26. #范例:
  27. docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
  28. -v /data/redis/node-1/data:/data \
  29. -v /data/redis/node-1/conf/redis.conf:/etc/redis/redis.conf
  30. -d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 \
  31. /usr/local/bin/redis-server /etc/redis/redis.conf

范例:

  1. #创建Redis集群
  2. redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 \
  3. 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
  4. $ docker exec -it redis-1 /bin/sh
  5. /data # redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 \
  6. > 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
  7. >>> Performing hash slots allocation on 6 nodes...
  8. Master[0] -> Slots 0 - 5460
  9. Master[1] -> Slots 5461 - 10922
  10. Master[2] -> Slots 10923 - 16383
  11. Adding replica 172.38.0.15:6379 to 172.38.0.11:6379
  12. Adding replica 172.38.0.16:6379 to 172.38.0.12:6379
  13. Adding replica 172.38.0.14:6379 to 172.38.0.13:6379
  14. M: 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379
  15. slots:[0-5460] (5461 slots) master
  16. M: 2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379
  17. slots:[5461-10922] (5462 slots) master
  18. M: 5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379
  19. slots:[10923-16383] (5461 slots) master
  20. S: 2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379
  21. replicates 5090c8478f101c5f1520b9264cab5d602befd6a8
  22. S: 55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379
  23. replicates 24a8d8f3302ce28f1b9b06ce3bec871adb8585df
  24. S: f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379
  25. replicates 2eff279dc91be6f8840a59f990b58ac740ebfa01
  26. Can I set the above configuration? (type 'yes' to accept): yes
  27. >>> Nodes configuration updated
  28. >>> Assign a different config epoch to each node
  29. >>> Sending CLUSTER MEET messages to join the cluster
  30. Waiting for the cluster to join
  31. ...
  32. >>> Performing Cluster Check (using node 172.38.0.11:6379)
  33. M: 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379
  34. slots:[0-5460] (5461 slots) master
  35. 1 additional replica(s)
  36. S: 2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379
  37. slots: (0 slots) slave
  38. replicates 5090c8478f101c5f1520b9264cab5d602befd6a8
  39. S: 55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379
  40. slots: (0 slots) slave
  41. replicates 24a8d8f3302ce28f1b9b06ce3bec871adb8585df
  42. M: 5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379
  43. slots:[10923-16383] (5461 slots) master
  44. 1 additional replica(s)
  45. M: 2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379
  46. slots:[5461-10922] (5462 slots) master
  47. 1 additional replica(s)
  48. S: f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379
  49. slots: (0 slots) slave
  50. replicates 2eff279dc91be6f8840a59f990b58ac740ebfa01
  51. [OK] All nodes agree about slots configuration.
  52. >>> Check for open slots...
  53. >>> Check slots coverage...
  54. [OK] All 16384 slots covered.
  55. /data # redis-cli -c
  56. #查看Redis集群的信息
  57. 127.0.0.1:6379> cluster info
  58. cluster_state:ok
  59. cluster_slots_assigned:16384
  60. cluster_slots_ok:16384
  61. cluster_slots_pfail:0
  62. cluster_slots_fail:0
  63. cluster_known_nodes:6
  64. cluster_size:3
  65. cluster_current_epoch:6
  66. cluster_my_epoch:1
  67. cluster_stats_messages_ping_sent:249
  68. cluster_stats_messages_pong_sent:250
  69. cluster_stats_messages_sent:499
  70. cluster_stats_messages_ping_received:245
  71. cluster_stats_messages_pong_received:249
  72. cluster_stats_messages_meet_received:5
  73. cluster_stats_messages_received:499
  74. #查看Redis集群节点健康
  75. 127.0.0.1:6379> cluster nodes
  76. 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379@16379 myself,master - 0 1661526536000 1 connected 0-5460
  77. 2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379@16379 slave 5090c8478f101c5f1520b9264cab5d602befd6a8 0 1661526537442 4 connected
  78. 55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379@16379 slave 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 0 1661526537000 5 connected
  79. 5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379@16379 master - 0 1661526536000 3 connected 10923-16383
  80. 2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379@16379 master - 0 1661526536433 2 connected 5461-10922
  81. f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379@16379 slave 2eff279dc91be6f8840a59f990b58ac740ebfa01 0 1661526535526 6 connected
  82. 127.0.0.1:6379> set k1 v1
  83. -> Redirected to slot [12706] located at 172.38.0.13:6379 #redis-3 的容器操作
  84. OK
  85. #redis-3暂停后再次查看
  86. /data # redis-cli -c
  87. 127.0.0.1:6379> cluster nodes
  88. 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 172.38.0.11:6379@16379 myself,master - 0 1661526692000 1 connected 0-5460
  89. 2d2733a06379f90a2c51c0e131b0391160d8f6b8 172.38.0.14:6379@16379 master - 0 1661526693000 7 connected 10923-16383
  90. 55d41b51e6053d6dcf9b730a79ce6cca1314e363 172.38.0.15:6379@16379 slave 24a8d8f3302ce28f1b9b06ce3bec871adb8585df 0 1661526693143 5 connected
  91. 5090c8478f101c5f1520b9264cab5d602befd6a8 172.38.0.13:6379@16379 master,fail - 1661526636272 1661526634000 3 connected
  92. 2eff279dc91be6f8840a59f990b58ac740ebfa01 172.38.0.12:6379@16379 master - 0 1661526692740 2 connected 5461-10922
  93. f8d17750650743957b846ccaab8799fca7c301a9 172.38.0.16:6379@16379 slave 2eff279dc91be6f8840a59f990b58ac740ebfa01 0 1661526691733 6 connected
  94. 127.0.0.1:6379> get k1
  95. -> Redirected to slot [12706] located at 172.38.0.14:6379 #redis-4 从容器变成Master
  96. "v1"
  97. #将redis-3 容器暂停
  98. $ docker stop redis-3

9 将SpringBoot 微服务打包成Docker镜像

  1. 构建 Spring Boot 项目

💫1 Docker最新超详细版教程通俗易懂 - 图59

  1. package com.example.demo.controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. @RestController
  5. public class HelloController {
  6. @RequestMapping("/hello")
  7. public String hello() {
  8. return "hello,kubesphere";
  9. }
  10. }

💫1 Docker最新超详细版教程通俗易懂 - 图60

  1. 打包应用

💫1 Docker最新超详细版教程通俗易懂 - 图61

  1. 编写 Dockerfile
  1. #使用基础镜像
  2. FROM java:8
  3. #将jar包拷贝到容器内
  4. ADD *.jar /app.jar
  5. #提示指定暴露端口
  6. EXPOSE 8080
  7. CMD ["--server.port=8080"]
  8. ENTRYPOINT ["java", "-jar", "/app.jar"]
  1. 构建镜像
  1. $ mkdir -pv /data/springboot ; cd /data/springboot
  2. $ ls -lh
  3. total 16M
  4. -rw-r--r-- 1 root root 16M Aug 26 23:29 demo-0.0.1-SNAPSHOT.jar
  5. -rw-r--r-- 1 root root 186 Aug 26 23:30 Dockerfile
  6. $ docker images myapp:1.0
  7. REPOSITORY TAG IMAGE ID CREATED SIZE
  8. myapp 1.0 b29e611caf73 8 seconds ago 660MB
  1. 发布运行
  1. $ docker run -it -d --name myspringboot-web -p 8080:8080 myapp:1.0
  2. $ curl 10.0.0.101:8080/hello
  3. hello,kubesphere

💫1 Docker最新超详细版教程通俗易懂 - 图62

以后使用了 Docker 之后,给运维或者开发交付就是一个镜像即可!

预告:如果有很多镜像?怎么处理呢?解决方法:Docker Compose / Docker Swarm

企业实战
  • Docker Compose
  • Docker Swarm