一、Docker 概述
1. Docker 架构与核心概念
- Docker uses a client-server architecture. The Docker client talks to the Docker daemon, which does the heavy lifting of building, running, and distributing your Docker containers. The Docker client and daemon can run on the same system, or you can connect a Docker client to a remote Docker daemon. The Docker client and daemon communicate using a REST API, over UNIX sockets or a network interface. Another Docker client is Docker Compose, that lets you work with applications consisting of a set of containers.
- 镜像:一个只读的文件和文件夹组合,它包含了容器运行时所需要的所有基础文件和配置信息,是容器启动的基础
- 容器:容器是镜像的运行实体,带有运行时需要的可写文件层。容器运行着真正的应用进程,其本质还是主机上运行的一个进程,但是有自己独立的命名空间隔离和资源限制。容器有初建、运行、停止、暂停和删除五种状态
- 仓库:用来存储和分发 Docker 镜像,分为公共仓库(如 Docker Hub)和私有仓库(如 Harbor)
2. 容器运行时的发展
- OCI(Open Container Initiative,开放容器标准)规范由 Docker 倡导和提议,联合 CoreOS 等多家厂商,于 2015 年 6 月 22 日发起。OCI 主要包括三个部分:
- runtime-spec:容器运行时标准,定义了容器运行的配置,环境和生命周期。即如何运行一个容器,如何管理容器的状态和生命周期,如何使用操作系统的底层特性(namespace,cgroup,pivot_root 等)
- image-spec:容器镜像标准,定义了镜像的格式,配置(包括应用程序的参数,环境信息等),依赖的元数据格式等,简单来说就是对镜像的静态描述
- distribution-spec:镜像分发标准,即规定了镜像上传和下载的网络交互过程
- 容器运行时所做的工作(简单来说就是容器的全生命周期管理)
- 拉取镜像:如果本地没有镜像,从镜像仓库拉取
- 镜像解压缩:镜像被解压缩到一个 COW(copy-on-write,写时复制)的文件系统上,所有容器层相互堆叠,最终合并成一个文件系统
- 为容器准备一个挂载点
- 设置容器:从镜像中获取元数据信息,并对容器进行设置,保证容器能按照期望的方式正常启动,设置过程中还包括来自用户的输入,比如 CMD,ENTRYPOINT 或程序启动的入参等
- 设置 namespaces:内核为容器设置相关隔离,如文件系统,进程,网络,IPC 等
- 设置 cgroups:内核为容器设置资源使用限制,如 CPU,memory 等
- 通过系统调用启动容器
- 设置 SELinux/AppArmor,即为容器设置相关安全策略
- 在 Docker 之前,其实早就有了类似的容器技术,就是 LXC(LinuX Containers,Linux 容器)。2008 年发布的 Linux 2.24 内核首次引入了 Cgroups 技术,而在同一时间,Linux 就发布了 LXC
- LXC 是一个操作系统级的虚拟化方式,对 Cgroups 和 Namespaces 等 Linux 内核特性进行封装,然后提供一个统一的接口,降低用户使用容器技术的门槛
- 技术层面确实如此,但设计理念却完全不同:LXC 是封装系统的轻量级虚拟机,而 Docker 则是封装应用
- Docker 依靠统一的内核,从应用的角度出发,每个容器默认只跑一个程序。比如部署 LAMP,那就是 4 个容器,然后再将其组合起来(而不是将环境作为一个整体交付)
- Docker 在 v1.10 版本之前是直接利用 LXC 来实现容器隔离的。但到了 2014 年,Docker 公司利用 Go 语言开发了新的底层驱动 libcontainer,从而越过 LXC 直接操控 Namespaces 和 Cgroups。如此一来,Docker 便能直接与系统内核打交道,LXC 也就在 Docker 的崛起中被其淘汰了
- 2015 年,Docker 又带头搞出了 OCI 标准。为了推动 OCI 标准的落地,Docker 进一步向前演进自身的架构
- 先将 libcontainer 独立出来,重构成了 runc 项目,并将其捐献给了 Linux 基金会
- 由 runc 来负责底层容器的生成和运行,即直接操控 Namespaces 和 Cgroups 等内核特性。官方还特地说明,runc 是一个底层工具,不适合终端用户直接操作,最好配合高层次的容器软件使用(containerd)
- Docker 继续重构 Docker daemon 子系统,将其中与容器运行时交互的部分抽象为 containerd 项目,并将其捐献给了 CNCF
- containerd 负责主机上容器的生命周期管理:包括镜像的传输和存储,容器的执行、监管、日志、构建、网络等功能。同样,它也不适合开发人员直接使用,而是嵌入到更大的系统中
- 实际上,containerd 运行后,内部会创建一个 containerd-shim 进程,与 runc 搭配使用。可以运行一个 Docker 容器后用 ps 看到
- 先将 libcontainer 独立出来,重构成了 runc 项目,并将其捐献给了 Linux 基金会
二、Docker 安装
1 | $ yum remove docker \ |
- 配置 Docker 拉取镜像时使用的代理(虚拟机内部需要开启 Clash 的“局域网连接”功能)
1 | $ vim /etc/systemd/system/docker.service.d/proxy.conf |
三、Docker 基本操作
1. 镜像操作
- 镜像标识:
[Registry]/[Repository]/[Image]:[Tag]
Registry
:注册服务器,默认docker.io
,也可以为自建镜像仓库服务器Repository
:镜像仓库,默认library
,通常把一组相关联的镜像归为一个镜像仓库Image
:镜像名称Tag
:镜像标签,默认latest
1 | # 拉取镜像 |
- Dockerfile 常用指令:
指令 | 描述 |
---|---|
FROM | Dockerfile 除了注释第一行必须是 FROM,后面跟镜像名称,代表要基于该基础镜像构建容器 |
RUN | RUN 后面跟一个具体的命令 |
ADD | 拷贝本机文件或者远程文件到镜像内 |
COPY | 拷贝本机文件到镜像内 |
USER | 指定容器启动的用户 |
ENTRYPOINT | 容器的启动命令 |
CMD | CMD 为 ENTRYPOINT 指令提供默认参数,也可以单独使用 CMD 指定容器启动参数 |
ENV | 指定容器运行时的环境变量,格式为 key=value |
ARG | 定义外部变量,构建镜像时可以使用 build-arg= 的格式传递参数用于构建 |
EXPOSE | 指定容器监听的端口,格式为 [port]/tcp 或者 [port]/udp |
WORKDIR | 为跟在其后的所有 RUN、CMD、ENTRYPOINT、COPY 和 ADD 命令设置工作目录 |
2. 容器操作
1 | # 创建容器(未启动) |