「Tools」:Docker

本篇文章主要分四个部分,首先介绍了Docker是什么:为什么会有Docker技术的出现;虚拟化技术和容器虚拟化技术的区别;Docker的基本组成;Docker的运行为什么会比虚拟机快。

第二个部分主要介绍了Docker的常用命令,包括镜像命令和容器命令,文中还从底层的角度分析Docker镜像。

第三个部分介绍了Docker中的容器数据卷,和如何挂载数据卷。

最后一个部分,简单介绍了Dockerfile文件。

Docker简介

Docker 是什么

开发和运维之间的环境和配置问题:在我的机器上可以正常工作。

把代码/配置/系统/数据等全部打包成镜像,运维工程师带环境安装软件。

Docker基于Go语言实现的云开源项目,Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,做到一次封装,处处运行。

Linux 容器技术的出现就解决了这样一个问题,而 Docker 就是在它的基础上发展过来的。将应用运行在 Docker 容器上面,而 Docker 容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。

Docker解决了运行环境和配置问题的软件容器,方便做持续集成并有助于整体分布的容器虚拟化技术。

能干嘛?

之前的虚拟化技术

虚拟机是带环境安装的解决方案,可以在一种操作系统中运行另一种操作系统。

虚拟机用软件实现了硬件、内核、操作系统及应用程序,对底层来说,虚拟机就是一个普通文件。

虚拟机的缺点缺点:

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

容器虚拟化技术

Linux容器(Linux Containers,LXC),对进程隔离,将软件运行所需的资源打包到一个隔离的痛其中。

UzVUbV.png

Linux容器不是模拟一个完整的操作系统,而是将软件工作所需的库资源和设置等资源打包到一个隔离的容器中,因此Linux容器变得高效且轻量,并且能保证部署在任何环境中的软件都能始终如一地运行。在

宿主机上,Linux容器就是一个运行的进程,所以Linux容器是对进程进行隔离。

再看Docker的图标,上面的集装箱就是一个一个容器,鲸鱼就是宿主机的硬件、内核。

比较:

  1. 传统虚拟机技术虚拟一套硬件,在其上运行一个完整的操作系统,再运行所需的应用进程。
  2. 容器内的应用直接运行于宿主的内核,容器内没有硬件虚拟,容器更轻便。
  3. 容器之间互相隔离,每个容器有自己的文件系统,容器之间进程不会相互影响。

所以,可以认为容器是一个轻量的Linux。

开发/运维(DevOps)

DevOps, Develop and Operations, 可以利用Docker实现开发自运维。

  1. 更快速的应用交付和部署。
  2. 更便捷的升级和扩缩容器。
  3. 更简单的系统运维。
  4. 更高效的计算资源利用。

Docker的基本组成

Docker的三要素:

  1. 镜像(image):只读的模版,类比Java中的类。镜像可以用来创造Docker容器。
  2. 容器(container):镜像的实例,独立运行的一个或一组实例。可以把容器看作一个简易版的Linux环境。
  3. 仓库(repository):保存镜像的场所。

Docker本身是一个容器运行载体或管理引擎。

把应用程序和配置打包成为一个可交付的运行环境,打包好的运行环境就是一个image镜像文件,只有通过这个镜像文件才能生成Docker容器。image文件可以看作是容器的模版。Docker根据image文件生成容器的实例。

Docker运行原理

Docker是一个C/S结构的系统。

Docker守护进程运行在宿主机上,客户通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。

为什么比虚拟机快

  1. Docker有比虚拟机更少的抽象层,不需要实现硬件资源虚拟化,运行在docker容器中的程序直接使用的都是实际物理机的硬件资源。
  2. Docker使用宿主机上的内核,新建容器时,不需要和虚拟机一样重新加载一个操作系统内核。因此新建一个dock er容器只需要几秒钟。

Docker镜像加速

