Docker 开篇 从 0 到 1

一个不注意小事情的人,永远不会成功大事业

Docker 的资源限制和隔离性是云上必不可少的特性

在这里插入图片描述

那么这么多的应用部署到一起就需要解决三个主要问题: 应用隔离、应用部署和资源限制。

应用隔离

很容易理解,比如 不同的应用依赖了冲突的基础软件包,如果都直接部署在操作系统环境下,必然会引起冲突。
这个时候就需要应用之间互相隔离。 Docker 使用操作系统底层的 Namespace 技术来做隔离是一种主流的技术方案。

应用部署

在容器技术诞生之前,所有的应用都是 直接部署在操作系统上面的,彼此之间共享底层的操作系统资源,比如内存、 CPU 、磁盘等。

打个比方,如果我们要将应用 A 和应用 B 部署到同一台机器上,那么这两个应用需要的环境信息,我们都需要满足。如果应用 A 和 B 的环境依赖之间存在冲突,或者说不兼容,那么管理起来就会非常的困难。

而这个问题,我们通过容器的镜像技术却可以非常简单地解决掉。除此之后, Docker 技术还有很多相比于传统技术更加优势的地方,这也 是Docker 技术能够迅猛发展起来的根本原因。

资源限制

不同的应用共享集群不可避免的涉及到资源使用限制的问题。比如一个公司同时有多个业务部门使用一个统一的集群,在统计研发成本时,需要统计各个部门的资源使用情况然后做划分。这样我们就可以根据资源使用限制来划分,当某些应用资源使用超限就杀掉应用。 Docker 使用内核提供的 Cgroup 技术来做资源限制正好可以应用到这个场景。

Kuberneters

前面介绍了 Docker 技术的一些理论知识和最佳实践,但是正如前面所说, Docker 技术从来都不是一个孤立的技术。 Docker 更多的还是作为一种技术基石,基于 Docker 我们还需要做一些包括容器编排管理等工作。

容器编排和集群管理系统

说到容器编排和集群管理系统,从早期的 docker-compose 到 swarm ,可以说是诸侯林立。但是自从 Kubernetes出现后慢慢的出现了大一统的趋势,凭借着 Google 内部早期积累的经验和优秀的云原生设计理念, Kubernetes 可以说是一骑绝尘,目前已经全面占领了云端统一管理的地位。

第一章: Docker 基础

第二章: Docker 核心技术
第三章: Docker 最佳实践
第四章: 云原生容器技术 Kubernetes
第五章: 云原生监控方案 Prometheus

Docker 核心优势其实是它的镜像技术

Docker 确实是也是使用 NameSpace 和 CGroup 技术没有错,但是 Docker 相比于各种 PaaS 云平台技术的核心优势其实是它的镜像技术。正是利用镜像技术, Docker 在诞生之后的几个月在各大 PaaS 软件还没有反应过来的时候迅速占领市场的有力地位.

Docker 解决的痛点!

难以维护,打包步骤繁琐

PaaS 软件在运行用户的应用上和 Docker 技术没有区别,但是在打包应用上却显得极为繁琐。以 Cloud Foundry为例, 它为不同的主流编程语言定义不同的打包方式,维护起来极其繁琐且容易出问题,用户往往也是因为这个环节苦不堪言。那么 Docker 镜像是如何解决这个问题呢?

Docker 镜像是一套操作系统文件 + 应用程序。镜像一般都会有一个 base 镜像,而这个 base 镜像一般都是操作系统或者其 mini 版本。这样如果我们的应用在云端是运行在 Centos 7.4 上面,那么我们的 base 镜像直接使用 Centos 7.4 的操作系统文件即可,这样就解除了用户要保持本地环境和云上环境的底层一致的心智负担。
简单来说, Docker 镜像的精髓是保证了环境的一致性。

除此之外, Docker 镜像技术是一套统一的技术,我们再也 不需要根据我们应用的开发语言不同而选择不同的打包方式。在 Docker 中,我们打包镜像使用的技术叫做 Dockerfile 技术,我们只需要按照镜像技术的规范去编写Dockerfile 即可,下面是一个简单的 Dockerfile 例子。

FROM quay.io/prometheus/busybox:glibc
LABEL maintainer="The Prometheus Authors <prometheus-developers@googlegroups.com>"

