Docker基础

Docker学习笔记

容器化技术介绍

历史演变

应用的部署演化主要有三个阶段:

graph LR A[物理机时代]-->B[虚拟机时代]-->C[容器化时代]

物理机时代

在物理机时代,应用部署在物理机上。那么如果想要安装一个软件,需要准备一台物理机,并在这个物理机上安装操作系统,然后安装各种各样的为了软件能够运行所需要的插件、资源等。物理机时代有如下缺点:

  • 安装部署慢:在物理机上部署应用,需要为应用的运行环境做准备。例如:运行java应用,就要安装jre,运行python就要安装python...
  • 成本很高:每一个应用不是在一个物理机上。为了应用的稳定性,一般都会部署多个实例,那么就需要准备多个物理机。而一台物理机包括cpu,内存,磁盘等,如果应用对性能要求较高的话,那么对硬件是要求很高的,组装一台物理机的成本也很高。
  • 资源浪费:物理机上安装应用,对资源的使用是不平衡的。如果应用是存储类型的,那么对磁盘,内存等要求会很高,而对cpu要求不高。如果应用是计算型的,那么就对cpu要求很高,而对磁盘内存等要求就不高。因此物理机需要针对应用配置不同的硬件,比较繁琐,否则就会导致资源浪费。而这个硬件资源的平衡点又不好取。
  • 难于扩展和迁移:如果想要对应用做横向扩展,如果只是加内存还好,如果想要扩展实例重新部署一台,那么新的服务器也需要安装各种各样的运行环境,配置各种各样的参数等,比较繁琐且容易出错。如果是想要将应用从旧的win服务器,转到高性能的linux服务器,那么如果软件只能在win系统上运行的话,就会出问题。
  • 受限于硬件:可能会产生硬件兼容性问题。

虚拟机时代

由于物理机时代的各种各样的问题,为了解决这些问题,进入了虚拟机时代。虚拟机是建立在物理机之上的。

image-20220118151553726

虚拟机具有如下特点:

  • 多部署:一台物理机上可以部署多个虚拟机

  • 资源池:多个虚拟机共享物理机所拥有的资源。Host Operation System是物理机所拥有的资源,虚拟机的资源统一通过Hypervisor来分配。

  • 资源隔离:虚拟机之间资源是完全隔离的,因此一台虚拟机出问题,不会影响其他虚拟机的运行

  • 容易扩展:虚拟机的拓展非常方便,由于虚拟机的资源统一通过Hypervisor来分配,因此只要硬件资源够大,那么很方便就可以扩展一个新的虚拟机。

  • 需要安装操作系统:上面的都是虚拟机的优点。虚拟机就是相当于在硬件操作系统的基础上又重新创建了一套虚拟的操作系统,虚拟操作系统之间隔离。因此想要使用一个虚拟机就需要安装一个操作系统。这也是虚拟机最大的缺点。可能我一个应用只有几百kb,而且运行时所需要占用的资源也不是很多,但是为了支撑这个应用,就需要安装一个操作系统,一个操作系统占用的最小资源也得几百兆。同时虚拟机占用的cpu内存资源等也比这个微小的应用要高的多。

    而一个物理机上可能会安装几十个虚拟机,如果每个虚拟机上只安装一个上面这种小的应用,那么操作系统浪费的资源会多得多。会造成很大的资源浪费。

    为了解决上面的问题,由于我们关心的只有应用软件的运行,因此就有人想,能不能把app从操作系统中剥离出来,因此就进入了容器化时代。

容器化时代

容器化技术可以看成是一个不需要安装操作系统的虚拟机。这样描述可能不太准确,但是可以先这样去简单理解。

image-20220119100450562

右图是虚拟机模型:每一个虚拟机都需要安装Guest OS--客户操作系统,然后在客户操作系统之上安装app及各种类库。

左图是容器化模型:Docker只是容器化的一个典型代表,Docker安装在主操作系统之上,然后通过Docker的沙箱机制隔离出一个一个的容器,容器只包含app和类库,不包含操作系统,所以体积是非常小的。

容器化技术并不会取代虚拟化技术