可以登陆阿里云获得专属镜像加速器链接,配置本机Docker拉取镜像仓库的链接,将拉取镜像的链接从DockerHub换成阿里云的仓库,下载更快捷。

具体按照系统自行Google。

Docker常用命令

docker version

docker info

docker –help 帮助命令

镜像命令

  • 列出本地images

    docker images

    repo

    • 参数
      • -a :包括中间映像层
      • -q : 只显示镜像id
      • –digests :显示摘要信息
      • –no-trunc :显示完整信息
  • 从Docker Hub查询镜像名

    docker search [OPTIONS] image_name

    • –no-trunc
    • -s n:收藏数不小于n的镜像
    • –automated
  • 下载/拉取镜像

    docker pull 镜像名[:TAG]

    默认:latest

  • 删除镜像

    docker rmi 镜像唯一名字/镜像ID

    -f :强制删除运行中的镜像文件

    • 删除单个:

      docker rmi -f 镜像ID

    • 删除多个

      docker rmi -f 镜像名1:TAG 镜像名2:TAG

    • 删除全部:

      docker rmi -f $(docker images -qa)

容器命令

容器是一个建议的Linux。

  • 启动容器:

    docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

    • --name 容器名 :为容器指定一个名字
    • -d :后台运行容器,返回
    • -i : 以交互模式运行容器,通常与-t 一同使用
    • -t :为容器重新分配一个伪输入终端,通常与-i 一同使用。
    • -p :主机端口和容器端口
      • -p ip:hostPort:containerPort
      • -p ip::containerPort
      • -p hostPort:containerPort
      • -p containerPort
    • -P :随机分配端口
  • 列出当前运行所有容器:

    docker ps

    • -a : 列出当前所有正在运行的容器和历史上运行过的容器
    • -l :显示最近创建的容器
    • -n :显示最近创建的num个容器
      • docker ps -n 3
    • -q :静默模式,只显示容器编号
    • --no-trunc : 不截断输出
  • 退出/停止容器

    • 容器停止退出

      exit

    • 容器不停止退出

      Ctrl + P + Q

  • 启动容器

    docker start 容器名/容器ID

  • 重启容器

    docker restart 容器名/容器ID

    重启成功后返回容器名/容器ID

  • 停止容器

    docker stop 容器名/容器ID

  • 强制停止容器

    docker kill 容器名/容器ID

  • 删除已停止的容器

    docker rm 镜像ID

    • 一次删除多个容器

      docker rm -f $(docker ps -a -q)

      docker ps -a -q | xargs docker rm (管道传递参数)


  • 启动守护式容器

    docker run -d 镜像名/镜像ID

    docker run -d -p 主机端口:容器内端口 容器ID

    • 如果使用 docker ps -a 查看,会发现容器已经退出

    • Docker容器后台运行,就必须要有一个前台进程与之交互

      如果容器后台运行,如果不是一直挂起的命令,他就会自动退出。

    • 所以最佳的解决方式是将运行的进程以前台进程运行。

  • 查看容器日志

    docker logs -f -t --tail 容器ID

    • -t:显示加入时间戳
    • -f :持续显示最新的日志
    • --tail :显示最后多少条
  • 显示容器内运行的进程

    docker top 容器ID

  • 查看容器内部的细节

    docker inspect 容器ID

  • 进入正在运行的容器并以命令行与之交互

    • 直接进入容器启动命令的终端

      docker attach 容器ID

    • 在容器中打开新的终端,并且可以启动新的进程。

      docker exec -it 容器ID bashShell

      docker exec -it 容器ID /bin/bashdocker attach 容器ID 相同。

  • 把容器内文件拷贝文件到主机上

    docker cp 容器ID:容器内的路径 目录主机路径

    docker cp 130b1f6708dd:/x.txt /Users

Docker镜像

image:

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

UnionFS

