Loading...

给,你要的 Docker 详细教程

Docker 是一个用于开发、发布和运行应用程序的开放平台,它能够使您将引用程序与基础设施分离开来,以实现快速交付。使用 Docker,您可以使用与管理您的应用程序相同的方式管理您的基础设施。通过利用 Docker 快速发布、测试及部署代码的方法,您可以显著地减少编写代码和在生产环境中运行代码之间的延迟。

Docker 概述

Docker 平台

Docker 提供了在一个被称之为 容器 的松散隔离的环境中进行打包和运行程序的能力。这种隔离性和安全性使您能够在同一台给定的主机上同时运行多个容器。所有的容器都是轻量级的,并且包含了应用程序运行所需要的一切,因此您不需要再依赖于当前主机所安装的内容。

Docker 可以做什么

  1. 快速、一致的交付应用程序。

    Docker 适合持续集成和持续交付(CI/CD)工作流。

    DevOps能力环

    关于 CI/CDhttps://www.redhat.com/zh/topics/devops/what-is-ci-cd

    关于 DevOpshttps://baike.baidu.com/item/devops/2613029

  2. 响应式部署和扩展。

    Docker 基于容器的平台允许高度可移植的工作负载。Docker 容器可以在开发人员的本地笔记本电脑、数据中心的物理或虚拟机、云提供商或混合环境中运行。

    Docker 的可移植性和轻量级特性还使动态管理工作负载、根据业务需求几乎实时地扩展或拆除应用程序和服务变得容易。

  3. 在相同的硬件环境上运行更多工作负载。

    Docker 非常适合高密度环境以及需要以更少资源完成更多任务的中小型部署。

Docker 理念是将应用及依赖包打包到一个可移植的容器中,可发布到任意 Linux 发行版 Docker 引擎上。使用沙箱机制运行程序,程序之间相互隔离。

Docker 体系结构

Docker 使用 客户端 - 服务器 的架构模式, Docker 客户端与 Docker 守护进程进行对话,后者负责构建、运行和分发 Docker 容器的繁重工作。它们既可以在同一系统上运行,也可以远程建立连接。客户端和守护进程之间使用 REST API 、 UNIX 套接字或网络接口进行通信。 Docker 体系结构如下图所示:

Docker体系结构示意图

Docker 守护进程

Docker 守护进程( dockerd )侦听 Docker API 的请求并管理 Docker 对象,如镜像、容器、网络和卷。守护进程还可以与其他守护进程进行通信以管理 Docker 服务。

Docker 客户端

Docker 客户端 ( docker ) 是许多 Docker 用户与 Docker 进行交互的主要方式。当用户发起 docker run 等命令时,客户端会使用 Docker API 将这些命令发送到 dockerd 并执行。一个 Docker 客户端可以与多个 dockerd 进行通信。

Docker 注册表

Docker Hub 是 Docker 镜像的公共注册中心, Docker 默认配置是从 Docker Hub 上查找镜像,您也可以根据需要配置自己的私有注册表。

当使用 docker pulldocker run 命令时,您所需要的镜像将从配置的注册表中提取。当使用 dcoker push 命令时,您的镜像会被推送到您配置的注册表中。

Docker 注册表工作原理与 Git 相似。