虚拟化主要是物理资源层面的隔离。例如为每个虚拟机分配多少的内存,分配几核的cpu。而容器化技术,所有的容器都是共同运行在物理资源上的,物理资源不隔离,应用隔离,应用之间不会互相影响。

虚拟化和容器化技术混合运用场景

  • 如阿里云,腾讯云的服务器性能好:采用的是虚拟机和容器化技术的实现

  • 比如在阿里云购买ECS服务器,在阿里云那边就是一个虚拟机,根据应用场景不同,安装不同容器,比如购买阿里云的mysql数据库,阿里云通过容器化技术自动安装mysql容器,就得到一台mysql服务器了,这里虚拟化主要是对硬件资源的隔离,但在虚拟机中所使用到的mysql是通过容器化技术部署的

容器化解决的问题

在我之前的公司,使用的是虚拟化技术。而且当时项目没有使用springboot,因此当时项目打出来的压缩包,项目包和类库包是两个文件夹的。

当时在上线项目前,我们开发需要准备:项目及类库压缩包、环境变量修改项整理一个文件、上线操作步骤要整理文档、如果需要安装sdk等,还需要准备sdk文件及安装方式。然后上线时,运维按照操作步骤文档,将jar包放入文档上写的位置,安装sdk等,配置环境变量,导入种子数据等....

经过了一系列繁琐的操作,终于完成,项目启动......... 报错。然后又是一系列的排查,结果发现给的文档里路径写错了/环境变量写错了等。

而使用容器化技术后,开发人员会将项目打成一个镜像--app容器。镜像内包含了所有项目启动需要的类库、变量配置、数据、sdk等。只要这个镜像经过层层测试,没有问题可以上线了。那么直接把这个镜像发送给运维,运维直接部署这个镜像,这样就避免了上面虚拟化的那些问题。

容器化的特点

经过上面的简单了解,可以总结出容器化具有如下特点:

  • 标准化的迁移方式:应用统一通过镜像来部署,迁移时只需要拿到镜像就可以了。
  • 统一的参数配置:参数也一块打包进镜像里了。
  • 自动化部署:只需要提供镜像,Docker会自动将镜像还原成应用并进行部署启动。
  • 应用集群监控:容器化技术是自带健康检查的,可以很轻松的查到出问题的应用。
  • 开发与运维之间沟通的桥梁:使用容器化技术后,减少了运维的工作量,也避免了上线出问题,开发和运维之间甩锅的情况。

Docker介绍

docker是一个容器化平台,主要可以分为如下三层:

image-20220119134627317

  • 应用程序:部署的具体的应用服务

  • Docker引擎:docker引擎是一个中间层,从下层物理机获取可用资源,再通过docker引擎为app分配提供可用资源。应用程序不直接访问底层资源,通过docker中间层操作。

    docker引擎结构如下:

    image-20220119135332816
    1. docker client: docker客户端,提供交互命令
    2. rest api: 通讯层,docker client通过rest api将操作命令传达给docker serverrest api是http协议的一种规范,由于使用http协议,因此docker的兼容性和可拓展性都很好
    3. docker server:也称为docker demon守护进程,所有docker服务都是通过docker daemon完成,如容器的获取,镜像的创建等
  • 可用资源:只物理机/虚拟机的内存,cpu,磁盘等硬件资源

docker的特点

  • 开源的应用容器引擎,基于go语言开发

  • 容器完全使用沙箱机制,容器开销极低,反向提升系统利用率

  • Docker具备虚拟化职能,可以创建容器,还可向本地的物理机申请资源,更加方便程序的迁移与扩展。

  • 基于cs架构,拥有服务端和客户端的架构

    image-20220119135927945

    cs架构的优势:服务器和客户端不用安装一台电脑上,可以使运维工程师用一台电脑对很多台服务器进行管理

  • Docker提供标准的应用打包的方式,集装箱是镜像文件,镜像文件中包含了所有的资源,如web引用,数据库,web应用服务器,队列等,在这个镜像中还用来描述这些应用所需要的硬件环境要求,部署后也可灵活的调整,运维工程师得到镜像文件后,方便的进行扩展与还原,马上能得到与之前一样的运行环境。这也是docker存在的意义。