UnionFS(联合文件系统)是一种分层、轻量高性能的文件系统,支持对文件系统的修改作为一次提交来一层层的叠加,同时将不同目录挂载到同一个虚拟文件系统下。

Union文件系统时Docker镜像的基础。

镜像通过分层来进行继承,基于基础镜像可以制作各种具体的应用镜像。

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

Docker镜像的加载

Docker镜像实际是由一层一层的文件系统组成。

bootfs(boot file system)包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统。

Docker镜像的最底层就是bootfs,这一层和典型的Linux/Unix系统是一样的,包含bootloader和kernel。

当boot加载完成后,整个kernel就在内存中了,此时内存的使用权已由bootfs转交给kernel,此时系统也会卸载bootfs。

rootfs(root file system),在bootfs之上,包含的就是典型Linux系统中的/dev, /proc, /bin, /etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Linux,Centos等。

平常安装等虚拟机的CentOS都是几个G,为什么docker版的centos只有几百兆?

对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库,因为底层直接使用宿主机的kernel,自己只需要提供rootfs就行了。

因此,对于不同的Linux发行版,bootfs基本一致,rootfs会有差别,因此不同的发行版可以共用bootfs。

分层的镜像

在docker image下载、删除时,可以发现是一层一层的。

分层的镜像的一个最大的好处是共享资源。

如果有多个镜像都是从相同的base镜像build而来,那宿主机中只需在磁盘上保存一份base镜像,同时内存中也只需要加载一份base镜像,就可以为所有的容器服务了。

镜像commit操作

Docker镜像都是只读的,但当镜像实例化,启动容器时,一个新的可写层被加载到镜像的顶部,这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。

docker commit提交容器层副本使之成为一个新的镜像。

docker commit -m "message" -a "author" 容器ID 命名空间/新建镜像名[:TAGS]

容器数据卷

Docker理念:

将代码和运行的环境打包形成容器,运行伴随着容器,但希望运行中的数据是持久化的,希望容器之间是共享数据的。

如果不通过docker commit生成新的镜像,使得数据作为镜像的一部分保存下来,那么容器删除后,数据也没有了,为了保存数据,使用容器数据卷。

如果不使用commit 生成新的镜像,Docker容器产生的数据将随着容器的删除而一起删除,为了保存数据,我们使用卷。

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

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

数据卷的特点:

  1. 数据卷可以在容器之间共享或重用数据。
  2. 卷中的更改直接在所有共享该卷容器中生效。
  3. 数据卷中的更改不会包含在镜像的更新中。
  4. 数据卷的生命周期一直持续到没有容器使用它为止。

数据卷挂载

直接命令添加

  1. 数据挂载(-v value)

    docker run -it -v /宿主机目录:/容器内目录 镜像名

  2. 查看挂载是否成功

    docker inspect 镜像名

  3. 宿主机和容器之间实现数据共享,在容器停止退出后,修改宿主机数据,数据完全同步。

  • 带权限的数据挂载,加:ro (readonly)

    docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名

    此时容器中对数据卷只读。


当挂载主机目录事,Docker访问出现cannot open directory .: Permission denied

解决办法:在挂砸目录后加参数 --privileged=true

DockerFile添加

在DockerFile中可以使用VOLUME 指令给镜像添加一个或多个数据卷。

注意:

Docker出于可移植性和分享的考虑,指令中只有容器内的地址,因为宿主主机目录依赖于特定的主机。

  1. Dockerfile文件构建

    1
    2
    3
    4
    FROM centos
    VOLUME ["/dataVolumeContainer1", "/dataVolumeContainer2", "/dataVolumeContainer3"]
    CMD echo "finished,-----success"
    CMD /bin/bash

    以上docker文件类似于一下命令挂载

    1
    docker run -it -v /host1:/dataVolumeContainer1 -v/host1:/dataVolumeContainer2 -v /host3:/dataVolumeContainer3 centos /bin/bash
  2. build构建镜像(-f file)

    docker build -f DockerFile文件路径 -t 命名空间/镜像名 镜像生成路径

    docker build -f ./Dockerfile -t fred/centos .