COPY node_exporter /bin/node_exporter

EXPOSE 9100
USER nobody
ENTRYPOINT [ "/bin/node_exporter" ]

其中第一行 FROM 就是引用基础镜像,这里的 busybox 就是一个精简版的操作系统镜像,相当于 Docker 镜像中hello world 。第 4 行将我们本地的可执行文件拷贝到镜像中;第 6 行和第 7 行设置端口和用户,最后一行设置应用的启动入口。

容器编排技术

对于规模稍微大一点应用,在生存环境中,需要发布的容器数量很可能极其庞大,这也就意味着如何管理容器之间的联系和拓扑结构并不是一件简单的事情。

举个例子,对于 一个成熟的 web 应用,首先要具备高可用架构,其次其内部可能包含数据库、缓存等各种依赖,除此之外还有很多运维管理需求,比如监控告警等。对于这些操作,通过人工来管理肯定是不现实的。这个时候我们就需要定义容器集群的编排和部署工具。

说到容器编排,不得不提三剑客: Compose 、 Machine 和 Swarm

compose

为了展示容器编排的内容,我们这里简单展示一下如何去部署一个 Flask 应用。 Flask 应用如下,定义一个route : “/” ,当访问到来的时候, redis 会做一个访问计数。

from flask import Flask
from redis import Redis
import os

app = Flask(__name__)
redis = Redis(host='redis', port=6379)

@app.route('/')
def hello():
redis.incr('hits')
return "Hello World. I am a Flask App!"

if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)

然后我们要生成一个 Docker 镜像,打包镜像只需要定义一个 Dockerfile ,如下:

From python:2.7
ADD . /code
WORKDIR /code
RUN pip install flask
RUN pip install redis

最后我们通过 compose.yml 来定义部署拓扑。

web:
build: .
command: python app.py
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redi

这里是编排部署的核心,这里定义了两个最高级别的 key: web 和 redis ,也就是说 compose 定义了由两个 ” 服务 “( web 和 redis )组成的 Docker ” 集群 “ 。这里 compose 部署的 docker 集群其实是在一台机器上的.通过 compose 定义完 Docker 集群之后,通过 docker-compose up 就可以将服务部署起来了。。

Machine

Machine 的主要功能是帮助用户在不同的云主机提供商上创建和管理虚拟机,并在虚拟机中安装 Docker 。除了搭建环境之外, Machine 还可以帮助用户配置 Docker 的连接参数等、管理 Docker 主机,比如启动、关闭、重启、删除等操作。

Machine 简单来说就是一个统一管理工具,成功的关键与否还是要看各大云厂商买不买单,接不接入。目前来看,情况不太乐观。

Swarm

Swarm 也是 Docker 集群的编排和管理工具,和 Compose 不同的是,这里的集群是真正的由多个主机组成的集群。
Docker 生态依托 Compose 、 Machine 和 Swarm 重新定义了一个容器生态的 PaaS.

CNCF ,全名 Cloud Native Computing Foundation ,由 Google 、 RedHat 等开源基础设施领域玩家们共同发起。这个基金会的目的其实很容易理解: 它希望以 Kubernetes 项目为基础,建立一个由开源基础设施领域厂商主导的、按照独立基金会方式运营的平台级社区,来对抗以 Docker 公司为核心的容器商业生态。而为了打造出这样一个围绕 Kubernetes 项目的 “ 护城河 ” , CNCF 社区就需要至少确保两件事情;
Kubernetes 项目必须能够在容器编排领域取得足够大的竞争优势;
CNCF 社区必须以 Kubernetes 项目为核心,覆盖足够多的场景。

Kubernetes 项目具有非常优秀的设计理念和健康的社区基础

Kubernetes 并不是凭空设计出来的,其前身来自 Google 内部的 Borg 和 Omega 系统。我们可以看一下下图,感受一下 Borg 和 Omega 系统在 Google 内部的位置。

在这里插入图片描述
关于优秀的设计理念,这里简单举一个例子: Pod 。