Docker 对象
  1. 镜像(Image

    镜像是用于创建 Docker 容器指令的 只读模板 。通常,一个镜像会基于另一个镜像,例如,您可以构建一个基于 ubuntu 镜像的镜像,并安装 Apache Web 服务器和您的引用程序,以及您的应用程序所需要的相关环境和配置详细信息。

    在构建自己的镜像时,您需要使用简单的语法创建一个 Dockerfile ,用于定义创建和运行镜像所需要的步骤。Dockerfile 中的每条指令都会在镜像中创建一个层(layer)。当对镜像进行重建时,只会重建被更改的层,而非所有。这种虚拟化技术及其模式,使得镜像轻量而小巧且易于部署。

  2. 容器(Container

    容器是镜像的可运行实例。您可以使用 Docker API 或 CLI 进行创建、启动、停止、移动或删除容器。也可以将容器连接到一个或多个网络,以为其附加存储,甚至可以根据当前的状态创建新的镜像。

    默认情况下,一个容器与其他容器,及其自身所在的主机是相对隔离的。

    命令示例:docker run

    命令行提示符 command:("[root@xxx ~] $":2)
    # 使用docker运行一个ubuntu容器,以交互的方式添加到本地命令行会话,并运行 /bin/bash docker run -i -t ubuntu /bin/bash

    运行该命令时,将发生以下情况:

    1. 如果当前主机本地没有该镜像, Docker 会从配置的注册表中进行提取,这一步骤等同于 docker pull ubuntu

    2. Docker 会创建一个新的容器,这一步骤等同于 docker container create

    3. Docker 为容器分配一个读写文件系统,作为它的最后一层。这允许正在运行的容器在其本地文件系统中创建或修改文件和目录。

    4. Docker 创建一个网络接口来将容器连接到默认网络,其中包括为容器分配 IP 地址。默认情况下,容器可以使用主机的网络连接连接到外部网络。

    5. Docker 启动容器并执行 /bin/bash 。由于容器以交互方式运行并附加到您的终端(由于 -i-t 标志),您可以在输出记录到终端时使用键盘提供输入。

      docker 中必须保持至少一个进程运行,否则容器会自动退出。这里在启动时指定启动了 /bin/bash 进程。

    6. 当输入 exit 以终止 /bin/bash 命令时,容器会停止,但不会被移除。您可以重新启动或删除它。

  3. 仓库(Repository

    Docker 仓库与代码仓库类似,所不同的是它用于集中存放镜像。Docker 仓库存放于 Docker 注册表中,通常情况下,注册表中会有多个仓库,而仓库中则存放一类镜像,每个镜像利用 tag 进行区分,比如 Ubuntu 仓库中存放有多个版本的 Ubuntu 镜像。

底层技术支持

Docker 是使用 Golang 编写的,并利用 Liunx 内核的几个特征来提供其功能, Docker 使用一种被称为 namespaces 的技术来提供隔离的工作空间,这种工作空间即被称为容器。当运行一个容器时, Docker 会为该容器创建一组命名空间。

这些命名空间提供了一层隔离。容器的每个方面都在单独的命名空间中运行,并且其访问权限仅限于该命名空间。

Docker 安装

警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker 。

系统要求

Docker 支持 64 位版本 CentOS 7/8,并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。

查看 CentOS 版本: cat /etc/redhat-releasecat /etc/os-release

卸载旧版本

旧版本的 Docker 称为 docker 或者 docker-engine,如果安装了这些,请卸载它们以及相关的依赖项:

命令行提示符 command:("[root@xxx ~] $":1)
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine

使用 yum 安装

执行以下命令安装依赖包:

命令行提示符 command:("[root@xxx ~] $":1)
sudo yum install -y yum-utils

执行以下命令设置 yum 软件源:

命令行提示符 command:("[root@xxx ~] $":2)
# 阿里镜像源(国内推荐) sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # Docker 官方镜像源 # sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 测试版本 Docker # sudo yum-config-manager --enable docker-ce-test

安装 Docker

更新 yum 软件源缓存,并安装 docker-ce

命令行提示符 command:("[root@xxx ~] $":2,4)
# 更新 yum 软件包索引 yum makecache fast # docker-ce 指的是社区版 sudo yum install docker-ce docker-ce-cli containerd.io

注:使用阿里云服务器的在这里可能会有些坑。

启动 Docker

命令行提示符 command:("[root@xxx ~] $":2,3,5)
# 设置 docker 开机启动 sudo systemctl enable docker sudo systemctl enable containerd # 启动 docker sudo systemctl start docker

如需禁用开机启动

命令行提示符 command:("[root@xxx ~] $":1,2)
sudo systemctl disable docker sudo systemctl disable containerd

建立 Docker 用户组

默认情况下, docker 命令会使用 Unix 套接字与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket 。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

建立 docker 组:

命令行提示符 command:("[root@xxx ~] $":1)
sudo groupadd docker

将当前用户加入 docker 组:

命令行提示符 command:("[root@xxx ~] $":1)
sudo usermod -aG docker $USER

测试 Docker 安装

  1. 启动服务

    命令行提示符 command:("[root@xxx ~] $":1)
    service docker start
  2. 查看 docker 版本

    命令行提示符 command:("[root@xxx ~] $":1)
    docker version
  3. 拉取测试镜像

    命令行提示符 command:("[root@xxx ~] $":1)
    docker run --rm hello-world # --rm 启动成功后就删掉 Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world b8dfde127a29: Pull complete Digest: sha256:df5f5184104426b65967e016ff2ac0bfcd44ad7899ca3bbcf8e44e4461491a9e Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/

    如果正常输出如上信息,则表示 docker 已安装成功。

    使用 docker 命令获取镜像时,默认会先从本地进行查找,如果本地不存在,则会从配置的镜像源中下载。

  4. 查看本地镜像

    命令行提示符 command:("[root@xxx ~] $":1)
    docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest d1165f221234 5 months ago 13.3kB

配置镜像加速

命令行提示符 command:("[root@xxx ~] $":2,9,11)
# 创建并打开 daemon.json 文件 vi /etc/docker/daemon.json # 写入内容 { "registry-mirrors": ["您的镜像地址"] } # 保存退出 # 重启 sudo systemctl daemon-reload # 重启 docker sudo systemctl restart docker

如何获取自己的镜像地址?

  1. 阿里云
    • 登录阿里云控制台。
    • 搜索并进入 容器镜像服务
    • 找到 镜像工具 / 镜像加速器 即可获取到唯一的镜像地址。
  2. 华为云
    • 登录华为云控制台。
    • 搜索并进入 容器镜像服务
    • 找到 镜像资源 / 镜像中心 并添加 镜像加速器 即可获取到唯一的镜像地址。

获取其余服务器商提供的镜像加速器方式均相似。

卸载 Docker

命令行提示符 command:("[root@xxx ~] $":2,4,5)
# 卸载依赖 sudo yum remove docker-ce docker-ce-cli containerd.io # 删除目录 sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd

Docker 启动流程及原理

Docker 启动流程

Docker启动流程图

Docker VS 虚拟机

Docker 有着比虚拟机更少的抽象层,且 Docker 容器在 Linux 本机上运行,它与其他容器共享主机的内核,因此在新建一个容器时, Docker 并不需要像虚拟机一样重新加载一个操作系统的内核,避免了很多引导操作。

而虚拟机(VM)则是运行一个完整的“Guest操作系统”,通过虚拟机监控程序对主机资源进行虚拟访问。一般来说,虚拟机除了应用程序逻辑所消耗的开销之外,还会产生很多开销。

Docker VS 虚拟机

Docker 常用命令

帮助命令

命令行提示符 command:("[root@xxx ~] $":2,4,6,8)
# 显示 docker 版本信息 $ docker version # 显示 docker 详细信息 $ docker info # 查看 docker 状态 $ docker stats # 帮助命令 $ docker xxx --help

Docker命令图表

Docker 命令行参考文档:https://docs.docker.com/reference

镜像命令

  1. docker images

    命令行提示符 command:("[root@xxx ~] $":1,11-13)
    docker images # 查看镜像 REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest d1165f221234 5 months ago 13.3kB # REPOSITORY: 镜像的仓库源 # TAG: 镜像的标签 # IMAGE ID: 镜像ID # CREATED: 创建时间 # SIZE: 镜像大小 docker images -a # -all 列出所有镜像 docker images -q # -quiet 仅列出镜像的ID docker images -aq # 组合使用
  2. docker search

    命令行提示符 command:("[root@xxx ~] $":1,2)
    docker search mysql # 搜索 mysql 镜像 docker search mysql --filter=STARS=3000 # 过滤:过滤STARS大于3000的mysql镜像
  3. docker pull

    docker 镜像既可使用命令拉取,也可以前往 Docker Hub 进行下载。

    命令行提示符 command:("[root@xxx ~] $":1,24)
    docker pull mysql # 拉取 mysql 镜像 Using default tag: latest # 不指定tag时默认下载最新版本 latest: Pulling from library/mysql 45b42c59be33: Pull complete # 分层下载(分层下载会复用已下载的layer,即已存在的layer不会重复下载) b4f790bd91da: Pull complete 325ae51788e9: Pull complete adcb9439d751: Pull complete 174c7fe16c78: Pull complete 698058ef136c: Pull complete 4690143a669e: Pull complete f7599a246fd6: Pull complete 35a55bf0c196: Pull complete 790ac54f4c47: Pull complete b0ddd5d1b543: Pull complete 1aefd67cb33d: Pull complete Digest: sha256:03306a1f248727ec979f61424c5fb5150e2c5fd2436f2561c5259b1258d6063c # 签名,用于防伪 Status: Downloaded newer image for mysql:latest docker.io/library/mysql:latest # 真实地址 # docker pull mysql # 等价于 # docker pull docker.io/library/mysql:latest docker pull mysql:5.7 # 指定版本拉取镜像
  4. docker rmi

    命令行提示符 command:("[root@xxx ~] $":1-3)
    docker rmi -f d1165f221234 # docker remove image: 删除指定的镜像 docker rmi -f d1165f221234 8457e9155715 # 删除多个镜像 docker rmi -f $(docker images -aq) # 删除所有(递归删除)

容器命令

拉取一个测试镜像。

命令行提示符 command:("[root@xxx ~] $":1)
docker pull centos
  1. 创建并启动容器

    命令行提示符 command:("[root@xxx ~] $":1,2,4,6||"[root@01939a1ac7cd$":7)
    docker run [可选参数] [image] # 新建并启动容器 docker run -it centos /bin/bash # 启动并以交互的方式进入容器 # 后台以端口80启动centos,并命名为centos01,并映射到外部端口3344 docker run -d --name centos01 -p 3344:80 # 进入容器 docker run -it centos /bin/bash [root@01939a1ac7cd /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
  2. 退出容器

    命令行提示符 command:("[root@xxx ~] $":1,4||"[root@286b1fb8e21d[root@xxxxxxxx /]#":5)
    exit # 退出容器(停止并退出) # ctrl + P + Q 退出容器(退出但容器不停止) docker run -it centos /bin/bash docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS 286b1fb8e21d centos "/bin/bash" About a minute ago Up About a minute
  3. 查看运行的容器

    命令行提示符 command:("[root@xxx ~] $":1-4)
    docker ps # 查看正在运行的容器 docker ps -a # 查看所有运行的容器(包括曾经运行过的) docker ps -n=2 # 显示运行过的2个容器 docker ps -aq # 显示当前正在运行的容器ID
  4. 删除容器

    命令行提示符 command:("[root@xxx ~] $":1-3)
    docker rm [容器ID] # 移除指定的容器(不能移除正在运行的容器) docker rm -f $(docker ps -aq) # 移除所有的容器(-f 可删除正在运行的容器) docker ps -a -q|xargs docker rm # 移除所有的容器
  5. 启动和停止容器

    命令行提示符 command:("[root@xxx ~] $":1-4)
    docker start [容器ID] # 启动容器 docker restart [容器ID] # 重启容器 docker stop [容器ID] # 停止容器 docker kill [容器ID] # 强制停止容器
  6. 后台启动

    命令行提示符 command:("[root@xxx ~] $":1)
    docker run -d centos # 后台启动centos # 注:docker必须保证至少一个运行的进程,否则会自动停止
  7. 查看日志

    命令行提示符 command:("[root@xxx ~] $":1)
    docker logs -tf --tail 10 8b1c31f83ff8 # 查看10条日志 # -tf 显示时间戳(格式化显示)
  8. 查看容器内部的进程

    命令行提示符 command:("[root@xxx ~] $":1)
    docker top 8b1c31f83ff8 # 查看指定容器内部运行的进程
  9. 查看镜像的元数据

    命令行提示符 command:("[root@xxx ~] $":1)
    docker inspect 8b1c31f83ff8 # 查看指定容器的所有信息
  10. 进入当前正在运行的容器

    命令行提示符 command:("[root@xxx ~] $":1,2)
    docker exec -it 8b1c31f83ff8 /bin/bash # 进入指定的容器并启动命令行(进入后开启一个新的终端) docker attach 8b1c31f83ff8 # 进入指定容器(不会启动新的进程)
  11. 从容器内拷贝文件到主机

    命令行提示符 command:("[root@xxx ~] $":1,2)
    docker cp [容器ID]:[路径] [路径] # 将容器内指定路径的文件拷贝到主机 docker cp 8b1c31f83ff8:/home/test.java /home # 将容器内的/home/test.java文件拷贝到主机/home目录下

可视化工具 Portainer

Portainer 官方网站:https://www.portainer.io

命令行提示符 command:("[root@xxx ~] $":1,3)
docker run -d -p 8088:9000 -v /root/portainer:/data -v /var/run/docker.sock:/var/run/docker.sock --name dev-portainer portainer/portainer # 查看是否启动成功 curl localhost:8088

此时从外部浏览器访问该主机的对应端口,可以看到如下界面:

Portainer初始页面

完善信息后创建用户后即可登录。

注:如果是云服务器,需要确认在安全组规则中是否开放对应的端口。这里不推荐使用可视化工具,如有兴趣,登录成功后自行探索。

Docker 镜像详解

镜像和容器

镜像由多个层组成,每层叠加之后,从外部看来就如一个独立的对象。镜像内部是一个精简的操作系统(OS),同时还包含应用运行所必须的文件和依赖包。因为容器的设计初衷就是快速和小巧,所以镜像通常都比较小。

镜像就像停止运行的容器(类),但实际上,它可以停止某个容器的运行,并从中创建新的镜像。在该前提下,镜像可以理解为一种构建时(build-time)结构,而容器可以理解为一种运行时(run-time)结构,如下图所示。

镜像和容器间的关系

我们通常使用 docker container rundocker service create 命令从某个镜像启动一个或多个容器,一旦容器从镜像启动后,二者之间就变成了互相依赖的关系,并且在镜像上启动的容器全部停止之前,镜像是无法被删除的。尝试删除镜像而不停止或销毁使用它的容器,会导致出错。

Docker 镜像加载原理

UnionFS(联合文件系统)

UnionFS(联合文件系统): UnionFS 是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。 UnionFS 是 Docker 镜像的基础。镜像可以通过分层来进行继承, 基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性: 一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker 镜像加载原理

Docker 的镜像实际上是由一层一层的文件系统组成的,这种层级的文件系统(UFS)主要包含 bootleaderkernel , bootleader 主要是引导加载 kernel , Linux 刚启动会加载 bootfs 文件系统,在 Docker 镜像的最底层是 bootfs 。这一层与我们典型的 Linux/Unix 系统是一样的,包含 boot 加载器和内核。当 boot 加载完成之后,整个内核就都在内存中了,此时内存的使用权已由 bootfs 转交给内核,此时系统也会卸载 bootfs 。

roofts(root file system)就是各种不同的操作系统发行版,比如 Ubuntu 、 Centos 等。

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

由此可见对于不同的Linux发行版, boots 基本是一致的, rootfs 会有差別,因此不同的发行版可以公用 bootfs 。

镜像的分层结构

Docker 支持通过扩展现有镜像,创建新的镜像。

层级文件系统图示

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

Docker 镜像采用这种分层结构的好处在于共享资源,多个镜像都从相同的基础层镜像创建而来, Docker 主机则只需要在磁盘上保存一份基础层镜像,同时内存中也只加载一份基础层镜像,就可以为所有容器提供服务了。

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

Docker 的镜像都是只读的,而容器层则是可写的。当容器启动时,一个新的可写层被加载到镜像的顶部,它被称为容器层,在容器层之下,都是叫镜像层。

当我们对容器进行添加、删除或修改等操作时,其实所作用的是通过 run 命令启动后生成的一个新的容器层。

提交镜像
命令行提示符 command:("[root@xxx ~] $":2)
# 将一个容器提交为新的镜像 docker commit -m '镜像描述' -a '作者' [容器ID] [镜像名]

docker 原理与 git 相似,也可以简单理解为:原镜像 + 自己的文件 = 新的镜像

容器数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用。
  • 对数据卷的修改会立马生效。
  • 对数据卷的更新,不会影响镜像。
  • 数据卷默认会一直存在,即使容器被删除。

容器目录的挂载:

命令行提示符 command:("[root@xxx ~] $":1,2)
docker run -it -v [宿主机目录]:[容器目录] # 使用 -v 参数实现挂载 docker run -it -v /home/ceshi:/home centos /bin/bash # 将主机的/home/ceshi目录与容器内的目录/home进行挂载,挂载成功后,两个目录之间会自动进行双向同步。 # 指定多个 -v [宿主机目录]:[容器目录],即可挂载多个目录。

数据卷的使用,类似于 Linux 下对目录或文件进行 mount ,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。

具名挂载与匿名挂载

命令行提示符 command:("[root@xxx ~] $":2,4,7)
# 匿名挂载,即没有指定容器内挂载目录对应的外部路径和名字 docker run -d -P --name nginx01 -v /etc/nginx nginx # 使用随机端口启动nginx,并将目录/etc/nginx匿名挂载。 docker volume ls # 查看所有数据卷信息 # 具名挂载,即没有指定容器内挂载目录对应的外部路径,但指定了数据卷名字 docker run -d -P --name nginx02 -v nginx-test:/etc/nginx nginx # 使用 docker inspect 查看容器详情,可以看到卷挂载路径信息。

具名和匿名挂载会将数据卷挂载到 /var/lib/docker/volumes/xxx/_data 目录中。

匿名挂载不易识别,不建议使用。

命令行提示符 command:("[root@xxx ~] $":1,2)
docker run -d -P --name nginx01 -v /etc/nginx:ro nginx # ro -> 只读,只能通过书主机操作 docker run -d -P --name nginx01 -v /etc/nginx:rw nginx # rw -> 读写,默认值。

数据卷容器

多个容器之间也可以通过 --volunes-from 挂载实现容器间数据共享。

命令行提示符 command:("[root@xxx ~] $":1)
docker run -it --name docker02 --volunes-from docker01 exclave/centos # 启动镜像 exclave/centos 并命名为docker02,且挂载到 docker01

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

但如果容器挂载到了本地文件系统,移除所有容器,本地信息也不会丢失。

DockerFile

DockerFile 构建示例

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

添加数据卷除了直接通过命令进行挂载外,还可以通过 dockerFile 进行添加,操作方式如下:

  1. 创建一个 dockerfile 文件(名字可以随意),并在该文件中写入脚本。

    FROM centos
    VOLUME ["volume01", "volume02"]
    CMD echo "--------------success--------------"
    CMD /bin/bash

    FROM: 用于指定基础镜像,定制的镜像都是基于 FROM 的镜像。

  2. 构建镜像

    命令行提示符 command:("[root@xxx ~] $":2)
    # docker build -f [脚本路径]:[镜像标签] . docker build -f dockerfile:v3 .

    注意:命令结尾有一个 . ,代表着本次执行的上下文路径。

  3. 启动构建成功后的镜像

    命令行提示符 command:("[root@xxx ~] $":1)
    docker run -it [镜像ID] /bin/bash

    启动成功后,根目录下会生成脚本中指定的挂载目录(匿名挂载)。

DockerFile 构建说明

  • 每个保留关键字都必须是大写。
  • 执行顺序从上至下。
  • # 表示注解。
  • 每一个指令都会创建提交一个新的镜像层。

DockerFile 指令

FROM      # 基础镜像
MAINTAINER  # 镜像作者:姓名+邮箱
RUN       # 镜像构建时需要运行的命令
ADD       # 步骤
WORKDIR    # 镜像的工作目录
VOLUME     # 挂载目录
EXPOSE     # 指定暴露的端口
CMD       # 指定容器启动时要运行的命令(只有最后一个会生效,可被替代)
ENTRYPOINT  # 与CMD相似,但可以追加命令
ONBUILD    # (触发指令)
COPY      # 类似ADD,将文件拷贝到镜像中
ENV       # 构建时设置环境变量

docker history [镜像ID] 可以查看镜像的构建过程。

DockerFile 的官方命名文件为 Dockerfile ,编译时不指定 -f 参数则会自动读取该文件。

DockerFile 步骤总结

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

制作 Tomcat 镜像

  1. 准备目录及镜像文件。

    命令行提示符 command:("[root@xxx ~] $":1||"[root@xxx usr] $":2,3||"[root@xxx soft] $":4,5)
    cd /usr mkdir soft cd soft wget https://mirrors.cnnic.cn/apache/tomcat/tomcat-9/v9.0.52/src/apache-tomcat-9.0.52-src.tar.gz wget https://download.oracle.com/otn/java/jdk/8u301-b09/d3c52aa6bfa54d3ca74e617f18309292/jdk-8u301-linux-x64.tar.gz
  2. 创建并编辑 Dockerfile 文件。

    命令行提示符 command:("[root@xxx soft] $":1)
    vim Dockerfile

    写入如下内容(可根据实际情况调整):

    FROM centos
    MAINTAINER Chinmoku<xfc_exclave@163.com>
    
    ADD jdk-8u301-linux-x64.tar.gz /usr/soft/
    ADD apache-tomcat-9.0.52-src.tar.gz /usr/soft/
    
    RUN yum -y install vim
    
    ENV MYPATH /usr/soft
    WORKDIR $MYPATH
    
    ENV JAVA_HOME /usr/soft/jdk1.8.0_301
    ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
    ENV CATALINA_HOME /usr/soft/apache-tomcat-9.0.52-src
    ENV CATALINA_BASH /usr/soft/apache-tomcat-9.0.52-src
    ENV PATH $PATH:$JAVA_HOME/bin;$CATALINA_HOME/lib;$CATALINA_BASH/bin
    
    EXPOSE 8080
    
    CMD mkdir /usr/soft/apache-tomcat-9.0.52-src/logs && chmod -R 777 /usr/soft/apache-tomcat-9.0.52-src/bin/*.sh && /usr/soft/apache-tomcat-9.0.52-src/bin/startup.sh && tail -F /usr/soft/apache-tomcat-9.0.52-src/bin/logs/catalina.out
  3. 构建镜像。

    命令行提示符 command:("[root@xxx soft] $":1,2)
    docker build -t mytomcat . docker images REPOSITORY TAG IMAGE ID CREATED SIZE mytomcat latest 93a17796c1b6 2 minutes ago 309MB centos latest 300e315adb2f 8 months ago 209MB
  4. 启动容器。

    命令行提示符 command:("[root@xxx soft] $":1,4)
    docker run -d -p 9090:8080 --name ChinmokuTomcat -v /home/Chinmoku/build/tomcat/test/:/usr/local/apache-tomcat-9.0.52-src/webapps/test -v /home/Chinmoku/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.52-src/logs mytomcat d7cd19e650dd71c21d293199578acd80407d04c27ff132648cddc57ff2327d99 docker ps # 查看容器是否存在该容器

    如果容器列表中不存在,则通过查看容器日志进行问题排查:

    命令行提示符 command:("[root@xxx soft] $":1,2)
    docker ps -a # 查看容器历史记录 docker logs -tf --tail 20 d7cd19e650dd7 # 查看20条日志信息

    通过日志信息,分析容器启动失败或自动停止的原因(一般是 Dockerfile 文件编写错误或文件权限等问题引起的),在排查问题的过程中,您也许会反复用到以下命令:

    命令行提示符 command:("[root@xxx soft] $":1-5)
    docker ps -a docker logs -tf --tail 20 xxx # 查看日志 docker rm xxx docker rmi -f $(docker images -aq) # 移除所有镜像 docker inspect xxx
  5. 启动成功后,进入容器。

    命令行提示符 command:("[root@xxx soft] $":1)
    docker exec -it d7cd19e650dd7 /bin/bash

    由于当前容器已存在其他活跃进程,这里使用 CTRL + P + Qexit 均可退出容器。

    另外,在云服务器环境下,外部网络访问时,需要注意配置安全组规则。

  6. 部署测试文件。

    /webapps/test 的挂载目录下创建测试文件:

    命令行提示符 command:("[root@xxx soft] $":1||"[root@xxx test] $":2,,3)
    cd /home/Chinmoku/build/tomcat/test mkdir WEB-INF cd WEB-INF

    创建并编辑 web.xml 文件:

    命令行提示符 command:("[root@xxx WEB-INF] $":1)
    vim web.xml

    写入如下内容:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
        <display-name>docker-tomcat</display-name>
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
        </welcome-file-list>
    </web-app>

    创建并编辑 index.html 文件:

    命令行提示符 command:("[root@xxx WEB-INF] $":1)
    vim index.html

    写入如下内容:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>docker:::tomcat</title>
        </head>
    
        <body>
            <h3 style="color: red;">Docker built tomcat successful!</h3>
        </body>
    </html>
  7. 外部浏览器访问。

镜像发布

发布到 Docker Hub

  1. Docker Hub 注册账号。

  2. 登录。

    命令行提示符 command:("[root@xxx ~] $":1)
    docker login -u [用户名] Password: ******
  3. 提交。

    命令行提示符 command:("[root@xxx ~] $":1)
    docker push [仓库名]/[镜像名]:[版本标签]

发布到阿里云

Docker 流程总结

Docker流程图

Docker 网络

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个端口到内部容器开放的网络端口。

-p 则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。

Docker0

宿主机每启动一个 Docker 容器,就会给容器分配一个 IP 。宿主机只要安装了 docker ,就会安装一个 docker0网卡进行桥接,这种技术被称为 evth-pair 技术。

veth-pair 是成对出现的虚拟设备接口,它们一端连着协议栈,一端彼此相连,正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备,以此构建复杂的虚拟网络结构。

Docker 的网络模式

执行如下命令,可查看当前主机 docker 环境的网络模式信息:

命令行提示符 command:("[root@xxx ~] $":1)
docker network ls NETWORK ID NAME DRIVER SCOPE 0b517709dd56 bridge bridge local e49e96b5ebf4 host host local 963d10eff9fe none null local

Docker 网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接 Container-IP 访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即 docker run 创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过 [宿主机IP]:[容器端口] 访问容器。

  1. host 模式

    配置方式: -network=host

    如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace ,而是和宿主机共用一个 Network Namespace 。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

    host 模式最大的优势在于网络性能比较好,但是 docker host 上已经使用的端口就不能再用了,因此网络的隔离性不好。

  2. container 模式

    配置方式: –net=container:[NAME/ID]

    这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace ,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。

  3. none 模式

    配置方式: –net=none

    使用 none 模式时, Docker 容器拥有自己的 Network Namespace ,但是,并不为 Docker 容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。

    这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。

  4. bridge 模式

    配置方式: –net=bridge (docker 默认)

    当 Docker 进程启动时,会在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

    从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡 veth-pair 设备,Docker 将 veth-pair 设备的一端放在新创建的容器中,并命名为 eth0 (容器的网卡),另一端放在主机中,以 vethxxx 这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。可以通过 brctl show 命令查看。

    使用 brctl show 命令需要先安装工具包: sudo yum install -y bridge-utils

    bridge 模式是 docker 的默认网络模式,不写 --net 参数,就是bridge模式。使用 docker run -p 时, docker 实际是在 iptables 做了 DNAT 规则,实现端口转发功能。可以使用 iptables -t nat -vnL 查看。

    Docker网络桥接

在不同的 docker 容器之间,通过如下方式使用 ping 命令:

命令行提示符 command:("[root@xxx ~] $":1)
docker exec -it tomcat02 ping tomcat01 # default # 假定tomcat01和tomcat02在启动时均未指定 --link 参数

这时,两个容器之间是无法 ping 通的。

但当容器启动时,可以指定 --link 参数,即可实现不同容器之间通过容器名进行互联,示例如下:

命令行提示符 command:("[root@xxx ~] $":2,5,6)
# 运行一个 tomcat03 容器并连接到 tomcat02 docker run -d -P --name tomcat03 --link tomcat02 tomcat # 再次执行 ping 命令 docker exec -it tomcat03 ping tomcat02 # success docker exec -it tomcat02 ping tomcat03 # default

通过示例,可以看出使用 --link 参数可以单向打通容器之间的网络。

实际上, --link 参数的本质就是在 /etc/hosts 文件中添加映射。

现在 docker 已经不再推荐使用 --link 来进行容器之间的互联,而是推荐使用自定义网络。

容器互联:自定义网络

通过 docker network create 命令即可创建自定义网络。

当使用 docker run 命令启动容器时,其实该命令默认指定了 --network bridge 参数,该参数即表示所启动的容器将使用名为 bridge 的网络, bridge 是 docker0 默认的网络名称,因此,在创建自定义网络的名称应当避开这个名字。

在创建一个自定义网络时,需要注意的是, subnet 子网是必须配置,并加上掩码; driver 默认即为 bridge 模式,值不值定都行。 gateway 是配置网关,必填。示例如下:

命令行提示符 command:("[root@xxx ~] $":1)
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet

在这条命令中,我们指定了网段为 192.168.0.0 ,掩码为16位(即拥有255*255个可用IP,24位则只有255个可用IP),指定的网关为 192.168.0.1 ,自定义的网络名称为 mynet 。

查看自定义网络创建是否成功:

命令行提示符 command:("[root@xxx ~] $":1,8)
docker network ls NETWORK ID NAME DRIVER SCOPE 0b517709dd56 bridge bridge local e49e96b5ebf4 host host local 0df835de9886 mynet bridge local 963d10eff9fe none null local docker network inspect mynet # 查看自定义网络信息

使用自定义网络创建容器:

命令行提示符 command:("[root@xxx ~] $":1-4)
docker run -d -P --name tomcat-net-01 --net mynet tomcat docker run -d -P --name tomcat-net-02 --net mynet tomcat docker exec tomcat-net-02 ping tomcat-net-01 # success docker exec tomcat-net-01 ping tomcat-net-02 # success

网络连通

通过上面的操作,基于自定义网络创建的容器之间,已经实现网络互联,但自定义网络与 docker0 默认网络之间仍旧无法联通,我们可以通过如下方式,将 docker0 默认的网络添加到自定义的网络中。

命令行提示符 command:("[root@xxx ~] $":1-4)
docker run -d -P --name tomcat-01 tomcat # -network bridge docker exec tomcat-01 ping tomcat-net-01 # fault docker network connect tomcat01 mynet # connect tomcat01 to mynet docker exec tomcat-01 ping tomcat-net-01 # success

Redis 集群部署

docker search redis
docker pull redis
mkdir redis-cluster-d
cd redis-cluster-d

[❗️TODO]{.label .danger} 待完善

Springboot 微服务打包 Docker

[❗️TODO]{.label .danger} 待完善

参考

版权声明

本文链接:https://www.chinmoku.cc/java/advanced/docker-tutorial/

本博客中的所有内容,包括但不限于文字、图片、音频、视频、图表和其他可视化材料,均受版权法保护。未经本博客所有者书面授权许可,禁止在任何媒体、网站、社交平台或其他渠道上复制、传播、修改、发布、展示或以任何其他方式使用此博客中的任何内容。

Press ESC to close