镜像和容器的区别

  • 镜像:镜像是只读的文件,提供了运行程序完整的软硬件资源,是应用程序的"集装箱"

  • 容器:是镜像的实例,由Docker负责创建,如windows光盘与实际安装的PC机,又或者java之中的Class类和new出来的对象的关系,容器直接相互隔离,互不影响

docker工作流程

工作流程如下图:image-20220119140536161

  1. client通过rest api发送命令docker pulldocker host
  2. docker host中的docker daemon接收并处理命令
  3. docker daemon查看本地仓库images中是否有镜像,如果没有镜像就到registry远程仓库中拉取镜像到本地仓库
  4. 发送docker run命令到server端,docker daemon通过镜像创建容器运行

Docker的安装

docker安装官方文档

由于本地安装的虚拟机是ununtu,所以下面是安装步骤是在ubuntu上安装的,其他途径的安装,官方文档上都有很详细的步骤。本次安装也是按照官网步骤来的。推荐使用centos系统,因为centos是使用最广泛的服务器系统

# centos yum源加速
# yum install -y yum-utils device-mapper-persistent-data lvm2
-y 自动确认安装
yum-utils是yum安装的工具包,简化安装过程中设置安装源的配置过程
安装数据存储驱动包,docker内部存储通过这2个组件完成数据存储

# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
修改yum的安装源,设置新的安装源,路径为国内阿里云的安装源
设置新的安装源目的:docker安装源默认是在国外,下载的非常慢

# yum makecache fast
让安装源自动检测速度是最快的,优先使用它

卸载旧的版本

如果本地没有安装过docker,可以跳过此步骤

$ sudo apt-get remove docker docker-engine docker.io containerd runc

设置存储库

更新apt软件包索引

$ sudo apt-get update
$ sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

添加docker官方gpg密钥

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

设置稳定存储库

 echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

安装docker引擎

$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
  • 关于docker-cedocker-ce代表是社区版免费,docker-ee是企业版收费

运行hello-world验证docker引擎是否安装正确

$ sudo docker run hello-world

image-20220120133931633

启动成功

image-20220120134755901

  • client:代表客户端的版本
  • server:代表服务端的版本

阿里云加速

docker默认会从国外的docker中央仓库下载镜像。但是由于国内网络的特殊性,从国外下载非常慢。可以通过配置从阿里云的容器镜像下载docker镜像。

登陆阿里云官网,搜索容器镜像服务image-20220120141444347

点进进入,并进入管理控制台界面

image-20220120141710684

按照下面的操作文档去配置加速。

Docker命令

docker镜像版本查询

hub.docker.com网站中搜索查看是否拥有对应的镜像。否则会拉取不到镜像

  • 不建议使用第三方镜像。也许存在bug,恶意代码等。

  • 建议使用官方镜像,官方镜像拥有Official Image标识

    image-20220120145003827

    点进去后如下图:

    image-20220120150316272

    tags就是i可用的版本,可以直接通过右边复制命令拉取镜像。