如果我们有两个容器 A 和 B 需要调度在一台机器上,其中 A 和 B 各需要内存 1 G ,但是这个时候机器只有 1.5G ,如果先调度 A , A 是可以被调度到机器上的,但是后面的 B 调度却会有问题了。要想解决这个问题,如果 A 和B 单独调度,往往需要考虑非常多的场景和复杂的解决算法。

Kubernetes 针对这个问题提出了 Pod 的概念, Pod 是一组容器的集合,类似进程组, Pod 是 Kubernetes 中一个最小的调度单位,也就是说同一个 Pod 中的不同容器一定会被调度到一台机器上,并且可以互相共享NameSpace 。 Pod 的提出解决了很多调度需要考虑的复杂问题.

声明式 API

所谓声明式 API ,是直接描述我们要什么或者要达到一种什么状态。与之对于是命令式 API ,命令式 API 对应的则是一个具体的动作:比如 “ 创建容器 ” , “ 修改容器 ” 等。

声明式 API 的一个典型例子就是 SQL ,在下面的 SQL 中,我们直接说明我们需要的东西是 a, b, c 三个字段,现在条件是 id > 123 。但是我们并不需要管这条 SQL 如果是数据库引擎中的真正的执行计划是什么样的.

select a, b, c from table where id > 123;

那么在 Kubernetes 中,我们如何去声明我们需要什么呢?下面是一个创建两个 nginx 副本的 Deployment 。其中的replicas 就是我们最终需要的副本数,也就是最终状态需要有两个副本运行, Kubernetes 如何去保证,我们不需要关心。

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx## 标题
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

声明式 API 的最大好处是解放开发者的心智负担,让编程变得简单

开放的生态

我们看一下下面的 Kubernetes 分层架构图。
在这里插入图片描述

在上图的最底部包含了很多开放生态,包括容器运行时,网络插件,存储插件、以及云厂商 Provider 。这样各大云厂商在将 Kubernetes 运行在自己的云上环境的时候就可以很方便的将自己的存储以及网络产品以一种插件式的方式集成到 Kubernetes 中。

Docker 是什么?

Docker 是一个开源的平台,我们可以用 Docker 来开发、部署和运行我们的应用程序。 Docker 可以帮助我们将应用程序和底层基础设施进行分离,以帮助我们更快的实现交付。通过 Docker 技术,我们可以像管理我们的应用一样管理我们的基础设施(比如基础依赖等,这里的具体技术其实就是镜像)。通过 Docker 技术,我们可以精简我们的整个开发和交互流程.

这里面的几个核心点包括:

平台: 镜像 + 容器 + daemon (管理镜像和 容器)

平台( platform ):官方将 Docker 定义为一个平台,但是其实更准确的来说, Docker 其实是一个技术栈,包括 Docker 镜像,可以类比为程序,是磁盘上面的静态文件,可以通过 Docker 来启动运行; Docker 容器,也就是 Docker 镜像的运行时实例; Docker Daemon ,用来管理机器上的镜像、容器等。

应用程序和底层基础设施分离

:传统的软件开发模式是我们先开发好我们的应用,然后在线上环境或者测试环境上先安装应用的基础依赖,比如像一些 C++ 的二进制文件会依赖一些操作系统的动态链接库。

依赖安装完成之后,然后将应用程序部署上去。使用 Docker 的镜像技术我们可以将这些依赖和应用程序都打包到镜像中,然后测试或者正式上线的时候只需要将整个镜像部署上去就可以了,不需要关心目标服务器上面的基础环境,这也就是应用程序和基础设施分离。也是精简开发流程的核心的所在。

熟悉 Docker 的同学肯定会意识到这里对 Docker 的定义其实少了很多东西,确实是这样的,比如 Docker 的隔离性和资源限制在定义里面都没有体现出来。定义还是从一种更加宏观的角度来介绍 Docker ,也没错.

Docker v.s. 虚拟机

很多人学习 Docker 的过程中都会看到有人把 Docker 拿来和虚拟机做对比,也就是下面这张图。左边是 Docker 的架构,右边是虚拟机的架构图。我们可以看到 Docker 和虚拟机的主要区别有:

所有的 Docker 应用共享一个宿主机操作系统,每个虚拟机有自己的操作系统;
每个 Docker 应用通过 Docker 层和宿主机的操作系统交互,而虚拟机应用直接和操作系统交互。

