Docker 入门

1. 什么是 Docker ?

  • Go 语言实现
  • 基于 Linux 内核 CGroup, namespace, UFS 等技术:
    1. CGroup : Control Groups 的缩写,控制 CPU,内存,IO等资源限制。
    2. namespace : 资源隔离,通过namespace,让一个进程是一个独立的盒子,拥有自己的 PID,网络,hostname等等,不能访问别的 namespace 的资源。
    3. UFS: 镜像和容器的管理,Docker 目前支持的联合文件系统包括 OverlayFS, AUFS, Btrfs, VFS, ZFS 和 Device Mapper,默认使用 overlay2
  • 操作系统层面的虚拟化技术。
  • 与传统的虚拟化技术对比:
    docker-virtual-compare
  • Open VZ 类似,但是虚拟出来的是 VPS,利用底层的内核,虚拟出一整套的操作系统。

2. 为什么要使用Docker,有哪些优缺点?

  • 优点:
    1. 不需要搭建各种环境,应用的依赖打包到镜像内部,所以不需要重复安装各种底层依赖工具。需要一个 nodejs,直接使用现成的 nodejs 镜像拉取到本地。比如 某些代码,依赖了指定的 Linux 版本,在 Mac 本地开发调试就很麻烦,这样可以用 Docker 基于 Centos 的镜像来运行代码。
    2. 隔离性带来的好处,每个进程相当于一个系统,比如同一个 jar 要运行多个进程,会造成端口冲突等等。
    3. 迁移方便,只需要在目的 Host 安装 Docker 即可。
    4. 更高效的利用系统资源: 不需要虚拟硬件,也不需要允许整套完整操作系统。
    5. 启动时间等等。
  • 缺点:
    1. 需要更新的 Linux 内核:Version 3.10 or higher of the Linux kernel. CentOS 6 需要更新内核。
    2. 基于 Linux 内核实现,在 Windows 和 MacOS 上的兼容是通过实现一个 Linux 虚拟机,然后在虚拟机里面安装 Docker 来实现,不能原生支持。

3. Docker 的基本概念

  • 镜像(Image)

    1. Docker 镜像是一个特殊的文件系统,简单的理解就是包含了,当前应用需要运行所依赖的所有文件。
    2. 多层结构: 依赖于 Linux 的 UFS 存储驱动,采用分层结构最好的好处就是——资源共享和存储空间优化。一般在构建镜像的时候,都会有一个基本镜像,这样,这个基本镜像就只用存储一份。同时,构建的时候,会一层层构建,前一层是后一层的基础,后一层会指向前一层。这样,镜像的复用和定制变得就更简单。
    3. 两个属性:名称 和 tag.
      参考:10张图带你深入理解Docker容器和镜像
  • 容器(Container):

    1. 容器 和 镜像 的关系就像 实例 和 类的关系一样。容器是根据镜像 + 读写层(容器存储层) 来创建的一个运行时的实体。
    2. 容器的本质是一个进程(相对于宿主机),进程拥有自己独立的 namespace,简单的看,就可以看做一个 独立的 Linux server,容器和宿主机相互之间是隔离的。
      • 容器中可以有多个线程,比如启动一个 Nginx 容器,Nginx 的 worker_processes 定义为 8,那么在宿主机中,只能看到一个 Nginx 的 docker 进程。但是在容器中,nginx 有 8 个进程。
        在Host中:
        nginx-process-host
        在容器中:
        nginx-process-in-context
    3. 容器的状态:创建,启动,停止,删除,暂停。
    4. 容器的内核是使用的宿主机的内核,所以,这个也是镜像比较小的原因。
    5. 结构:一个镜像 + 读写层(容器存储层) = 容器。
    6. 容器存储层的生命周期跟容器的生命周期一样,容器删除了,存储层的数据也就丢失了,停止容器,但是没有删除,就不会丢失。所以,容器不应该像存储层写入任何数据,容器要保证无状态化,如果需要持久化数据,可以挂载本地的目录到容器。
  • 仓库(Repository)

    1. 一个镜像是一个仓库,一个镜像有多个 tag 。所以,一个仓库的意思是指 一个镜像的多个版本的集合。比如 ubuntu 是仓库的名字,有多个不同的版本,18.04,16.04 这个是标签。
    2. Docker Registry : 包含多个仓库 Repository的集合。大多时候,仓库的名字经常以两段路径出现,比如 jwilder/nginx-proxy:1.0,前面往往表示在这个 Registry 中这个仓库所属的用户名,第二段表示仓库名,冒号后面表示版本。
    3. Docker 官方的 Registry: Docker Hub
    4. 可以使用官方提供的 docker-registry 镜像搭建自己的私有 Registry,但是官方提供的只有服务端 API 实现,没有图形界面,用户管理,访问控制等功能,商业版本提供了高级功能。
    5. 除了官方提供的 docker-registry 镜像,还有第三方软件实现了 Docker Registry API,提供了用户界面。比如,Harbor 和 Sonatype Nexus.
    6. 跟 Maven 区别。

