目录

Docker

容器是一个标准的软件单元,它打包代码及其所有依赖项,以便应用程序从一个计算环境快速可靠地运行到另一个计算环境。Docker 容器镜像是一个轻量级的、独立的、可执行的软件包,包括运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。

  • 快速、一致地交付您的应用程序
  • 响应式部署和扩展
  • 在相同硬件上运行更多工作负载

容器与虚拟机

/images/docker/docker-vm.png

容器是应用程序层的抽象,将代码和依赖项打包在一起。多个容器可以在同一台计算机上运行,并与其他容器共享OS内核,每个容器在用户空间中作为隔离的进程运行。容器占用的空间少于VM。

虚拟机(VM)是将一台服务器转变为多台服务器的物理硬件的抽象。虚拟机管理程序允许多个VM在单台计算机上运行。每个VM包含操作系统,应用程序,必要的二进制文件和库的完整副本-占用数十GB。VM也可能启动缓慢。

特性 容器 虚拟机
启动 秒级 分钟级
硬盘使用 一般为MB 一般为GB
性能 接近原生 弱于原生
系统支持量 单机支持上千个容器 一般几十个

/images/docker/architecture.svg

Docker 引擎

Docker Engine 是一个服务端-客户端结构的应用,主要有这些部分:Docker守护进程、Docker Engine API、Docker客户端。

/images/docker/docker-engine.png

  • Docker 守护进程 (Docker daemons),也叫 dockerd ,是一个持久化的进程,用户管理容器。守护进程会监听 Docker Engine API 的请求。
  • Docker Engine API 是用于与Docker守护进程交互用的的API。它是一个RESTful API,因此它不仅可以被Docker客户端调用,也可以被wget 和 curl等命令调用。
  • Docker 客户端,也叫 docker,是大部分用户与Docker交互的主要方式。用户通过客户端将命令发送给守护进程。命令会遵循 Docker Engine API

/images/docker/docker-commands-diagram.png

Docker 注册中心

Docker registry 是用于存储Docker的镜像。Docker Hub 是一个官方公共的注册中心, 是最重要、最常用的 image 仓库,任何人都可以使用,默认配置下,Docker将会在这里寻找镜像。另外,用户可以自行构建私有注册中心。

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

1
2
3
4
5
6
7
8
# 官方提供可以用于构建私有的镜像仓库。
docker run -d \
    -p 5000:5000 \
    -v /opt/data/registry:/var/lib/registry \
    registry
# docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]
docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest
docker push 127.0.0.1:5000/ubuntu:latest

因为 Docker 默认不允许非 HTTPS 方式推送镜像。我们可以通过 Docker 的配置选项来取消这个限制

1
2
3
4
5
{
  "insecure-registries": [
    "10.0.0.100:5000"
  ]
}

最新版本 Nexus3.x 全面支持 Docker 的私有镜像。一个软件来管理 Docker , Maven , Yum , PyPI 等是一个明智的选择。

Docker objects

Docker的对象是指Images、Containers、Networks、Volumes、Plugins等等。

镜像 Image

镜像是一个只读模板,用于指示创建容器。 镜像分层(layers)构建的,而定义这些层次的文件叫 Dockerfile。

1
2
3
4
5
6
7
# 列出已经下载的镜像 docker image ls
docker images
# 拉取 redis:latest 最新镜像 一般 docker run 自动拉取
docker pull redis
docker pull redis:6.0
# 删除本地镜像
docker rmi redis:6.0

由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。

1
2
# 查看镜像、容器、数据卷所占用的空间
docker system df

旧镜像名称被取消,从而出现仓库名、标签均为 <none> 的镜像。这类无标签镜像也被称为 虚悬镜像 (dangling image)

1
2
docker image ls -f dangling=true
docker image prune

容器 Container

容器是镜像的可运行的实例。容器可通过API或CLI(命令行)进行操控。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 在新的容器运行命令 如果本地不存在 redis 镜像 会先下载镜像,然后 docker create 创建容器  docker start 启动容器
docker run --name some-redis -p 6379:6379 -d redis
# 查看运行中的容器
docker ps

docker ps -a
# 停止容器
docker stop some-redis
# 删除容器
docker rm some-redis
# 删除所有终止容器
docker container prune

网络

1
2
3
4
5
6
7
8
# The docker network command
docker network
# List networks
docker network ls
# Inspect a network
docker network inspect bridge
# List network driver plugins
docker info
Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay

bridge 桥接

网桥网络 适用于容器运行在相同地Docker守护进程的主机上。不同Docker守护进程主机上的容器,它们之间的通信需要依靠操作系统层次的路由,或者你应该使用 overlay网络 进行代替。

bridge 是网桥驱动,是Docker默认的网络驱动(接口名为 docker0) 当你不为容器指定一个网络时候,Docker将会使用该驱动。

host 主机模式

host 用于单独容器,该网络下容器只能和Docker主机进行直接连接。host 只适用于 Docker 17.06或以上版本的swarm服务。

host网络和VirtualBox的 仅主机网络(Host-only Networking) 类似。

overlay 覆盖网络

overlay (中文:覆盖网络)网络驱动将会创建分布式网络,该网络可以覆盖若干个 Docker守护进程主机。该网络是基于 主机特定网络(host-specific networks),允许 swarm服务 和 容器 进行安全通信(当加密功能开启时)。在该网络下,Docker能够清晰地掌握 数据包的路由 以及 发送接收容器。