在这里插入图片描述
但是上图左边的图中的 Docker 的位置其实很不严谨,实际上 Docker 并不会像 Hypervisor 那样对应用进程的隔离环境负责,也不会创建任何实体的容器,真正对环境负责的是宿主机操作系统本身。所以上图中 Docker 的问题应该是处于靠边的位置,因为通过 Docker 启动的容器本质上和操作系统中运行的进程并没有本质的区别。

Docker 的应用场景

应用交付

Docker 技术为应用交付领域带来的最大的变化就是 开发环境的一致性。传统的开发方式需要开发者自己在本地进行开发,但是 本地的开发环境和远端的测试和正式环境还是存在差异,所以每次开发完成都需要反复比对环境的差异, 包括操作系统以及操作系统里面的依赖软件包是否齐全,非常的麻烦.

但是 使用 Docker 镜像,我们可以将所有的环境依赖都打包到镜像中,然后通过镜像来传输,这样会更加地高效。

试想下面几种场景:(非常常见)

1 开发者在本地编写代码进行开发,然后通过 Docker 镜像和其他协作者共享;
2 使用 Docker 技术将应用 push 到测试环境,自动触发自动化 test case ;
3 当开发者发现应用程序的 bug 时,可以在本地开发环境进行修复。修复完之后再将应用重新部署到测试环境进行测试验证;
4 当测试完成之后,需要给客户的环境升级,只要把修复完的应用镜像推送到客户可以访问的镜像中心即可

多版本混合部署

随着产品的不断更新换代, 一台服务器上部署同一个应用的多个版本在企业内部非常常见。但一台服务器上部署同一个软件的多个版本, 文件路径、端口等资源往往会发生冲突,造成多个版本无法共存的问题。

如果用 docker ,这个问题将非常简单。由于 每个容器都有自己独立的文件系统,所以根本不存在文件路径冲突的问题;对于端口冲突问题,只需要在启动容器时指定不同的端口映射即可解决问题。

内部开发测试环境

传统的开发测试环境都是由运维人员进行专门的环境配置而搭建出来的,而且需要运维人员进行专门维护。环境一旦出现问题,恢复起来也很麻烦。

借助于 Docker 技术,我们 将应用程序需要的依赖都固化到 Docker 镜像中,然后在对应的 Docker 容器中进行开发测试。就算环境出现问题,我们只要将当前容器删除重新启动即可恢复。

使用 Docker 镜像来维护内部开发测试环境还有另一个好处就是 DevOps ,传统的应用开发部署要跨两个团队: 开发团队负责开发,运维团队服务部署,一旦涉及到跨团队合作就要牵扯到沟通成本。而且开发作为应用的owner ,实际上对其依赖环境会更加的熟悉才对。

通过 Docker 镜像技术, 开发人员在开发应用的过程中就将这些依赖固化到镜像中。在环境部署环节,即使需要运维人员参与,也只是负责拉起 Docker 。整个过程都会更加的高效。

Docker 架构浅析

很多人说 Docker 是简单的 Server-Client 的架构,其实并不一定准确。 Docker 的架构比较复杂,并不是纯粹的只有 Server 和 Client 。下图是 Docker 架构的一个详细的图。几个主要的组成部分有。
Docker Client ;
Docker Daemon ;
Docker Registry 。
在这里插入图片描述

Docker Client

(人使用的 客户端,人机交互)
我们安装完 Docker 包之后,直接使用敲命令: docker ,界面是有提示的,这个 docker 就是 docker client 。
docker client 都是用来和 docker daemon 交互的。

Docker Daemon

docker daemon 是一个 docker 后台运行的守护进程,我们的 docker client 的命令就是和 Docker Daemon 来进行交互的。

Docker Registry

Registry 中文一般翻译为 注册中心,是用来 存储 Docker 镜像的地方。 Docker Registry 有多种不同的表现,比如Docker Hub 就是一个公开的注册中心,同时各大云厂商也提供了自己的注册中心,比如阿里云、腾讯云等,甚至你可以搭建自己的私有注册中心。

Docker 默认使用 Docker Hub ,比如我们执行 docker pull 时, Docker 默认去 Docker Hub 中寻找名字为 image-name 的镜像。如果使用自己的 Registry 需要进行单独的配置。