4. 如何 安装 Docker ?

参考链接:https://yuanmomo.net/2019/08/05/docker-install/

5. Docker 镜像

  • 拉取镜像

    1. 配置镜像加速器

    2. docker pull xxx:xxx 拉取指定镜像的指定版本,如果不指定版本,那就用最新的 latest (不建议)

    3. 默认从 Docker 官方的 Docker hub 拉取镜像。

    4. 拉取镜像的时候,其实拉取的是很多层,一层层的结构。

      1
      2
      3
      4
      5
      6
      7
      8
      » docker pull ubuntu:18.04
      18.04: Pulling from library/ubuntu
      5b7339215d1d: Pull complete
      14ca88e9f672: Pull complete
      a31c3b1caad4: Pull complete
      b054a26005b7: Pull complete
      Digest: sha256:9b1702dcfe32c873a770a32cfd306dd7fc1c4fd134adfb783db68defc8894b3c
      Status: Downloaded newer image for ubuntu:18.04
  • 查看镜像
    1. docker images -a
    2. 虚悬镜像, 为不同的镜像打相同的tag,会导致旧的镜像没有tag而变成虚悬镜像。
    3. 中间镜像,docker build 产生的中间镜像,因为有上层镜像的依赖,所以不能删除。
  • 删除本地镜像
    1. docker rmi xxx:xxx 注意镜像名和版本号
    2. Untagged 和 Deleted 的区别 : 镜像的唯一标识是 id 和 摘要。可能一个镜像有多个名称和标签,所以在删除的时候,优先删除标签。当一个镜像没有标签指向的时候,才会触发 delete 行为。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      [[email protected] #] docker images -a
      REPOSITORY TAG IMAGE ID CREATED SIZE
      yuanmomo/nodejs-hexo 1.0 57f58e397d26 8 days ago 107MB

      [[email protected] #] docker tag yuanmomo/nodejs-hexo:1.0 yuanmomo/nodejs-hexo:3.0
      [[email protected] #] docker tag yuanmomo/nodejs-hexo:1.0 hexo-local:2.0

      [[email protected] #] docker images -a
      REPOSITORY TAG IMAGE ID CREATED SIZE
      hexo-local 2.0 57f58e397d26 8 days ago 107MB
      yuanmomo/nodejs-hexo 1.0 57f58e397d26 8 days ago 107MB
      yuanmomo/nodejs-hexo 3.0 57f58e397d26 8 days ago 107MB

      [[email protected] #] docker rmi yuanmomo/nodejs-hexo:3.0 hexo-local:2.0
      Untagged: yuanmomo/nodejs-hexo:3.0
      Untagged: hexo-local:2.0
  • docker commit 制作镜像(慎用)

    1. 使用 docker run 运行一个镜像, 启动一个容器,
    2. 在容器中增加某些包,依赖,修改某些文件,比如 某某配置文件
    3. 然后用 docker diff 可以查看文件变动的记录
    4. 使用 docker commit 可以帮当前运行的容器打包成一个镜像。
    5. docker commit 优点 :
      • 被入侵后, 保存入侵现成 这种需要保存容器所有内容的特殊场合
    6. docker commit 缺点 :
      • 会将很多无用的文件修改保存下来,导致有很多无关的内容被打包,导致镜像极其臃肿。
      • 黑箱镜像,只有制作人知道镜像的内容,其他人都不知道,就算制作人,过段时间,也可能会忘记。黑箱镜像的维护成本非常痛苦。
  • Dockerfile 定制镜像

    1. 编写 Dockerfile,第一行为 FROM,表示以某个镜像为基础镜像来定制镜像。(scratch镜像)
    2. RUN 执行一个命令,比如安装某个包: apt, yum;修改某个文件:sed
    3. ENTRYPOINT 和 CMD 的比较:
      • ENTRYPOINT 和 CMD 都是让容器启动的时候,运行一个指令。
      • docker run -it image_name CMD 命令行中的 CMD 会替换 Dockerfile 中的 CMD 命令的内容。
      • ENTRYPOINT 是将 docker run -it image_name CMD 命令行中的 CMD 作为一个参数传递给 ENTRYPOINT. 这样可以在运行镜像的时候,动态的增加一些参数。
      • 因为命令行 中的 CMD 会作为 ENTRYPOINT 的一个参数,这样的话,可以在运行 CMD之前执行一些操作,然后再执行 CMD。比如给 redis 运行的时候切换账号
        redis-Dockerfile-ENTRYPOINT
    4. Volumn 匿名卷的作用
      • 在 Dockerfile 中定义一个挂在目录,存储数据,防止用户没有使用 -v 或者 –mount 挂在目录,而将数据写到容器的读写层,当容器销毁后数据丢失。
    5. 使用 docker build 根据文件制作镜像
    6. 需要注意的是,在 docker build 打包镜像的时候,遇到一个 RUN 命令,就会提交一层镜像,所以尽量合并 RUN,COPY,ADD 命令,UFS是有最大层数限制的,128层,层数越多,性能越低。同时在安装包后,删除下载包,缓存,中间的编译文件等等,尽量缩减包的大小。
    7. docker build 在编译的时候,在构建新的一层的时候,会判断是否有缓存,如果有缓存,则会直接优先使用缓存中的层,节约空间和编译时间:
    8. docker build 缓存的规则:
      • 禁止使用 cache特性,build 时增加参数 –no-cache=true
      • 如果用一个已经存在的镜像做为父镜像,那么在执行下一句的时候,会对该镜像的所有子镜像做检查,如果找到有一个子镜像是用相同的语句构建,那么使用cache.
      • 大多数情况下,简单的比较构建语句是否相同即可,但是排除后面的特殊情况。
      • 对于使用 ADD 和 COPY 指令,会检查当前文件,或者文件夹下面的所有文件是否有修改(最后修改时间和最后访问时间除外),不管是任何文件的新增,内容的修改,都会认为缓存失效,并且重新编译这层镜像。
      • 除了 ADD 和 COPY 指令外,其它的指令不会去对文件做校验,比如 apt-get -y update这样的命令,是不会去检查所有文件,这个时候,这个命令字符串将作为关键字查找镜像。
      • 缓存一旦失效,那么这个 Dockerfile 中后面所有的层, 将全部重新编译。所以说,在编写Dockerfile 时,有一个注意的就是,不经常变动的文件或者命令,尽量写在前面,以此减少在缓存失效后,需要重新编译的镜像层数。
    9. 构建上下文(重要)
      • docker build 有一个重要的参数 “.” 或者某个目录。
      • Docker 运行分为服务端和客户端(C/S)。Docker 服务端是一个服务端守护进程,/usr/bin/dockerd,并提供了一组 REST API,Docker Remote API。
      • 客户端使用 docker 命令,通过调用 REST API 接口和 docker 引擎通信。
      • docker build 在构建的时候,指定的路径,比如”.”,叫做上下文路径。docker build 执行的时候,会把指定的这个目录打包发送到 docker 引擎。docker 引擎收到这个上下文的包的时候,会展开这个包,就获得了构建所需要的所有文件。
      • Dockerfile 中的 ADD, COPY 进行文件操作的时候,源路径都是相对路径。
      • .dockerignore 配置不发送到 docker 引擎的文件列表,类似 .gitignore
    10. Dockerfile 多阶构建
      • 场景:有一个 C++ 的项目,源码和编译后的so库文件。
      • 旧的方式,编写两个 Dockerfile 文件 和一个 shell 脚本。第一个 Dockerfile 包含代码,安装依赖库,编译测试打包。第二个 Dockerfile 负责安装运行环境,就不需要源码,编译中的依赖,比如 cmake,pb 等等。shell 脚本负责调用 docker build 来整合两个 Dockerfile.
      • 新的方式,编写一个 Dockerfile,使用 FROM xxx:xxx as phase,来制定阶段,后面的phase 可以通过 COPY –from 来使用前面 phase 产生的文件。使用 docker builder –target来编译指定阶段的镜像。
  • 其他的镜像制作方式

    1. docker build 使用 git repo
    2. docker build 使用 tar 包
    3. docker build 从标准输入读取 Dockerfile 进行构建
    4. docker build 从标准输入中读取上下文压缩包进行构建
    5. 从 rootfs 压缩包导入

6. Docker 容器的操作

  • 容器有几种状态:created|restarting|running|removing|paused|exited|dead
  • 常用的状态: running, paused, exited.
  • 启动容器:
    • 创建容器,并运行 : docker run
      1. 检查本地仓库是否有镜像,没有镜像,从远程仓库去拉取镜像。
      2. 利用镜像创建一个容器。
      3. 分配一个文件系统,在容器的最上层,挂在一个读写层。
      4. 配置虚拟网卡。
      5. 分配ip地址。
      6. 用户指令。
      7. 运行完成后退出容器。
    • 重新启动停止的容器 : docker start
  • 后台运行: docker run -it: -i 交互模式;-t: 分配一个终端)。没有 -t ,只有 -i的时候,看不到 Linux 的命令提示符。
    • docker 运行的命令,比如 nginx ,就不能是 service nginx start/restart. 而应该是 ./nginx.
    • 因为 service nginx start 这个命令运行完成了过后,命令结束,那么容器就会退出,如果使用 ./nginx,则会一直卡在运行 nginx,容器就不会退出。
    • 如果有得时候,运行的时候,需要强制容器不退出,那么可以在命令后面添加一个 top命令,或者while,echo 命令,强制 CMD 运行不能完成。
    • -d 参数,这个参数时用来控制 docker 运行方式,前台还是后台,直观的就是日志是到宿主机还是在后台。宿主机的日志可以直接看到;后台的日志,需要使用 docker logs container-name 查看。需要注意的是,容器是否能长久运行(能不能保持运行的状态),不是取决于有没有使用 -d 参数,而是 docker run 后面的指令。
  • 终止: docker stop
  • 进入容器:
    • docker attach,如果从 attach 中 exit,会导致容器exit停止,所以不建议使用。
    • docker exec -it xxx /bin/bash (镜像需要安装 bash,如果没有安装,则进入不了)
  • 删除 docker rm [-f] ,不加 -f, 如果容器在 running 状态则不会删除,需要先 stop. -f 就可以直接讲running 状态的容器删除。
  • 注意 docker run 容器的用户权限,像redis,需要用 redis 的用户运行,部分用户可能需要用 root 账号运行(比如需要 gdb 调试等等)。

7. Docker 仓库

9. Docker 数据操作 https://docs.docker.com/storage/

  • Volumes(挂载卷)
    -
  • Bind mounts(挂载点)
    -
  • tmpfs mount(仅限 Linux)
    • 10. Docker 三剑客 Compose, Swarm, Machine

      11. 容器编排工具 k8s(Kubernetes) VS Docker Swarm

      12. Docker 运维和监控

Just for my love !!