0%

Docker

Docker Tutorial

https://www.docker.com/

大佬slides的结构

  1. Docker的定义

  2. Docker架构

    *:不同系统下的架构

  3. Docker优势

  4. Docker三个基本概念

    • Image
    • Container
    • Repository
  5. Docker常用command

  6. Dockerfile

    构建镜像

    使用的命令行/参数

    *:layered architecture

    构建成功之后的代码运行界面

  7. Docker文件系统

    1. AUFS/创建容器时AUFS产生的变化
    2. Volumes
    3. Bind mounts
  8. Docker容器连接

  9. Docker容器互联

    *:网络结构图

  10. Docker Compose

  11. Docker仓库管理

  12. References

pdf

安装

Install Docker Desktop on Mac

(However, to get the best experience, we recommend that you install Rosetta 2)

自带的tutorial命令行

docker run -d -p 80:80 docker/getting-started

![Screen Shot 2022-02-21 at 16.38.18](https://raw.githubusercontent.com/HenryVarro666/images/master/uPic/Screen Shot 2022-02-21 at 16.38.18.png)

参考资料

文档

  1. https://zh.wikipedia.org/wiki/Docker

  2. https://docs.docker.com/get-started/overview/

    https://docs.docker.com/get-started/

  3. https://www.runoob.com/docker/docker-tutorial.html

  4. https://yeasy.gitbook.io/docker_practice/

  5. https://www.bilibili.com/read/cv15180540

    https://www.bilibili.com/read/cv15181760

视频

  1. https://www.youtube.com/watch?v=iqqDU2crIEQ
  2. https://www.youtube.com/watch?v=pTFZFxd4hOI
  3. https://www.youtube.com/watch?v=3c-iBn73dDE
  4. https://www.youtube.com/watch?v=fqMOX6JJhGo
  5. https://www.bilibili.com/video/BV1s54y1n7Ev?from=search&seid=6545698363302615398&spm_id_from=333.337.0.0

Slides结构

什么是Docker

https://docs.docker.com/get-started/overview/

What can I use Docker for?

https://docs.docker.com/get-started/overview/

https://www.runoob.com/docker/docker-tutorial.html

和传统的虚拟化方式比较

  • Linux容器基础之LXC技术介绍:

    描述:Docker引擎的基础是Linux容器(Linux Containers,LXC)技术,容器有效的将各个操作系统管理的资源划分到孤立的组,以便更好的在孤立的组之间平衡有冲突的资源使用需求。
    容器可以在核心CPU本地运行指令,并不需要任何专门的解释机制;最早的容器技术可以追溯到1982年Unix系列操作系统上的chroot工具;用户操作容器就像操作一个轻量级虚拟机那样简单,也可以将容器比作为一种沙盒(Sandbox)

    传统虚拟化

    传统虚拟化

    Docker

    Docker

https://www.bilibili.com/read/cv15180540

https://yeasy.gitbook.io/docker_practice/introduction/why

传统虚拟化方式是在硬件层面实现虚拟化,需要有额外的虚拟机管理应用和虚拟机操作系统层,然后在该系统上运行业务所需程序;

Docker虚拟化方式是在宿主系统层面上实现虚拟化,直接复用本地主机的操作系统与内核,容器内没有自己的内核,所以容器内的应用进程实际运行于宿主机内核,因此更加轻量级

  • 什么是虚拟化技术?
    虚拟化技术是一个通用的概念,在不同的领域有不同的理解;在计算机领域一般指的是计算机虚拟化(Computer Virtualization)或者说是服务器虚拟化;虚拟化的核心是对资源进行抽象和管理,目标往往是为了在同一个主机上运行多个系统或者应用,从而提高系统资源的利用率,同时带来降低成本,方便管理和容错和容灾等好处;

    • 硬件虚拟化:真正意义上的基于它的技术不多见,少数网卡中的单根多IO虚拟化等技术;

    • 软件虚拟化(Virtualization)

    1.应用虚拟化

    2.平台虚拟化:细分

    2.1 完全虚拟化

    2.2 硬件辅助虚拟化:利用硬件CPU辅助支持虚拟化技术Intel-VT和AND-V处理铭感指令来实现完全虚拟化的功能;

    2.3 部分虚拟化:只对部分硬件资源进行虚拟化,客户端系统需要进行修改;

    2.4 准虚拟化(Paravirtualization):如xen

    2.5 操作系统级虚拟化:内核通过创建多个虚拟化的操作系统实例内核和库来隔离不同的进程,dokcer以及其他容器都在这个范畴之内; 作者:WeiyiGeek https://www.bilibili.com/read/cv15180540 出处:bilibili

为什么要用Docker

Docker的体系(architecture)

Docker Architecture Diagram

image-20220224100436306

daemon

A background process that handles requests for services such as print spooling and file transfers, and is dormant when not required.

(指在某种条件得到满足时能自动进行工作的计算机程序),守护程序

监听 Docker API 请求并管理 Docker 对象,如图像、容器、网络和卷。守护进程还可以与其他守护进程通信,以管理 Docker 服务。

完整的Docker组成

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

(2) REST API: 主要与Docker Daemon进行交互,比如Docker Cli或者直接调用REST API;

(3) 客户端(Docker Client): 它是与Docker交互的主要方式通过命令行接口(CLI)客户端(docker命令),客户机将命令通过REST API发送给并执行其命令;()

(4) Register Repository 镜像仓库: Docker注册表存储Docker镜像,可以采用Docker Hub是公共注册仓库,或者采用企业内部自建的Harbor私有仓库;

(5) Image 镜像: 映像是一个只读模板,带有创建Docker容器的指令。映像通常基于另一个映像,还需要进行一些额外的定制,你可以通过Docker Hub公共镜像仓库进行拉取对应的系统或者应用镜像;

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

(7) Services : Docker引擎支持集群模式服务允许您跨多个Docker守护进程()扩展管理容器,服务允许您定义所需的状态,例如在任何给定时间必须可用的服务副本的数量。默认情况下,服务在所有工作节点之间进行负载平衡。对于使用者来说Docker服务看起来是一个单独的应用程序

WeiyiGeek.引擎图

Docker内部的具体实现

Docker 内部具体实现

Docker资源隔离

Docker 本质是宿主机上的一个进程

•通过namespace实现资源隔离以及轻量级虚拟化容器服务

•通过cgroup实现了资源限制

•通过写时复制技术(Copy-on-write)实现了高效的文件操作

Docker通过由内核namespace提供实现的隔离,namespace的API包括还有在/proc下的部分文件

•进程隔离:每个容器都运行在自己的进程环境中

•网络隔离:容器间的虚拟网络接口和 IP 地址都是分开的

•文件系统隔离:每个容器都有自己的 root 文件系统

•资源隔离和分组:使用 cgroups 将 CPU 和内存之类的资源独立分配给每个 Docker 容器

Namespace的六项隔离

namespace的六项隔离

Docker常见概念

概念 说明
Docker 镜像(Images) Docker 镜像是用于创建 Docker 容器的模板,比如 Ubuntu 系统。
Docker 容器(Container) 容器是独立运行的一个或一组应用,是镜像运行时的实体。
Docker 客户端(Client) Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。
Docker 主机(Host) 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
Docker Registry Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。
Docker Machine Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure

指令+重要概念

image 镜像

images 类似于虚拟机镜像,借鉴了Git利用分成分层优点,通过文件系统分层的概念实现了分层复用,极大的节约了磁盘空间;

简单的您可以将它理解成为一个面向Docker引擎的只读模板包含文件系统

创建镜像的方法

  1. 从已有镜像的容器创建
  2. 基于本地模板导入:使用操作系统模板导入一个镜像文件
  3. 基于Dockerfile导入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
######### 从已有镜像创建 ############
$sudo docker commit [option-选项] ContainerId [Repository[:Tag]]
$sudo docker commit -m "xx" -a "oo" ContainerID(被修改过的ID) [Repository[:Tag]]

-a,--author="作者"

-m,--message="更改信息"

-p,--pause=ture 提交时暂停容器Container运行

$sudo docker run -it centos:latest /bin/bash
[root@32a481e170c6 ~]$ touch {1..10}.txt #上面这个容器ID非常重要在进行修改之后

$sudo docker container ls -a #查看容器记录

#创建一个新的镜像
$sudo docker commit -m "Zabbix base in Centos7" -a "Weiyigeek" 32a481e170c6 centoszabbix:latest
sha256:680ddb57c4b80c625ef68e113f553ee932a06f25d4685d25a0b6464cf5d60982 #成功会给出一个镜像ID

######### 从本地模板导入 ############
$sudo cat ubuntu-14.04.tar.gz | docker import - ubuntu:14.04 #本地导入镜像命令

当该镜像在容器运行存在的时候,镜像文件默认是无法被删除的;必须停止/删除容器ID才能删除镜像文件

1
2
3
4
5
6
7
8
9
10
11
12
#当同一个镜像有多个标签的时候rmi命令只是删错了该进行的标签而且,并不影响镜像文件
#但当只剩下一个标签的时候就要小心了,如果在停止的状态下再次使用rmi命令则会删除该镜像
$ sudo docker rmi ubuntu18.04
Untagged: ubuntu18.04:latest
$ sudo docker rmi ubuntu ubuntutls
Untagged: ubuntu:latest
Untagged: #删除了这个镜像文件的所有AUFS层
ubuntu@sha256:d26d529daa4d8567167181d9d569f2a85da3c5ecaf539cace2c6223355d69981

$ sudo docker rmi -f ubuntutls #强制删除
Untagged: ubuntutls:latest
Deleted: sha256:d131e0fa2585a7efbfb187f70d648aa50e251d9d3b7031edf4730ca6154e221e

镜像操作常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#搜索关于Archlinux镜像,输出信息不截断显示
docker search --no-trunc=false [镜像名称]

#仓库名(Repository) 或者 标签名[不指定着默认latest,即最新]
#获取Hub镜像如果不指定TAG将默认选择仓库中最新颁布的镜像
docker pull name/[repository[:tag]]

#上传到docker仓库
docker push DockerHubUser用户/test:latest

#列出本机已有镜像
docker images

#为本地镜像添加一个新标签 [注意仓库名称必须小写]
docker tag 原仓库[:标签] 新仓库名[:标签]

#修改镜像的标签
docker tag <image id> username/name:devel

#获取镜像的详细信息
docker inspect [image id]

#-f 获取单个属性 返回JSON
docker inspect -f {{".Architecture"}} 550(images Id 前面3位)

#删除镜像 镜像id|仓库名称
docker rmi [<image id>|<repository> ]

#不建议强行删除正在容器中运行的镜像文件
docker rmi -f <images id>

#将镜像文件打包存出到磁盘
docker save -o 保存文件名.tar [repository[:tag]]

#将镜像文件打包存出到磁盘
docker save [repository[:tag]] > 保存文件名

#将打包的镜像文件进行载入
docker load --input 保存文件名.tar
docker load < 保存文件名.tar

image-20220227083846357

save和export的区别

  1. save 与 load 命令对应即导出与导入镜像,而export与import命令对应即导出导入容器;

  2. save 保存后 load 加载的镜像没有丢失历史和层(Layer),而容器export导出然后import导入时所有的提交历史将会丢失,这意味着您无法回滚到之前的层;

  3. 补充:通过import导入的方式镜像只有一层,而通过commit的方式生成的镜像实际是在原有的Base Image(即复写层)上又生成了一层

Container 容器

镜像自身只读的,容器从镜像启动的时候会在镜像的最上层创建一个可写层,镜像本身将保持不变

创建并运行容器的标准操作

  • 检查本地是否存在指定镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一个可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个IP地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

启动容器有两种方式

  • 一种是基于镜像新建一个容器并启动
  • 另外一个是将在终止状态(stopped)的容器重新启动

容器操作常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#创建容器但处于停止状态
docker create -it repository[:tag]

#启动创建的容器
docker start -a -i <container id>

#-a 参数 连接容器并打印输出或错误 -

#-i 参数 启动容器并进入交互模式

#创建并启动容器 等同于上面两条命令
docker run -t -i repository[:tag] /bin/bash

#-t:让Docker分配一个伪终端(pseudo-tty)并绑定在容器的标准输入上.

#-i:则让容器的标准输入保持打开.

# CTRL+Z 和 Exit 则退出容器Container

docker run -d repository[:tag] /bin/sh -C "echo hello word" #守护态运行

#-d 参数即可后台运行,用户无法看到容器中的信息

#-c 执行的Command

#--rm 添加这个标记,容器会在停止后立即删除自身 (注意:与-d不能同时使用)

#--name 使用--name web 标记可以为容器重新命名

#获取容器的输出信息
docker logs <Container-id>

#采用ps -a NAMES 进入容器
docker attach [names]

#docker exec 至1.3版本起可以在容器中运行命令
docker exec -it <Container-id> /bin/bash

#显示本机上的所有容器ID运行的容器ID信息
docker ps -aq

#重启容器
docker restart <container id>

#停止容器
docker stop <container id>
#强行终止容器 可以直接发送SIGKILL信号来终止容器
docker kill <cantainer id>

#删除容器删除依赖该镜像的容器ID,前3位即可
docker rm <container id>

#-f,--force=false 强制终止并删除一个运行中的容器[默认会发生SIGKILL信号]

#-l,--link=false 删除容器连接但保留容器

#-v,--volumes=false 删除容器挂载的数据卷

#导出容器
docker export <container id> >导出文件.tar

#导入容器
docker import - repository[:tag]

Docker load 与 Docker import 的比较

描述:导入容器和导入镜像是差不多的但是实际上又是有所区别的

load:导入镜像存储文件到本地镜像库

import:导入一个容器快照到本地镜像库

容器快照:文件将丢失所有的历史记录和元数据信息(即保留容器当时的快照状态),导入的时候还需要重新制定标签等元数据信息;

镜像存储:文件将保存完整的记录,并且体积也要大

rm和rmi的区别

  • docker rm : 删除一个或多个 容器
  • docker rmi : 删除一个或多个 镜像
  • docker prune : 用来删除不再使用的 docker 对象

仓库

一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。

如果不给出标签,将以 latest 作为默认标签。

分为公开(Public)和私有(Private)。前者有多个版本,后者为用户自己搭建

Public

Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的 官方镜像。

除此以外,还有 Red Hat 的 Quay.io;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务;代码托管平台 GitHub 推出的 ghcr.io。

Private

Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。

开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。

镜像仓库操作常用命令

1
2
3
4
5
6
7
8
9
10
#连接并初始化hub仓库 需要输入用户名和密码认证,然后存放于/root/.docker/config.json 文件中
docker login

#下载指定仓库到本地
docker pull [repository]

#上传镜像到仓库
docker push [imges]


创建和使用私有仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ docker run -d -p 5000:5000 registry
#默认情况下仓库创建在容器的/tmp/register目录下,-v 通过镜像文件存放在宿主机本地的指定路径/opt/data/registry上;
$ docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry

#-d:容器启动后会进入后台,用户无法看到容器中的信息.

#-p:指定仓库镜像的端口.

#-v:将镜像的存放位置放在本地指定的路径上

#此时会在本地启动一个私有仓库服务,监听端口为5000; 更新标签本地仓库地址并上传到私有仓库之中
#更改镜像的标签
$docker tag ubuntu:14.04 10.0.2.2:5000/test

#下载镜像
$docker pull 10.0.2.2:5000/test

#可以以这个来测试是不是存在docker 5000未授权访问漏洞
#上传成功后可以查看到返回的json字符串
$ curl http://10.0.2.2:5000/v1/search

curl命令

curl是一个开源的用于数据传输的命令行工具与库,它使用URL语法格式,支持众多传输协议。作用是发出网络请求,然后获取数据,显示在”标准输出”(stdout)上面。

自动创建(Automated Builds)

对于需要经常升级镜像内程序来说十分的方便

  • 创建登录Docker Hub 绑定Github;
  • 在Docker Hub中配置一个自动创建
  • 选取一个目标网站中的项目(需要Dockerfile和分支)
  • 指定Dockerfile的位置并提交创建,可以在”自动创建页面”跟踪每次创建的状态;

总结:仓库管理镜像的设计理论与Git差不多,工作流程为文件分发和合作带来的众多优势

Dockerfile

Dockerfile是一个文本格式的配置文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
用户可以使用Dockerfile快速创建自定义的镜像;通过它所支持的内部指令,以及使用它创建镜像的基本过程,Docker拥有”一点修改代替大量更新”的灵活之处

  • 文本化的镜像生成操作让其方便版本管理和自动化部署

    方便部署

  • 每条命令对应镜像的一层,细化操作后保证其可增量更新,复用镜像块减小镜像体积

    减小体积

总结为一点就是**将每一层修改、安装、构建、操作命令都写入到一个脚本之中**。

[一个镜像构建时不能超过 127 层,我们需要保证了稳定的变化的命令至于上层保证了每层打包出来的 Layer 能够尽可能的复用,而不会徒增镜像的大小,影响后续拉取镜像的速度 ]

基本结构

•基础镜像信息:FROM <image> 或者 FROM <image>:<tag>

•维护者信息: MAINTAINER (建议使用LABEL标签进行替代,先已丢弃)

•镜像标签信息: LABEL

•镜像操作指令: RUN

•容器启动时执行指令: CMD

在编写完成Dockerfile之后可以通过docker build 命令来创建镜像,该命令读取指定路径下(包括子目录)的dockerfile(实际上是构建上下文Context),并将该路径下的内容发送给Docker服务端由它创建镜像; 因此一般建议放置Dockerfile的目录为空另外可以通过dockerignore文件(每一行添加一条匹配模式)会让Docker忽略路径下的目录和文件

docker 镜像生成常用命令

docker build [选项]

  • t :指定标签信息

–build-arg <参数名>=<值>

构建镜像的几种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#docker build [选项]

#-t :指定标签信息
#--build-arg <参数名>=<值>

#构建镜像的几种方式:

#1) 指定的Dockfile所在路径为/tmp/docker_builder
$docker build -t [TAG/version] /tmp/docker_builder

#2) 支持从 URL 构建
$docker build https://github.com/twang2218/gitlab-ce-zh.git#:11.1

#3) 用给定的 tar 压缩包构建
$docker build http://server/context.tar.gz

#4) 从标准输入中读取 Dockerfile 进行构建
$docker build - < Dockerfile
$cat Dockerfile | docker build -

#5) 从标准输入中读取上下文压缩包进行构建
#标准输入的文件格式还可以是 gzip、bzip2 以及 xz
$docker build - < context.tar.gz

Dockerfile指令

FROM - 基础镜像信息

描述:

尽可能使用官方镜像或者信任的镜像作为你构建镜像的基础设施,推荐使用Alpine镜像,因为它被严格控制并保持最小尺寸(目前小于 6 MB),但它仍然是一个完整的发行版。

1
2
3
4
5
6
7
8
#基础语法

FROM <image> 或者 FROM <image>:<tag>

#使用案例

FROM alpine
FROM golang:1.9-alpine as builder # 注意:多阶段构建使用 as 来为某一阶段命名

LABEL - 标签信息

描述:

可以给镜像添加标签来帮助组织镜像、记录许可信息、辅助自动化构建等

注意:如果你的字符串包含空格,那么它必须被引用或者空格必须被转义。如果您的字符串包含内部引号字符(”),则也可以将其转义。

1
2
3
4
5
6
7
8
9
10
11
12
13
#基础语法:每个标签一行,由 LABEL 开头加上一个或多个标签对。

LABEL key=<value>

#使用案例

#Set one or more individual labels `#`开头的行是注释内容。

LABEL maintainer="WeiyiGeek"
LABEL vendor="ACME Incorporated"
LABEL version=1.1
LABEL com.example.version="0.0.1-beta"
LABEL com.example.release-date="2015-02-12"

在 1.10 之前,建议将所有标签合并为一条LABEL指令,以防止创建额外的层,但是现在这个不再是必须的了

MAINTAINER - 维护者信息

1
2
3
4
5
6
7
#基础语法

MAINTAINER key=<value>

#使用案例

MAINTAINER WeiyiGeek master@weiyigeek.top

RUN - 镜像操作命令

描述:

为了保持 Dockerfile 文件的可读性,以及可维护性,建议将长的或复杂的RUN指令用反斜杠\分割成多行。

RUN 指令最常见的用法是安装包用的apt-get,因为该指令会安装包

有几个问题需要注意 :

  • 不要使用 RUN apt-get upgrade 或 dist-upgrade , 如果基础镜像中的某个包过时了,你应该联系它的维护者。
  • 如果你确定某个特定的包比如 foo 需要升级,使用 apt-get install -y foo 就行,该指令会自动升级 foo 包。
  • 最好将 RUN 多条语句汇集成为一条 apt-get update 和 apt-get install 以及 rm -rf /var/lib/apt/lists* 组合成一条 RUN 声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#基础语法

RUN <COMMAND> 或者 RUN ["executable","param1","param2"]

#使用案例

#如:RUN ["/bin/bash","-c","echo Hello"],当命令较长时可以使用\来换行;
RUN apt-get update;\
apt-get install -y nginx ;\
rm -rf /var/lib/apt/lists/*

#展示了所有关于 apt-get 的建议
#其中 s3cmd 指令指定了一个版本号`1.1.*`。如果之前的镜像使用的是更旧的版本,指定新的版本会导致 apt-get udpate 缓存失效并确保安装的是新版本。
RUN apt-get update && apt-get install -y \
aufs-tools \
automake \
build-essential \
curl \
dpkg-sig \
libcap-dev \
libsqlite3-dev \
mercurial \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.* \
&& rm -rf /var/lib/apt/lists/*

image-20220227095244915

CMD- 容器启动时执行指令

描述:

指令用于执行目标镜像中包含的软件和任何参数, 实际上为容器提供一个默认的执行命令。

在Dockerfile中CMD被用来为ENTRYPOINT指令提供参数,则CMD和ENTRYPOINT指令都应该使用exec格式

当基于镜像的容器运行时将会自动执行CMD指令, 并且如果在docker run命令中指定了参数,这些参数将会覆盖在CMD指令中设置的参数。

多数情况下CMD 都需要一个交互式的 shell (bash, Python, perl 等),例如 CMD [“perl”, “-de0”],或者 CMD [“PHP”, “-a”]。使用这种形式意味着,当你执行类似docker run -it python时,你会进入一个准备好的 shell 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#基础语法
#CMD指令有如下三种格式

#exec格式
CMD ["executable","param1","param2"]
#为ENTRYPOINT提供参数
CMD ["param1","param2"]
#shell格式,在/bin/bash中执行提供给需要交互的应用
CMD command param1 param2

#基础示例

#(1)如果创建镜像的目的是为了部署某个服务(比如 Apache)
CMD ["apache2", "-DFOREGROUND"]
#(2)如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:
CMD echo $HOME #在实际执行中,会将其变更为: CMD [ "sh", "-c", "echo $HOME" ]

注意事项:

(1)如果用户启动容器指定了运行命令则会覆盖掉CMD指定命令,注意每个Dockerfile只能有一条CMD命令,如果指定了多条命令只有最后一条执行;

(2)CMD 在极少的情况下才会以 CMD [“param”, “param”] 的形式与ENTRYPOINT协同使用,除非你和你的镜像使用者都对 ENTRYPOINT 的工作方式十分熟悉。

EXPOSE - 端口映射指令

描述:

EXPOSE指令用于指定容器将要监听的端口即默认向外部的暴露的服务端口。因此你应该为你的应用程序使用常见的端口。
对于外部访问,用户可以在执行 docker run 时使用一个标志来指示如何将指定的端口映射到所选择的端口。

#基础语法
EXPOSE […]

1
2
3
4
5
6
7
8
9
10
11
12
13
#基础示例

#(1) 告诉dokcer服务端容器暴露的端口号,供互联系统使用; 也就是 docker run -P 时会自动随机映射 EXPOSE 的端口。

EXPOSE 22 80 8443

#(2) 例如提供 Apache web 服务的镜像应该使用 80,而提供 MongoDB 服务的镜像使用 27017

EXPOSE 80 27017

#(3) 指定一个范围

EXPOSE 30000-40000

ENV - 修改环境变量指令

描述:

为了方便新程序运行,你可以使用ENV来为容器中安装的程序更新 PATH 环境变量。例如使用ENV PATH /usr/local/nginx/bin:$PATH来确保CMD [“nginx”]能正确运行。类似于程序中的常量,该方法可以让你只需改变 ENV 指令来自动的改变容器中的软件版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#基础语法

ENV <key> <value> #会被后续的RUN指令使用,并在容器运行时保持;

#基础示例

#方式1: 如RUN还是运行时的应用,都可以直接使用这里定义的环境变量。
ENV PG_MAJOR 9.3
ENV PATH /usr/local/postgres-\$PG_MAJOR/bin:$PATH
RUN curl -SL http://example.com/postgre-$PG_MAJOR.tar.xz && ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

#方式2: 将所有的环境变量定义在一条ENV语句中
ENV VERSION=1.0 DEBUG=on \
NAME="Happy Feet"

ARG - 构建参数

描述:

构建参数和 ENV 的效果一样都是设置环境变量不同点就是容器构建完成则失效

1
2
3
4
5
ARG <参数名> [=<默认值>]

#调用方式与Shell中一致

${IMG_PATH}

ADD - 添加指定目录文件到镜像指令

描述:

该命令将复制指定的源文件到镜像内中的目标文件,其中可以是在Dockerfile所在的目录的一个相对路径(文件或者目录)/URL/tar文件(本地 tar 提取和远程 URL 支持)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#基础语法

ADD <src> <dest>

#基础示例

#(1)最佳用例是将本地tar文件自动提取到镜像中

ADD rootfs.tar.xz

#(2)下载后的文件权限自动设置为 600 这个自动解压缩的功能非常有用;

ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz
ADD --chown=55:mygroup files* /mydir/

注意事项:

(1)为了让镜像尽量小,最好不要使用 ADD 指令从远程 URL 获取包,而是使用 curl 和 wget。

1
2
3
4
5
6
7
8
9
10
11
#你可以在文件提取完之后删掉不再需要的文件来避免在镜像中额外添加一层
#比如尽量避免下面的用法:
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all

#而是应该使用下面这种方法:由于使用的管道操作,所以没有中间文件需要删除。
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all

COPY - 复制指定文件或者目录到容器中

描述:

COPY只支持简单将本地文件拷贝到镜像中它比 ADD 更透明,所以ADD和COPY功能类似但一般优先使用 COPY ;

COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。

当目标路径不存在时候自动创建,当使用本地目录作为源目录时候推荐使用COPY:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#基础语法

COPY <src> <dest>

#选项

--from=多阶段构建的镜像名称 #FROM Alpine AS [名称]

#常规方式

COPY package.json /usr/src/app/
#<源路径> 可以是多个,甚至可以是通配符
COPY hom* /mydir/
COPY hom?.txt /mydir/

#选项来改变文件的所属用户及所属组。

COPY --chown=55:mygroup files* /mydir/
COPY --from=0 /go/src/github.com/go/helloworld/app . #多阶段构建,从上一阶段的镜像中复制文件
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf #复制任意镜像中的文件(但需要指定镜像名称 )

Tips:

对于其他不需要 ADD 的自动提取功能的文件或目录,你应该使用 COPY。

采用CPOY –from 从上一个构建阶段拷贝文件时,使用的路径是相对于上一阶段的根目录的,此时建议复制成果时候采用绝对路径;

ENTRYPOINT - 配置容器启动进入后的执行命令-应用运行前的准备工作

描述:

该指令是设置镜像的主命令,其作用是允许将镜像当成命令本身来运行(使用终端提供默认选项)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#基础示例
#exec格式推荐的格式
ENTRYPOINT ["executable","param1","param2"]
#shell格式:使用ENTRYPONT指令并不可被docker run提供的参数覆盖(与CMD不同之处)
ENTRYPOINT command param1 param2 #shell中执行

# 基础示例
# 1.例如下面的示例镜像提供了命令行工具 s3cmd:
ENTRYPOINT ["s3cmd"]
CMD ["--help"]
#现在直接运行该镜像创建的容器会显示命令帮助: $ docker run s3cmd
#或者提供正确的参数来执行某个命令:$ docker run s3cmd ls s3://mybucket

# 2.使用ENTRYPOINT 的exec形式来设置相对稳定的默认命令和参数,然后使用任何形式的CMD指令来设置可能发生变化的参数。
FROM alpine
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
#当运行容器是,可以看到只有一个top进程在运行:
$ docker run -it --rm --name alpine:test top -H
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top
$ docker exec -it alpine:test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux


# 3.ENTRYPOINT指令的shell格式
#通过为ENTRYPOINT指定文本格式的参数,此参数将在`/bin /sh -c` 中进行执行
#该形式将使用shell处理而不是shell环境变量,并且将忽略任何的CMD或docker run运行命令行参数。
FROM alpine
ENTRYPOINT exec top -b

补充说明:

1.ENTRYPOINT 指令也可以结合一个辅助脚本使用,和前面命令行风格类似,即使启动工具需要不止一个步骤。

2.通过ENTRYPOINT指令可以将容器设置作为可执行的文件

注意:当指定多个ENTRYPOINT时候只有最后一个生效;

VOLUME- 创建本地主机或其他主机挂载点-定义匿名卷

描述:

指令用于暴露任何数据库存储文件,配置文件,或容器创建的文件和目录。强烈建议使用 VOLUME来管理镜像中的可变部分和用户可以改变的部分。

  • VOLUME指令只是起到了声明了容器中的目录作为匿名卷,但是并没有将匿名卷绑定到宿主机指定目录的功能;
  • 镜像run了一个容器的时候,docker会在安装目录下的指定目录下面生成一个目录来绑定容器的匿名卷(这个指定目录不同版本的docker会有所不同)
1
2
3
4
5
6
# 基础示例:
VOLUME ["<路径1>", "<路径2>"...]

# 实际示例:
#一般用来存放数据和需要保持的数据等,在运行的时候我们就可以利用 -v /Store/data(宿主机):/date(容器),数据就可以直接存在在宿主机上面;
VOLUME ["/data"] 作者:WeiyiGeek https://www.bilibili.com/read/cv15220707 出处:bilibili

USER- 指定容器运行时名用户名或者UID

描述:

如果某个服务不需要特权执行,建议使用 USER 指令切换到非 root 用户。

注意事项:

1.在镜像中用户和用户组每次被分配的 UID/GID 都是不确定的,下次重新构建镜像时被分配到的 UID/GID 可能会不一样。
如果要依赖确定的 UID/GID 你应该显示的指定一个 UID/GID。

1
2
3
4
# 方式1
RUN groupadd -r postgres -g 1001 && useradd -r -g postgres postgres -u 1001
# 方式2
RUN useradd -r -u 1001 -U postgres

2.应该避免使用 sudo 命令因为它不可预期的 TTY 和信号转发行为可能造成的问题比它能解决的问题还多。如果你真的需要和 sudo 类似的功能(例如以 root 权限初始化某个守护进程,以非 root 权限执行它)你可以使用 gosu 命令; 最后为了减少层数和复杂度,避免频繁地使用 USER 来回切换用户。

1
2
3
4
5
6
7
8
# 基础示例
#(1) 当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在之前创建所需要的用户
USER daemon

#(2)要临时获取管理员权限可以使用gosu而不使用sudo;
#USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。
RUN groupadd -r postgres && useradd -r -g postgres postgres
USER postgres

WORKDIR - 配置工作目录

描述:为了清晰性和可靠性,你应该总是在WORKDIR中使用绝对路径。另外你应该使用 WORKDIR 来替代类似于 RUN cd … && do-something 的指令,后者难以阅读、排错和维护。

1
2
3
4
5
6
7
8
9
# 基础示例
#(1)为后续的RUN CMD ENTRYPOINT 指令配置工作目录
WORKDIR /path/to/workdir

#(2)使用多个WORKDIR指令,如果后续命令参数是相对路径,则会基于首个绝对路径进行拼接。
WORKDIR /a
WORKDIR b
WORKDIR C
RUN pwd #最终路径是/a/b/c

ONBUILD - 为他人做嫁衣裳

描述:ONBUILD是一个特殊的指令在当前镜像构建时并不会被执行。**只有当以当前镜像为基础镜像去构建下一级镜像的时候才会被执行**。它后面跟的是其它指令比如 RUN, COPY 等

Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。

STOPSIGNAL - 指定所创建镜像启动的容器接收退出的信号值

描述: 该指令设置将发送到的系统调用信号容器退出,如果不定义信号名称默认是 SIGTERM。
此信号可以是格式中的 SIG , 例如 SIGKILL,或与 例如内核的系统调用表 9

1
2
# 实际示例
STOPSIGNAL signal

SHELL - 指定其他命令执行时默认使用shell的类型

描述:

该指令允许使用 shell 形式覆盖命令,

  • Linux中默认的Shell是[“/bin/sh”, “-c”]
  • Windows中默认的Shell是[“cmd”, “/S”, “/C”]
  • 如果Linux中还存在备用的shell例如(zsh、csh、tcsh),我们也可以采用此种方法指定。
1
2
3
4
5
6
7
8
9
10
# 实际示例
SHELL ["/bin/sh","-c"]
SHELL ["powershell","-command"]

# 实践使用示例
FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'

HEALTHCHECK - 健康检查

描述:该命令设置检查容器健康状况的命令,它与 kubernetes 中的 Pod 探针类似

在没有 HEALTHCHECK 指令前Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常。很多情况下这没问题,但是如果程序进入死锁状态,或者死循环状态,应用进程并不退出,但是该容器已经无法提供服务了。

在 1.12 以前,Docker 不会检测到容器的这种状态,从而不会重新调度,导致可能会有部分容器已经无法提供服务了却还在接受用户请求。

从 Docker 1.12 引入该指令HEALTHCHECK 指令是告诉 Docker 应该如何进行判断容器的状态是否正常,从而比较真实的反应容器实际状态。

当在一个镜像指定了 HEALTHCHECK 指令后,用其启动容器,初始状态会为 starting,在 HEALTHCHECK 指令检查成功后变为 healthy,如果连续一定次数失败,则会变为 unhealthy。

1
2
3
4
5
6
7
8
9
10
# 基础语法
#命令的返回值决定了该次健康检查的成功与否:0:成功;1:失败;2:保留,不要使用这个值。
HEALTHCHECK [选项] CMD <命令>
# 选项:
--interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
--timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间本次健康检查就被视为失败默认 30 秒;
--retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。

# 如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK NONE

Docker容器文件系统

  • Docker 镜像代表了容器的文件系统里的内容,是容器的基础,镜像一般是通过 Dockerfile 生成的
  • Docker 的镜像是分层的,所有的镜像(除了基础镜像)都是在之前镜像的基础上加上自己这层的内容生成的
  • Docker 中每一层镜像的元数据都是存在 json 文件中的,除了静态的文件系统之外,还会包含动态的数据
  • Docker 镜像生产容器后会在此基础之上加入挂载点到安装Docker宿主机文件系统之中,并提供一个读写层(Read-Write Layer),所以容器进程的所有操作都在读写层进行

1df77b34c33c40f050fe76c25004e8599fdc09e6.png@942w_581h_progressive

Volume 数据卷

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

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

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

容器中管理数据的主要两种方式

  • 数据卷: Data Volumes
  • 数据卷容器: Data Volume Dontainers

数据管理共享的方式

  • 使用数据卷容器在容器和主机
  • 容器和容器之间共享数据

数据卷命令行

使用 --mount 标记可以指定挂载一个本地主机的目录到容器中去

本地目录的路径必须是绝对路径,以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。

Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读

1
2
3
4
5
$ docker run -d -P \
--name web \
# -v /src/webapp:/usr/share/nginx/html \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly\
nginx:alpine

--mount 标记也可以从主机挂载单个文件到容器中

1
2
3
4
5
6
7
8
9
$ docker run --rm -it \
# -v $HOME/.bash_history:/root/.bash_history \
--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
ubuntu:18.04 \
bash

root@2affd44b4667:/# history
1 ls
2 diskutil list

?: mount和–volumes-from的关系

1
2
3
4
5
6
7
8
#创建一个数据卷
$ docker volume create my-vol

#查看所有数据卷
$ docker volume ls

#查看指定数据卷的信息
$ docker volume inspect my-vol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#### 创建一个web容器并创建一个数据卷挂载到容器的/webapp目录下(默认将宿主机/根映射到容器中webapp目录中)
$ sudo docker run -d -P 5000 --name web(新建容器) -v /webapp(容器目录) training/webapp(镜像) python app.py
# --name 指定容器的名称
# -v:将镜像的存放位置放在本地指定的路径上.
# -P:是允许外部访问容器需要暴露的port
# -d:是容器的后台运行守护

#cp 把容器文件copy到宿主机,或者把宿主机的文件copy到容器
#把容器的1.php拷贝到宿主机家目录
$docker cp 容器id或者name:/home/wwwroot/1.php /home/Lcy/
#把宿主机的config.php拷贝到容器
$docker cp config.php 容器id或者name:/home/wwwroot/



#挂载主机目录/文件作为数据卷:
#挂载一个主机目录作为数据卷 /src/webapp[主机目录]:/opt/webapp[容器目录]:rw (权限)
$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:rw training/webapp python app.py
# Docker挂载数据卷的默认权限 rw,ro[只读] //加入ro后数据卷的数据就无法修改了

#挂载一个本地文件作为数据卷(注意再挂载文件得时候尽量设置ro自读,防止inode不一致报错)
$ sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
# --rm :当它退出自动移除容器 即docker ps -aq 不能查询到

数据卷容器命令行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#首先创建一个数据卷容器dbdata并在其中创建一个数据卷挂载到/dbdata上 
$ sudo docker run -it -v /dbdate --name dbdate ubuntu

#然后在容器中进行使用 创建两个容器db1 / db2
$ sudo docker run -it --volumes-from dbdate --name db1 ubuntu
$ sudo docker run -it --volumes-from dbdate --name db2 ubuntu
#使用--volumes-from参数所挂载数据卷的容器自身并不需要保持运行状态


#修改目录中其中任何一个文件,其他容器的该目录都会改变
#可以从已有挂载了容器卷的容器来挂载数据卷
$ sudo docker run -d --name db2 --volumes-from db1 tarining/postgresql
#删除挂载的容器(dbdata 、 db1 、db2)数据卷饼不会被自动的删除,必须在删除最后一个挂载着它容器时显示使用Docker rm -v 命令来指定同时删除关联的容器;


数据卷容器迁移数据

1
2
3
4
5
6
7
8
##可以利用数据卷容器对其中的数据卷进行备份、恢复以实现数据的迁移 
#备份: 创建一个worker容器 ,将本地当前目录挂载到容器中backup目录,进行选择数据目录备份压缩
$ sudo docker run --volumes-from dbdata -v $(pwd):/backup --name worker ubuntu tar cvf /backup/backup.tar /dbdate

#恢复: 首先创建一个带有数据卷的容器dbdata2
$ sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
#解压备份文件到挂载的数据卷中
$ sudo docker run --volumes-from dbdata2 $(pwd):/backup busybox tar xvf /backup/backup.tar

Z和z的区别

1
2
3
4
5
#配置selinux标签如果使用selinux,可以添加z或z选项来修改挂载到容器中的主机文件或目录的selinux标签
#:z选项指示绑定挂载内容在多个容器之间共享。
#:Z选项表示绑定挂载内容是私有和非共享的。
#重要:当使用绑定与服务挂载时,selinux标签(:z和:Z)以及:ro将被忽略,设置了z选项以指定多个容器可以共享绑定挂载的内容,此时不能使用——mount标记修改selinux标签
docker run -d --restart=always --name app-v /disk/webapp/war/:/usr/local/tomcat/webapps:z -p 4081:8080 -e JAVA_OPTS=-Dsome.property=value -e Xmx=1536m tomcat-base:6.0.85-jre8

总结说明

  • 推荐直接挂载文件目录到容器中,如果直接挂载一个文件到容器中在使用文本编辑工具时候可能会报错;
  • 可以多次使用–volumes-from参数从来多个容器挂载多个数据卷;锁挂载的容器自身并不需要保持在运行状态
  • 推荐使用数据卷和数据容器之外的物理备份存储系统,如RAID或者分布式系统如Ceph,GPFS,HDFS等

联合文件系统(UnionFS)

联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。

联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。

Docker 中使用的 AUFS(Advanced Multi-Layered Unification Filesystem)就是一种联合文件系统。 AUFS 支持为每一个成员目录(类似 Git 的分支)设定只读(readonly)、读写(readwrite)和写出(whiteout-able)权限, 同时 AUFS 里有一个类似分层的概念, 对只读权限的分支可以逻辑上进行增量地修改(不影响只读部分的)。

Docker 目前支持的联合文件系统包括 OverlayFS, AUFS, Btrfs, VFS, ZFSDevice Mapper

在可能的情况下,推荐 使用 overlay2 存储驱动,overlay2 是目前 Docker 默认的存储驱动,以前则是 aufs。你可以通过配置来使用以上提到的其他类型的存储驱动。

Question:存储驱动

Question:engine

Question: 容器格式

Docker Compose (需要加强学习)

Compose 定位是 「定义和运行多个 Docker 容器的应用(Defining and running multi-container Docker applications)

Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责实现对 Docker 容器集群的快速编排。

它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)

Docker Compose重要概念

  • 服务 (service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
  • 项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

Compose 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。

Compose 项目由 Python 编写,实现上调用了 Docker 服务提供的 API 来对容器进行管理。因此,只要所操作的平台支持 Docker API,就可以在其上利用 Compose来进行编排管理。

Docker Machine

Docker Machine 是一种可以让您在虚拟主机上安装 Docker 的工具,并可以使用 docker-machine 命令来管理主机。

Docker Machine 也可以集中管理所有的 docker 主机,比如快速的给 100 台服务器安装上 docker。

Docker Machine 管理的虚拟主机可以是机上的,也可以是云供应商,如阿里云,腾讯云,AWS,或 DigitalOcean。

使用 docker-machine 命令,您可以启动,检查,停止和重新启动托管主机,也可以升级 Docker 客户端和守护程序,以及配置 Docker 客户端与您的主机进行通信。

image-20220227111032314

容器连接

https://www.bilibili.com/read/cv15185166?spm_id_from=333.999.0.0

Question:网络连接的原理

实现通过端口连接到一个docker容器

网络端口映射

  • -P :是容器内部端口随机映射到主机的端口。
  • -p : 是容器内部端口绑定到指定的主机端口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#创建了一个 python 应用的容器
runoob@runoob:~$ docker run -d -P training/webapp python app.py
# 另外,我们可以指定容器绑定的网络地址,比如绑定 127.0.0.1。
#我们使用 -P 绑定端口号,使用 docker ps 可以看到容器端口 5000 绑定主机端口 32768
runoob@runoob:~$ docker ps
CONTAINER ID IMAGE COMMAND ... PORTS NAMES
fce072cc88ce training/webapp "python app.py" ... 0.0.0.0:32768->5000/tcp grave_hopper
#我们也可以使用 -p 标识来指定容器端口绑定到主机端口。
#-P :是容器内部端口随机映射到主机的端口。
#-p : 是容器内部端口绑定到指定的主机端口。
runoob@runoob:~$ docker run -d -p 5000:5000 training/webapp python app.py
33e4523d30aaf0258915c368e66e03b49535de0ef20317d3f639d40222ba6bc0
runoob@runoob:~$ docker ps
CONTAINER ID IMAGE COMMAND ... PORTS NAMES
33e4523d30aa training/webapp "python app.py" ... 0.0.0.0:5000->5000/tcp berserk_bartik
fce072cc88ce training/webapp "python app.py" ... 0.0.0.0:32768->5000/tcp grave_hopper
#另外,我们可以指定容器绑定的网络地址,比如绑定 127.0.0.1。
runoob@runoob:~$ docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py
95c6ceef88ca3e71eaf303c2833fd6701d8d1b2572b5613b5a932dfdfe8a857c
runoob@runoob:~$ docker ps
CONTAINER ID IMAGE COMMAND ... PORTS NAMES
95c6ceef88ca training/webapp "python app.py" ... 5000/tcp, 127.0.0.1:5001->5000/tcp adoring_stonebraker
33e4523d30aa training/webapp "python app.py" ... 0.0.0.0:5000->5000/tcp berserk_bartik
fce072cc88ce training/webapp "python app.py" ... 0.0.0.0:32768->5000/tcp grave_hopper
#这样我们就可以通过访问 127.0.0.1:5001 来访问容器的 5000 端口。

#docker port 命令可以让我们快捷地查看端口的绑定情况
runoob@runoob:~$ docker port adoring_stonebraker 5000
127.0.0.1:5001

上面的例子中,默认都是绑定 tcp 端口,如果要绑定 UDP 端口,可以在端口后面加上 /udp

Question: udp?

容器的链接(Linking)系统是除了端口映射外的另一种可以与容器中应用进行交换的方式;它会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息

Docker两种方式为容器公开连接信息:

  • 环境变量
  • 更新/etc/hosts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#首先创建一个新的数据库容器,启动db容器的时候并没有使用-p与-P标记,避免了暴露数据库端口到外部网络上
$sudo docker run -d --name db tranining/postgres

#使db容器与web容器建立互联关系;
#--link name:alias 其中namd是链接的容器的名称,alias是这个链接的别名.
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

#使用 docker ps 来查看容器的连接
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STAT
US PORTS NAMES
349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/db'
#web/db这表示web 容器链接到 db 容器,web 容器将被允许访问 db 容器的信息。


#使用env命令来查看web容器的环境变量
$sudo docker run --rm --name web1 --link db:db training/webapp env
#起点汇总DB_开头的环境变量是提供web容器连接到db使用,前缀采用大写的链接别名
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5000_TCP=tcp://172.17.0.5:5432
DB_PORT_5000_TCP_PROTO=tcp
DB_PORT_5000_TCP_PORT=5432
DB_PORT_5000_TCP_ADDR=172.17.0.5


#同时Docker还添加host信息到父容器的/etc/hosts文件
$sudo docker run -it --rm --link db:db training/webapp /bin/bash
cat /etc/hosts

跨主机实现互通

还可以通过通过添加路由route的方式进行跨主机实现互通,但是通常情况下我们不会如此操作,只在特殊环境中使用测试。

比如:Docker1: 172.18.0.1/24 <--> Gateways <--> 192.168.1.99

总结

用户可以链接多个子容器到父容器中比如连接多个web到db容器上;

学习额外的机制比如SDN(软件定义网络)或者NFV(网络功能虚拟化)的相关技术

容器互联

端口映射并不是唯一把 docker 连接到另一个容器的方法。

docker 有一个连接系统允许将多个容器连接在一起,共享连接信息。

docker 连接会创建一个父子关系,其中父容器可以看到子容器的信息。

随着 Docker 网络的完善,强烈建议大家将容器加入自定义的 Docker 网络来连接多个容器,而不是使用 --link 参数。

https://www.bilibili.com/read/cv15185166?spm_id_from=333.999.0.0

Question: bridge 和 overlay

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#创建一个新的Docker网络
$ docker network create -d bridge my-net
#-d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode

#连接容器
#运行一个容器并连接到新建的my—net网络
$ docker run -it --rm --name busybox1 --network my-net busybox sh
#打开新的终端,再运行一个容器并加入到my-net网络
$ docker run -it --rm --name busybox2 --network my-net busybox sh

#可以再打开一个终端查看容器信息
$ docker container ls

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b47060aca56b busybox "sh" 11 minutes ago Up 11 minutes busybox2
8720575823ec busybox "sh" 16 minutes ago Up 16 minutes busybox1

#接下来通过ping来证明busybox1容器和busybox2容器建立了互联关系
#在busybox1容器输入以下命令
/ # ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms
#用ping来测试链接 busybox2 容器,他就会解析成172.19.0.3
#同理在 busybox2 容器执行 ping busybox1,也会成功连接到。
/ # ping busybox1
PING busybox1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms
#这样,busybox1 容器和 busybox2 容器建立了互联关系

Question:

Question: swarm集群要不要讲

Question:存储驱动

Question:engine

Question: 容器格式


后续

kerbernets

docker网络

挂载


提问

最大区别:

进程/程序

​ 轻量,简单