type
status
date
slug
summary
tags
category
icon
password
 

传统服务部署模式的问题

在我介绍Docker之前,我想先说明没有使用Docker的情况下,我们的软件部署有哪些问题:这个软件在测试环境一切运行正常,为什么在预发环境就是有问题呢?有个服务故障导致主机CPU被占满了,导致同一主机的所有服务都处于不可用状态;某个服务发生了内存泄漏,导致同一主机的其他服务被OOM-KILLER强制关闭。
比如我们的软件是基于Java语言开发的,那么运行程序就需要有JRE环境,这就对我们程序运行环境的部署有了基本要求:需要首先在运行环境安装好Java运行时环境,要知道这只是最基础的环境(需要根据不同的系统架构安装对应的Java运行环境),一旦后续我们的软件需要升级JDK版本,那么就又需要去关注系统环境的运行环境版本,更为重要的是,对于同一主机可能运行的多个Java服务来说,升级JDK版本就意味着可能互相影响(尽管可以安装多个JDK);除此之外,我们的应用可能依赖各种服务,例如MySQL、Redis等,对于不同的操作系统这些基础服务的安装要求可能又不尽相同,使我们在应用开发与交付使用之间的周期很长,同时对系统运维也提出了很高的要求。
对于同一主机运行的不同服务来说,一旦某个服务“失控”,例如发生内存溢出、CPU占用飙升等问题,就可能影响其他服务的正常运行,比如说占用大量内存导致其他服务性能下降,比如说可能导致触发系统OOM-Killer的逻辑导致某些服务被异常“杀死”等,这样各个服务之间就无法做到更好的隔离。可能有的朋友会说,那可以设置JVM的内存大小、应用的内存设置等参数啊,但是前面就提到过了,这种方式需要部署环境的运维人员了解不同服务的配置,而不同的服务配置方式又千差万别,这就又带来了额外的学习和维护成本;可能有的朋友又会说,那我们可以使用虚拟机来进行物理资源的限制啊,其实这种方案是可以达到资源限制的目的,但就我了解到的缺点就是通过虚拟机运行服务,虚拟机本身的运行也需要占用系统资源,另一方面就是虚拟机的隔离可能造成系统资源的浪费。

Docker概念的引入

Docker是如何解决上述问题的呢?这里我会先介绍一下Docker的特点,后面再通过深入了解Docker概念并有了实践经验后,就能够体会到Docker的优点。首先需要清楚的概念是,Docker是作为一个开源开放平台存在的。按照官网的介绍,其允许我们将应用与依赖的基础设施分离(镜像封装了应用依赖的基础设施),这样可以帮我们显著减少应用开发到交付使用的额外工作,并且能够保证交付成果的一致性。Docker可以同时运行多个应用而保证互相之间的完全隔离(容器可以以统一的格式声明对于系统资源的使用限制),容器会封装好应用依赖的基础环境(Dockerfile中会声明)从而摆脱了对于宿主机的环境依赖,这样我们的应用容器就可以运行在任何环境(本地服务器、云服务器等)。
从JVM联想Docker
当我学习Docker这门技术时,让我非常感兴趣的就是它的一句标语:build once run anywhere。这是因为在我学习Java语言开发时,Java的那句write once run anywhere让我记忆很深,直到后面我深入了解JVM的相关知识,才明白其实我们能够利用Java语言做到一次编写处处运行的效果,实际上是由于Java提供了不同平台架构的Java运行环境,想要在对应平台运行Java程序,离不开的基础环境就是Java的运行时,而不同平台的JVM会通过加载标准格式的Class文件转为对应平台的字节码,从而实现对于不同平台可以运行同一份程序的效果。
Docker能够让我们实现一次构建处处运行效果的原理也是类似的,Docker作为一个平台,提供了镜像、容器的概念,使我们可以将运行程序的基础设施和程序本身封装到一个镜像文件内,而Docker通过镜像文件这个标准的程序描述结合镜像中心,就可以实现在不同主机运行基于同一份镜像生成的容器,Docker能够保证这些容器的行为一致性。

Docker架构涉及到的组件

下面是熟悉Docker需要了解的重要组件,这些在Docker程序的运行中都充当着不可或缺的角色。
  • Docker服务进程:监听并处理关于Docker镜像、容器、网络等组件的请求;
  • Docker客户端:用户与Docker交互的主要途径,负责将用户请求与服务进程进行通信交互;
  • Docker镜像中心:用于存储和分发Docker镜像的中心(centralized location),可以分为公共镜像中心(如dockerHub等)以及私有镜像中心(如Harbor等)。

Docker服务的相关对象

以下这部分就属于在我们利用Docker进行程序分发和运行过程中,必须要了解的概念。
  • 镜像:用于创建Docker容器的标准化包,包含了容器运行需要的各种文件、配置、库等,可以直接使用他人发布的镜像也可以通过Dockerfile文件创建自己的镜像,镜像一旦被创建后就是不可变的。
  • 容器:属于镜像的一个可运行实例。容器是组成应用的各个组件的隔离进程。
    • 依赖自我包含:容器本身就拥有了需要运行的各种基础环境,因此不需要宿主机提供任何依赖;
    • 隔离性:容器的隔离运行意味着给其他容器和宿主机带来了更高的安全性;
    • 独立性:每个容器都是被独立维护的,对容器的操作不会影响到其他容器;
    • 可移植性:基于Docker机制的容器可以确保以相同的方式运行于任何平台。

一些碎碎念

刚开始了解Docker的时候我陷入了一种误区,认为想要掌握这门技术或者说这个工具,一定要深入了解它的实现机制,发现其利用了Linux内核的诸如namespace、cgroup之类的基础来实现功能,但是由于对Linux的这些机制我查阅后发现很是深奥,于是就认为自己掌握不了这门技术。其实从使用角度来说,完全不需要太过深入探究其实现机制,当然如果能够理解其运行机制肯定对掌握这门技术会有帮助,但起码并不是必需的知识基础。这是我学习Docker时的感悟之一,仅供诸君参考。
 

参考资源

 
Java定时任务那些事儿2024七夕记
Loading...