docker常用命令

  1. docker pull image:tags:拉取中央仓库的镜像并下载。若存在多个版本,可以通过:tags指定版本,不指定将下载最新版本

    image-20220120151120393

  2. docker images:查看所有的镜像

    image-20220120152529469

  3. docker rmi [options] image:tag:移除镜像

    image-20220120152741466

    options可选项:

    • -f:对正在运行的容器镜像,强制删除(开发阶段可以这么干,线上不建议)

      删除时不带-f,若出现如下错误
      Error response from daemon: conflict: unable to delete 938b57d64674 (cannot be forced) - image is being used by running container dd02e9d733cb
      则说明当前镜像生成的容器正在使用中,需要先删除容器,再删除当前镜像
      
  4. docker build [options] PATH | URL | -:通过Dockerfile创建镜像。

    image-20220122163332204

    Options:

    • -f :指定要使用的Dockerfile路径;
  • -m :设置内存最大值;

    • -q :安静模式,成功后只输出镜像 ID;
  • --rm :设置镜像成功后删除中间容器;

    • --tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。

    PATH:上下文路径。是指 docker 在构建镜像,有时候想要使用到本机的文件(比如复制),docker build 命令得知这个路径后,会将路径下的所有内容打包。

    解析:由于 docker 的运行模式是 C/S。我们本机是 C,docker 引擎是 S。实际的构建过程是在 docker 引擎下完成的,所以这个时候无法用到我们本机的文件。这就需要把我们本机的指定目录下的文件一起打包提供给 docker 引擎使用。

    如果未说明最后一个参数,那么默认上下文路径就是 Dockerfile 所在的位置。

    注意:上下文路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

  1. docker run [options] image [command] [arg...]:通过镜像创建并运行容器。是docker createdocker start命令的结合。

    image-20220120153923270

    可以看到创建并运行了一个容器,但是run命令是前台运行的,页面会进入阻塞状态,如果ctrl+c退出tomcat,那么tomcat会停止运行。

    image-20220120154634150

    通过添加-d命令后台创建并运行容器。

    options:常用选项

    • -d:容器后台运行

    • --name:为容器指定名字。如果名字已经存在的话,那么容器会创建失败。

    • -p:宿主机和docker容器之间的端口映射,例-p 8000:8080,我们就可以通过宿主机的8000端口访问容器8080端口

      image-20220120163211424

      端口是访问成功的,建立了连接。拿不到页面报了404。

    • -v:路径映射,将本地文件挂载到容器的某个目录。-v 本地路径:容器内部路径

    • -e:通过key=value设置环境变量。也可以通过--env-file传递环境变量路径

    • --ip:声明ip地址

    • --rm:在容器结束时自动退出容器,另一种方式是手动停止然后删除它。

    • -t:在容器内分配终端会话,可以与容器进行交互。通常与-i一起使用,即使是后台运行也会是交互界面保持打开状态。

    • -a:登陆容器,必须是通过-d方式创建的容器。

  2. docker ps:列出当前运行的容器及相关信息

    image-20220120170603449

    -a: 列出所有容器的相关信息(包括未运行的)

    image-20220120170652743

  3. docker stop [options] container [contaniner..]:通过contanier id/container name停止一个或多个容器。容器id可以通过docker ps -a查询。

    image-20220120172201977

    options:

    • -t:关闭容器的限时,如果超时未能关闭则用kill强制关闭,默认值10s,这个时间用于容器的自己保存状态
  4. docker start [options] container [container...]:通过contanier id/container name启动一个或多个容器。

    image-20220121134004706

    options:

    • -a:将程序的输出带到终端上
    • -i:接收终端的输入,和run命令参数一样
  5. docker restart [options] container [container...]:重启容器

    image-20220122151906459

    options:

    • -t:与stop命令参数一样。关闭容器的限时
  6. docker logs [options] container:查看容器的日志

    image-20220122152314440

    options:

    • -f:跟踪日志,实时刷新日志
    • -n:显示最后多少条。-f, -n参数与tail命令参数意义相同
    • -t:显示时间戳
  7. docker exec [options] container command [arg...]`:在已经运行的docker容器中执行命令

    参数和docker run命令参数意义相同。可以通过docker exec --help查看支持的参数。常用此命令进入容器内部:image-20220122154147387

  8. 更多docker命令参见菜鸟教程docker官网

Docker容器内部结构

image-20220125144129099

通过tomcat容器内部的结构来了解docker容器内部结构。容器内部主要包含三层:linux操作系统、app运行环境、app服务

  • linux操作系统:在前面介绍docker时曾说过,docker是为了将操作系统层给抽离出来,保证容器足够小。但是这里又说容器内包含操作系统?这不是前后矛盾么?

    docker是为了抽出操作系统,但是同时docker又拥有一定的虚拟化职能。正是因为docker是介于容器化和虚拟化之间,所以绝大部份容器都会带一个操作系统。但是这个操作系统是一个迷你的操作系统。它所占用的资源非常少,拥有的功能也非常有限。但是对于容器的运行来说,对于资源分配、资源控制等这些操作系统底层的职能,它都可以实现,因此安装一个操作系统是利大于弊的。

  • app运行环境:容器内的类库

进入容器内部:

image-20220125150129136

docker默认会将相关文件存储在/var/lib/docker目录下:

image-20220125151758093

镜像存储在image目录下,容器存放在containers目录下。

Dockerfile构建镜像

Dockerfile官网介绍

什么是Dockerfile

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

注意:标准的Dockerfile文件名称就是Dockerfile,不要添加任何后缀,首字母注意大写

使用Dockerfile定制镜像

写Dockerfile

ubuntu@node01:~$ ls
ubuntu@node01:~$ mkdir dockerfile
ubuntu@node01:~$ cd dockerfile
ubuntu@node01:~/dockerfile$ touch Dockerfile

Dockerfile写入内容:

FROM nginx
RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html

通过Dockerfile构建镜像

通过docker build命令构建镜像,-t指定镜像的tagimage-20220122163332204

构建成功后可以通过docker images查看构建后的镜像

image-20220122163442674

运行自定义构建的镜像

image-20220122163759511

Dockerfile指令详解

  • FORM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续的操作都是基于 nginx。

  • RUN:用于执行后面跟着的命令行命令。有以下两种格式:

    1. shellRUN ./test.sh arg1 arg2 ...
    2. execRUN ["./test.sh", "arg1", "arg2", ...]

    两种方式等价

    注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:

    FROM centos
    RUN yum -y install wget
    RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
    RUN tar -xvf redis.tar.gz
    

    以上执行会创建 3 层镜像。可简化为以下格式:

    FROM centos
    RUN yum -y install wget \
      && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
      && tar -xvf redis.tar.gz
    

    如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。

  • COPY:复制指令,从上下文目录中复制文件或者目录到容器里指定路径。COPY <源文件路径...> <目标路径>

  • ADDADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

    • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
    • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。
  • CMD:类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

    • CMD 在docker run 时运行。
    • RUN 是在 docker build。

    作用为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

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

    格式:

    CMD <shell 命令> 
    CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
    CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
    

    推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。

  • ENTRYPOINT:类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

    但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

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

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

    格式:

    ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
    

    可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参

  • ENV:设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

    格式:

    ENV <key> <value>
    ENV <key1>=<value1> <key2>=<value2>...
    

    以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:

    ENV NODE_VERSION 7.2.0
    RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
      && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
    
  • ARG:构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。

    构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。

    格式:

    ARG <参数名>[=<默认值>]
    
  • VOLUME:定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

    作用:

    • 避免重要的数据,因容器重启而丢失,这是非常致命的。
    • 避免容器不断变大。

    格式:

    VOLUME ["<路径1>", "<路径2>"...]
    VOLUME <路径>
    

    在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

  • EXPOSE:仅仅只是声明端口。

    作用:

    • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
    • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

    格式:

    EXPOSE <端口1> [<端口2>...]
    
  • WORKDIR:指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。

    docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

    格式:

    WORKDIR <工作目录路径>
    
  • USER:用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

    格式:

    USER <用户名>[:<用户组>]
    
  • HEALTHCHECK:用于指定某个程序或者指令来监控 docker 容器服务的运行状态。

    格式:

    HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
    HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
    
    HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
    
  • ONBUILD:用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

    格式:

    ONBUILD <其它指令>
    
  • LABEL: 用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:

    LABEL <key>=<value> <key>=<value> <key>=<value> ...
    

    比如我们可以添加镜像的作者:

    LABEL org.opencontainers.image.authors="runoob"
    

Dockerfile两大特性

  • 分层:

    Dockerfile每一个指令都是一层,分层进行堆叠。下面的层会在上面层的基础上执行。执行顺序:

    graph LR A[FROM]-->B[WORKDIR]-->C[ADD]-->D[ENV]-->E[RUN]

    可以把from理解为汉堡最下面的面包片,在这个面包片上添加对应的蔬菜,肉,直到add把这些覆盖上,形成一个完整的汉堡。

  • 系统快照:

    在分层的基础上,Dockerfile执行每一层命令时,都会生成临时容器(系统快照)。

    临时容器可重用,极大加快了镜像的构建速度以及节省系统资源,比如在部署一个新镜像时,前3步和之前tomcat镜像build都一样,所以只需将第四步add进行重新的临时容器构建

    对于构建过的镜像,修改Dockerfile后再次构建,只会对修改的部分进行执行,已经执行过的步骤并未修改的话,直接使用缓存的临时容器。

Docker容器间通信

容器在创建时,docker会为每一个容器分配ip地址。容器间通过ip地址是可以互相访问的。创建两个容器:image-20220125155943478

查看容器分配的ip地址:docker inspect NAME|IDimage-20220125160319047

通过上面命令可以看到tomcat容器的ip地址为:172.17.0.2,nginx的ip地址为:172.17.0.3

image-20220125162543358

但是在实际使用中,服务间并不会通过ip地址去访问,而是通过配置host,通过host映射ip去访问。

image-20220125162621957

可以看到,容器并不知道tomcat是什么,那么容器内如何实现host配置的效果?

创建容器时通过--link参数,关联容器名:

root@node01:/var/lib/docker# docker run -d --name nginx2 -p 8003:80 --link tomcat nginx

image-20220125172436951

容器间双向通信bridge

通过--link只能单向的配置一个容器访问另一个容器,如果想要双方都能互通需要两个容器互相--link。这配置很不方便,而且实际使用中不可能只有两台容器间互相访问,如果容器数量大的话,这种配置方式也是反人类操作。因此还有一种更优雅的方式配置多容器间通信——bridge

何为网桥

image-20220125173521838

见上图,docker是部署在宿主机上的,docker内容器自己会组成一个局域网,拥有自己的ip,可以将docker带入虚拟机理解。如果宿主机想要访问docker内的容器,那么首先宿主机应该是与宿主机的网络是互通的。宿主机会有一个虚拟网卡,ip地址为容器内局域网的ip地址:

image-20220125193623422

这个虚拟网卡就是容器内部网络的网关。当容器内服务想要访问外部网络,例如:百度时,首先会跳转到网关,然后由网关转发到物理网卡,在有物理网卡出去访问外部网络。这里的网关就是网桥

如何配置网桥

通过docker network ls可以查看当前docker创建的网桥。image-20220125173346510

上面NAMEbridge的就是一个网桥,网桥信息如下。

image-20220125195956412

可以通过docker network create -d bridge <bridge-name>创建一个网桥:image-20220125201219142

查看网桥信息:image-20220125202226303

查看宿主机ip信息:image-20220125202324417

可以看到多了一块虚拟网卡,ip与网桥配置ip相同。

容器与网桥的绑定

网桥配置好后,容器就需要与网桥绑定了,有两种绑定方式:

  • 创建容器时绑定:docker run -d --name <容器名> --network <网桥名> [...其他参数] image:tags command

    image-20220125202823888

  • 通过connnect命令绑定:docker network connect <网桥名> <容器名>

    image-20220125203111845

网桥工作原理

其实前面介绍网桥时就已经大概介绍了工作原理了。不熟悉的话可以看一下之前写的另一篇文章集群负载均衡,在网络部分详细讲了下一跳的机制。

而网桥的工作原理与此类似。所有的容器都与网桥绑定,因此网桥知道每一个容器的ip地址。所有容器想要访问其他容器时,先将请求打到网桥这里,由网桥将请求转发到目标容器。

容器间数据共享

通过在宿主机开辟一块空间,所有容器共享这块空间。可以通过volume设置。两种方式:

  1. docker run -v <宿主机路径:容器内挂载路径> ...:创建容器时指定挂载路径。
  2. docker run --volumes-from <容器名> ....:从已创建的挂载容器获取挂载信息并创建新的容器。相比于第一种方式,只需要指定一次挂载路径,其他容器都从第一个容器复制信息。当容器数量较大时/路径较复杂时避免重复写路径出现写错路径的风险。
posted @ 2022-01-25 21:06  Zs夏至  阅读(47)  评论(0编辑  收藏  举报