Docker Images

Image 一般中文称之为 镜像。官方对镜像的定义比较复杂,我一般使用类比的方式来理解镜像。 镜像可以理解成计算机系统中的程序,也就是静态的位于磁盘上面可以通过特定方式执行的文件(程序是操作系统可以识别的特定的二进制文件, Docker 镜像是可以被 Docker Daemon 识别并执行的特定文件)。

镜像和普通的可执行文件的区别在于 镜像是分层架构,每个镜像一般都依赖于一个基础镜像。最基本的镜像叫scratch 镜像。当然我们也可以构建自己的镜像,然后发布到镜像中心别人就也可以使用了。

Docker 镜像的构建是通过一个 DSL 语言来编写的,叫 Dockerfile 。后文我们会细说,这里就先不赘述了。

Docker Container

Container 也就是容器。 Docker 官方对容器的定义非常的优雅。

A container is a runnable instance of an image.
容器是镜像的运行实例。

这个定义和进程的定义非常类似: 进程是程序的运行实例。这样我们就 可以将镜像类比为程序,容器类比为进程,这样就更好理解了。

镜像运行在容器中。

我们可以使用 Docker 的 CLI 命令或者 API 来创建、启动、停止和删除容器等操作。同时对于运行状态的容器我们可以登录进去,类似 ssh 命令等操作。
容器默认是和其他容器以及其宿主机隔离开的。具体的隔离策略可以进行自定义设置。

镜像是 Docker 技术的基石

Docker 镜像是什么???

程序和进程。在《深入理解计算机系统》中对程序和进程的关
系描述有一句话非常好:

进程是程序的一个运行实例

程序是打包好的静态文件,而进程相当于把这些静态文件加载到计算机内存中运行起来。相应的,

容器也可以说是镜像的一个运行实例.

不过这两组概念之间还有一个重大的区别就是

程序运行还依赖于一些操作系统的文件,但是镜像相当于把操作系统的文件也一起打包进了静态文件中。

镜像中包含的操作系统的文件只是一个精简版的,并不是全量的。值得注意的是,镜像中也只是包含了操作系统的必要的文件,在容器启动之后,

基础镜像

虽然说镜像解决了容器所谓的一致性:无论在本地、云端,用户只需要解压打包好的容器镜像,那么这个容器的运行环境就被重现出来了。
这里又出现了另外一个问题: 如果我们每一个应用都自己打包我们的容器依赖的镜像,过程还是很繁琐的,那么这个过程能不能做到复用呢

当然是可以的。首先官方镜像仓库中心提供了很多操作系统镜像,比如 ubuntu , centos 等。这样我们的应用就可以基于这些操作系统基础镜像来构建了。

其次,对于同一个公司内部,多个 Java 应用的开发人员对于环境的依赖都是一致的,比如 JDK , tomcat 等等。我们可以每个人都基于 centos 基础镜像来构建我们的应用镜像,但是还有一种更好的方式是 我们构建出一个 Java应用基础镜像,然后大家复用这个基础镜像。

alpine Linux 系统镜像 : alpine

尽管我在上面提到很多操作系统基础镜像,比如 Ubuntu 或者 CentOS ,但是这些镜像实在是太大了,在实际使用的使用时候会导致镜像的传输效率不高。这里介绍一个精简版本的 Linux 系统镜像 : alpine

下图是 alpine Linxu 官方网站的截图。从图中我们可以看到 alpine Linux 的核心特点就是三
个: small , simple , secure 。也就是 alpine Linux 主打的特点: 以安全为理念的轻量级的 Linux 发行版。很多情况下我们都可以使用 alpine Linux 来替代 Ubuntu 或者 CentOS ,而且这样会使得我们最终的镜像的体积小很多。
在这里插入图片描述

busybox 基础镜像

很多 Docker 教程都使用 busybox 镜像来举例子,而且很多应用镜像都使用 busybox 镜像来作为基础镜像,那么busybox 是什么呢?

简单来说 busybox 是一个集成了一百多个最常用的 Linux 命令和工具的软件工具箱,它在单一的可执行文件中提供了精简的 Unix 工具集。 busybox 既包含了一些简单使用的工具,如 cat 和 echo ,也包含了一些更大,更复杂的工具,如 grep , find , mount 以及 telnet 等。可以说 busybox 是 Linux 系统的瑞士军刀。另外 busyBox 可运行于多款 Posix 环境的操作系统中。