数据卷容器

数据容器卷:

命名的容器挂载数据卷,其他容器通过挂载这个父容器实现数据共享,挂载数据卷的容器称为数据卷容器。

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

  1. 挂载数据卷到父容器(命名为dc01 )上:命令添加/Dockerfile添加

  2. 容器继承父容器的数据卷(--volumes-from )

    docker run -it --name 子容器名 --volumes-from 父容器名 生成子容器的镜像名

    e.g: docker run -it --name dc02 --volumes-from dc01 fred/centos

dc01已经挂载数据卷,此时dc02继承它,那么dc01挂载的数据卷,dc02也实现了共享。

Dockerfile

Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本。

构建容器卷的步骤:

  1. 编写Dockerfile文件
  2. docker build构建
  3. docker run启动容器

Centos的Dockerfile文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM scratch
ADD centos-7.8.2003-x86_64-docker.tar.xz /

LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20200504" \
org.opencontainers.image.title="CentOS Base Image" \
org.opencontainers.image.vendor="CentOS" \
org.opencontainers.image.licenses="GPL-2.0-only" \
org.opencontainers.image.created="2020-05-04 00:00:00+01:00"

CMD ["/bin/bash"]

Dockerfile构建过程

基础规则:

  1. 保留字指令必须大写,且后面必须至少一个参数。
  2. 指令顺序执行。
  3. 注释符号:#
  4. 每条指令都会创建一个新的镜像层,并对该镜像进行提交。

执行流程:

  1. 从基础镜像运行一个容器
  2. 执行一条指令后并对容器进行修改
  3. 执行类似docker commit操作提交一个新的镜像层
  4. docker再基于刚提交的镜像运行一个容器
  5. 直到文件所有指令执行完成

辨析Dockerfile,Docker镜像,Docker容器:

Dockerfile、Docker镜像与Docker容器从软件应用的角度分别代表软件的三个不同阶段:

  1. Dockerfile是软件的原材料,是面向开发的。

    Dockerfile定义了进程需要的一切东西。Dockerfile设计的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程等等。

  2. Docker镜像是软件的交付品,是交付标准。

    在用Dockerfile定义一个文件之后,docker build会产生一个Docker镜像,运行 Docker镜像时,才真正开始提供服务。

  3. Docker容器则可以认为是软件的运行态,涉及部署和运维。

    Docker容器是直接提供服务的。

Dockfile体系结构

UzVdET.png
  • FROM

    基础镜像

  • MAINTAINER

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

  • RUN

    容器构建时需要运行的命令

  • EXPOSE

    当前容器对外暴露的端口号

  • WORKDIR

    指定在创建容器后,终端默认登陆进来的工作目录

  • ENV

    构建容器中的设置环境变量

  • ADD

    将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包

  • COPY

    拷贝文件和目录到镜像中

    COPY src dest

    COPY ["src", "dest"]

  • VOLUME

    容器数据卷 用于数据保存和持久化工作

  • CMD

    指定一个容器启动时运行的命令

    • shell 格式:CMD <命令>

    • exec格式:CMD[“可执行文件”, “arg1”, “arg2”,…]

    • 参数列表格式:CMD [“arg1”, “arg2”,…] 在指定来ENTRYPOINT指令后,用⌘指定具体的参数。

    只有最后一个CMD生效,CMD会被docker run之后的参数替换

  • ENTRYPOINT

    指定一个容器启动时运行的命令

    会在docker run后面追加参数

  • ONBUILD

    当构建一个被继承的Dockerfile时,父镜像在被子镜像继承后父镜像的ONBUILD被触发

Author

f7ed

Posted on

2020-07-21

Updated on

2020-07-25

Licensed under

CC BY-NC-SA 4.0


Comments