overlay 有两种网络类型网络:

  • ingress 网络,可掌控 swarm服务 的网络流量 。该网络是 overlay 的默认网络。
  • docker_gwbridge 网络是 网桥网络。该网络会将 单独的Docker守护进程 连接至 swarm里的另外一个守护进程。

overlay 网络下,单独的容器 和 swarm服务 的行为和配置概念 是不一样。

该策略不需要 容器们 具有操作系统级别的路由,因为Docker负责路由。

macvlan

macvlan 网络允许你赋予容器 MAC地址 ,在该网络里,容器会被认为是物理设备。Docker守护进程会路由

none

该策略下,容器不使用任何网络。none 常常用于连接自定义网络驱动的情况下。

Volume 数据卷

  • 数据卷 可以在容器之间共享和重用
  • 对 数据卷 的修改会立马生效
  • 对 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 创建一个数据卷
docker volume create vol-demo
# 查看所有的 数据卷
docker volume ls
# 查看指定 数据卷 的信息
docker volume inspect vol-demo
# 删除 数据卷
docker volume rm vol-demo
# 清楚无主的数据卷
docker volume prune

容器有两种永久化存储方式:卷(volumes)和 绑定挂载(bind mounts)。另外,Linux用户还可使用 tmpfs 进行挂载;Window用户还可以使用 命名管道(named pipe)。在容器中,不管是哪种永久化存储,表现形式都是一样的。

Docker Desktop

Docker Desktop 是一个易于安装的应用程序,适用于您的 Mac 或 Windows 环境,使您能够构建和共享容器化应用程序和微服务。Docker Desktop 包括 Docker 守护程序 ( dockerd)、Docker 客户端 ( docker)、Docker Compose、Docker Content Trust、Kubernetes 和 Credential Helper。

容器互联

可以使用 –link 参数来使容器互联,强烈建议大家将容器加入自定义的 Docker 网络来连接多个容器。

1
docker network create -d bridge my-net

-d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode

1
2
3
4
docker run -it --rm --name busybox1 --network my-net busybox
# 另外打开一个终端窗口
docker run -it --rm --name busybox2 --network my-net busybox
ping busybox1

Docker Compose

Docker Compose 是 Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。

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

Compose 中有两个重要的概念:

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

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

Compose文件 是一个YAML文件,定义了服务(service)、网络、卷(volume)。

  • 服务(service)定义 各容器的配置,定义内容将以命令行参数的方式 传给 docker run 命令。
  • 网络(network),类似地,将定义内容传给 docker network create 命令 。
  • 卷(volume),类似地,将定义内容传给 docker volume create 命令。

Dockerfile

Docker有两种文件格式,Dockerfile和Compose file。Dockerfile定义了单个容器的内容和启动时候的行为。Compose file定义了一个多容器应用。

Docker 可以依照 Dockerfile 的内容,自动化地构建镜像。 Dockerfile 是包含着用户想要如何构建镜像的所有命令的文本。

1
2
3
4
FROM openjdk:17.0.2
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
1
2
docker build -t springio/gs-spring-boot-docker .
docker run -p 8080:8080 springio/gs-spring-boot-docker

关键词:

  • RUN。RUN会在当前镜像的顶层上添加新的一层(layer),并在该层上执行命令,执行结果将会被提交。提交后的结果将会用于Dockerfile的下一步。
  • ENTRYPOINT,入口点。ENTRYPOINT允许你配置容器,使之成为可执行程序。即,ENTRYPOINT允许你为容器增加一个入口点。ENTRYPOINT和CMD类似,均在容器启动时执行,但是ENTRYPOINT为了提供稳定且不可被覆盖的操作。[29]通过在命令行中指定–entrypoint 命令的方式,可在运行时将Dockerfile文件中的ENTRYPOINT覆盖。
  • CMD,是command的缩写。CMD用于为已创建的镜像提供默认的操作,当不想要用默认操作时候,可用docker run IMAGE[:TAG|@DIGEST] [COMMAND] 进行替换 。但当Dockerfile拥有入口点时,CMD用于赋予入口点参数。

Best practices for writing Dockerfiles

命令行参数

1
docker --help
1
docker run --help
1
2
3
4
5
6
7
8
--name string                    Assign a name to the container
-p, --publish list                   Publish a container's port(s) to the host
-v, --volume list                    Bind mount a volume
--network network                Connect a container to a network
-i, --interactive                    Keep STDIN open even if not attached
-d, --detach                         Run container in background and print container ID
-t, --tty                            Allocate a pseudo-TTY
--rm                             Automatically remove the container when it exits
  • –rm 退出容器以后,这个容器就被删除了,方便在临时测试使用。
1
2
# 进入容器 从这个 stdin 中 exit,不会导致容器的停止。但 docker attach 会
docker exec -it 775c7c9ee1e1 /bin/bash

底层技术

Docker 是用 Go 编程语言编写的,并利用 Linux 内核的几个特性来提供其功能。Docker 使用一种称为 namespaces 的技术来提供隔离的工作空间(又称容器)。当您运行容器时,Docker 会为该容器创建一组 namespaces (命名空间)。

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

Docker底层的核心技术包括Linux上的Namespace(命名空间),Cgroups(控制组,control groups),UFS(union file system)和Container format(容器格式)

虽然共用一个内核和某些运行时环境(例如一些系统命令和系统库),但是彼此却看不见,都以为系统中只有自己的存在。这种机制就是容器(Container),利用Namespace(命名空间)来做权限的隔离控制,利用Cgroups(控制组,control groups)来做资源分配。

附录