openjdk 基础镜像

在这里插入图片描述
我们要使用 openjdk 镜像和使用其他基础镜像没有区别。

FROM openjdk:7
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
RUN javac Main.java
CMD ["java", "Main"]

镜像构建

镜像构建是基于 Dockerfile 来构建的,具体来说我们只需要按照容器标准编写好 Dockerfile 文件,然后通过构建命令就可以构建出来我们需要的镜像了。

应用程序 build

Dockerfile 编写

我们将上面 build 出来的可执行文件放到镜像中,下面我们开始编写 Dockerfile 文件,如下:

编写 Dockerfile 文件

FROM busybox:glibc
COPY hello /bin/hello
EXPOSE 8080
ENTRYPOINT ["/bin/hello"]

简单解释一下上面的文件内容:
FROM : 表示我们镜像基于 busybox 镜像构建,这里的 busybox 是基础镜像中被广泛使用的一个镜像
COPY :拷贝文件,其中 hello 就是我们上面 go build 生成的可执行文件
EXPOSE :暴露端口
ENTRYPOINT :用来指定我们的镜像的默认启动脚本

镜像 build

编写完 Dockerfile 文件,我们就可以基于 Dockerfile 文件来构建我们的镜像了。将上面的文件命名为 Dockerfile ,然后执行如下的 docker build 命令.

docker build -t hello:v1 

行完之后如果没有出错就会生成一个镜像 hello:v1 ,可以通过 docker images 命令查看。

启动镜像

启动镜像我们通过如下的 docker run 命令来启动:

$ docker run -p 8080:8080 hello:v1

其中参数 -p 是用来将容器的端口和宿主机的端口做映射。运行完之后我们打开浏览器,或者直接通过 curl 命令请求 localhost:8080/hello 都会得到返回的 Hello World!

Docker 的操作参数,包括针对容器( container )、镜像( image )和镜像仓库中心( registry )的操作命令。

统一镜像管理:镜像仓库介绍

Docker 的镜像仓库中心,也就是 DockerRegistry 。本文涉及到的 Registry 主要有三个,由于云厂商的 Registry 大同小异,所以下文中我们将主要介绍官方的 Docker Hub 和使用最多的阿里云的 Docker Registry .

官方 Docker Registry , Docker Hub ; 阿里云 Docker Registry ; 腾讯云 Docker
Registry

Docker Hub

Docker Hub 是 Docker 官方提供的 Registry ,官网页面如下:其中右侧的黑框中的 docker pull ubuntu 就是镜像的拉取方式。
Docker Hub 使用起来非常的简单,但是我们一般也只是使用 Docker Hub 来搜索并下载镜像,并不会用来上传存储我们的自定义镜像,这其中的一个主要原因就是网络原因。所以关于 Docker Hub 的镜像上传我们这里就不介绍了。推荐使用云厂商的镜像仓库中心,比如阿里云

热门 Docker 镜像介绍

前面我们介绍过可以通过一个基础镜像,比如 CentOS 镜像,来构建我们自己的应用的镜像。但是很多情况下, 操作系统镜像都会比较大,比如官方的 Docker Hub 的 CentOS 镜像就有几百兆。使用这些镜像作基础镜像毫无疑问会导致我们最终的应用镜像会非常大。

但是更多时候, 我们并不需要一个完整的操作系统镜像。换句话说,更多情况下 我们只需要操作系统中部分文件,比如 /usr/bin 目录下的很多二进制文件我们可能都使用不到。针对这种情况,有没有 裁剪版或者说精简版的操作系统镜像可以提供给我们使用呢?下面我们就来看看两个精简版的操作系统镜像: busybox 和 alpine .

BusyBox 将许多常用的 UNIX 应用工具精简版集成到一个小的可执行文件中。使用 BusyBox
中的应用通常情况下可用替换我们在 Linux 系统中使用的 GNU 应用工具,比如文件应用工具(比如 cp, rm ), shell 应用工具(比如 xargs )。 BusyBox 中的工具比 GNU 完整版要少一些命令选项,通常是一些不太常用。 BusyBox提供的命令选项是和 GNU 完整命令一致的。同时 BusyBox 提供了一个相对完整和轻巧的操作系统环境。
BusyBox 的设计实现考虑对自身大小的优化和资源的物尽其用。同时 BusyBox 也是模块化的,这也就意味者你可以通过编译选择去掉一些特性。这也意味可以更好的支持定制化系统.

相应地,在镜像领域中有一个镜像就叫 busybox ,可以理解为是上面 busybox 对应的 docker 镜像,也被称为容器界的 helloworld 。 busybox 有很多版本,我们可以通过 docker search busybox 来搜索。
在这里插入图片描述
其中带有 OFFICIAL 标志的是官方镜像,我们通过 docker pull 命令下载

[root@docker ~]# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
Digest: sha256:6915be4043561d64e0ab0f8f098dc2ac48e077fe23f488ac24b665166898115a
Status: Image is up to date for busybox:lates

然后我们通过 docker images 命令查看,可以看到镜像非常的小,只有 1.22MB

[root@docker ~]# docker images | grep busybox
busybox latest 6d5fcfe5ff17 5 weeks ago 1.22MB

下面我们通过 docker run 命令启动 busybox 镜像。

[root@docker ~]# docker run -ti busybox sh
/ #

出现了 / # 提示符则说明现在我们位于 busybox 镜像启动的容器内部。我们可以通过 ls 命令查看包含了哪些文件。

/ # ls /
bin dev etc home proc root sys tmp usr var

细心的同学会发现这个相比完整的操作系统,少了很多目录,下面是一个完整的 centos 系统的根目录文件,相比
busybox 多了很多文件目录,也就是说 busybox 相当于是一个裁剪版的操作系统。

[root@docker ~]# ls /
bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr va

我们再看一下 busybox 内部包含了操作系统命令,在 /bin 目录下。常用的操作系统命令都包含了,比如 df 、 du 、awk 、 sed ,所以 busybox 又被称为瑞士军刀。

/ # ls /bin | wc -l
399

不光如此我们还可以把 BusyBox 作为我们的基础镜像来构建我们的应用镜像,只需要像下面那样在首行通过FROM 引用即可。

FROM busybox
....

Alphine

Alpine 操作系统是一个面向安全的轻型 Linux 发行版。它不同于通常 Linux 发行版, Alpine 采用了 musl libc 和busybox 以减小系统的体积和运行时资源消耗,但 功能上比 busybox 又完善的多,因此得到开源社区越来越多的
青睐。

在保持瘦身的同时, Alpine 还提供了自己的包管理工具 apk ,可以通过
https://pkgs.alpinelinux.org/packages 网站上查询包信息,也可以直接通过 apk 命令直接查询和安装各种软件.

Alpine 由非商业组织维护的,支持广泛场景的 Linux 发行版,它 特别为资深 / 重度 Linux 用户而优化,关注安全,性能和资源效能。 Alpine 镜像可以适用于更多常用场景,并且是一个优秀的可以适用于生产的基础系统 / 环境。

Alpine Docker 镜像也继承了 Alpine Linux 发行版的这些优势。相比于其他 Docker 镜像,它的容量非常小,仅仅只有 5 MB 左右(对比 Ubuntu 系列镜像接近 200 MB ),且拥有非常友好的包管理机制。官方镜像来自 docker-alpine 项目。

很多时候我们构建应用镜像的使用都没有必要使用 ubuntu 或者 centos 镜像作为基础镜像,因为完整的操作系统镜像体积非常的大。当我们在分布式环境下涉及到镜像分发的话,如果镜像太大会严重影响分发的速度。

本文介绍了两个小巧的基础镜像: busybox 和 alpine

程序员的痛点

很多时候我们要学习一门新的技术,比如 MySQL (这里举个例子, MySQL 当然不能算是新技术),如果不使用Docker 的情况下,我们第一步是配置开发环境。但是很多软件的环境配置起来非常复杂,这一步就吓退了很多初学者。借助于 Docker 镜像,我们将不再受到这些困扰,因为镜像中包含了运行该软件需要的所有软件依赖,这样我们就可以将主要精力用来学习核心技术,而不是环境配置上.

在这里插入图片描述

Docker 隔离的本质:namespace

Docker 或者说容器技术的一个核心优势就是资源隔离性,那么我们就来看一下资源隔离技术的内核支持,也就是 namespace 技术。

1. namespace 简介

namespace 的中文一般翻译成命名空间,我们也可以将 linux 的 namespace 理解成一系列的资源的抽象的集合。每个进程都有一个 namespace 属性,进程的 namespace 可以相同。对于同属于一个 namespace 中进程,可以感知到彼此的存在和变化,而对外界的进程一无所知,而这正是 docker 所需要的。

2. namespace 种类

Linux 内核中提供了 6 中隔离支持,
分别是: IPC 隔离、网络隔离、挂载点隔离、进程编号隔离、用户和用户组隔
离、主机名和域名隔离.

Namespace flag 隔离内容
IPC CLONE_NEWIPC IPC ( 信号量、消息队列和共享内存等)隔离
Network CLONE_NEWNET 网络隔离(网络栈、端口等)
Mount CLONE_NEWNS 挂载点(文件系统)
PID CLONE_NEWPID 进程编号
User CLONE_NEWUSER 用户和用户组
UTS CLONE_NEWUTS 主机名和域名

每个进程都有一个 namespace ,在 /proc//ns 下面,下面是一个示例:

[root@xxx ns]# ls -al
total 0
dr-x–x--x 2 root root 0 Nov 3 16:16 .
dr-xr-xr-x 9 root root 0 Nov 3 15:50 …
lrwxrwxrwx 1 root root 0 Nov 3 16:16 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Nov 3 16:16 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Nov 3 16:16 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Nov 3 16:16 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Nov 3 16:16 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Nov 3 16:16 uts -> uts:[4026531838

如上,我们可以看到 ns 目录下共有 6 个 link 文件,分别为 ipc, mnt, net, pid, user, uts ,分别对应了我们上面提到的 6 中隔离技术。对于我们直接运行宿主机上并且没有做资源隔离的进程,这 6 个 link 文件指向的目标文件也都是一致的。而对于 docker 进程, ns 目录下的 link 文件和宿主机上的 link 文件是不一样的,也就是说他们属于不同的 namespace 空间。

Docker 资源限制的幕后主使: cgroup

Docker 中的资源限制技术:CGroups 。 Linux Cgroups 的全称是 Linux Control Group ,简单来说, CGroups 的作用就是限制一个进程组能够使用的资源上限, CPU ,内存等.

  1. CGroups 的历史
    CGroups 最初由 Google 的工程师 Paul Menage 和 Rohit Seth 发起,当时项目名叫 Process Container 。后来为了避免 Linux 系统中各种各样的 container 含义引入歧义,改名为 control groups 。CGroups 的正式面世在 2008 年初,伴随 Linux 的内核版本 2.6.24 的 release 发布,这个是版本 version 1 。后来越来越多的特性开始被加入到 CGroups 中,但是由于设计并不是很好,后面 CGroups 中代码越来越多,越难维护,甚至出现某些情况下冲突的问题.

CGroups 中有几个重要概念

cgroup :通过 CGroups 系统进行限制的一组进程。 CGroups 中的资源限制都是以进程组为单位实现的,一个进程可以加入到某个进程组,从而受到相同的资源限制。

task :在 CGroups 中, task 可以理解为一个进程。

hierarchy :可以理解成层级关系, CGroups 的组织关系就是层级的形式,每个节点都是一个 cgroup 。 cgroup可以有多个子节点,子节点默认继承父节点的属性。

subsystem :更准确的表述应该是 resource controllers ,也就是资源控制器,比如 cpu 子系统负责控制 cpu时间的分配。子系统必须应用( attach )到一个 hierarchy 上才能起作用.

其中最核心的是 subsystem , CGroups 目前支持的 subsystem 包括:

cpu :限制进程的 cpu 使用率;
cpuacct :统计 CGroups 中的进程的 cpu 使用情况;
cpuset :为 CGroups 中的进程分配单独的 cpu 节点或者内存节点;
memory :限制进程的内存使用;
devices :可以控制进程能够访问哪些设备;
blkio :限制进程的块设备 IO ;
freezer :挂起或者恢复 CGroups 中的进程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值