本文首发于 ❄️慕雪的寒舍
因为本人没有学习过docker,虽然部署过很多镜像,但是对于docker底层的实现一概不知。趁学习一个新项目的契机,将docker的相关概念了解清楚。
安装docker的教程请查看 Linux主机安装docker。
如果你想和我一起学习docker,请关注本站的编程学习/Docker学习
专栏。后续和docker概念相关的内容都会放到这个分类里面,而折腾docker镜像和开源软件的内容则保留在编程学习/Docker真好玩
中。
1.docker和虚拟机的区别
1.1 简述
虚拟机和Docker都是在原有主机的基础上添加了抽象层,通过这些抽象层来实现特定的功能。
这就好比C语言中的文件操作实际上封装了Linux和Windows的文件系统调用接口。这一层封装就是一层抽象层,通过封装,C语言实现了跨平台的统一文件函数接口。
虚拟机:虚拟机是通过Hypervisor(虚拟机管理系统)来虚拟出网卡、CPU、内存等虚拟硬件,再在其上方建立一个虚拟机。每个虚拟机都有独立的操作系统,都有自己独立的系统内核。
docker容器:容器是利用linux下的namespace,将文件系统、进程、网络、设备等资源进行一定隔离,再使用cgroup对容器权限、资源进行限制(比如限制某个容器只能占用最多512MB的内存),最终让容器之间互不影响,容器无法影响宿主机。这些操作都是由宿主机的Docker Engine
来实现和管理的。
上图源自 微软官方文档
1.2 什么是GuestOS
GuestOS(Guest Operating System)是指在虚拟化环境中运行的操作系统。
在虚拟化技术中,物理服务器上的虚拟化软件(如VMware、KVM、VirtualBox等)可以创建一个或多个虚拟的计算环境,每个环境都可以独立运行一个操作系统,这个操作系统就是 GuestOS。
1.3 什么是namespace
好比C++中的namespace,Linux内核中的namespace可以限制某个进程能“看到”的资源。
它是一种将全局系统资源划分为独立单元的资源隔离方法,对文件系统、进程、网络、设备等资源进行隔离,使得在不同命名空间中运行的进程看到的资源是不同的,彼此之间相互隔离,容器内的进程认为它们运行在独立的环境中,而不会看到主机上其他容器的影响。
1.4 什么是cgroup
cgroup是一种用于限制和隔离一个或一组进程对系统资源使用的机制,将一组进程组织在一个控制组中,为这个控制组分配特定的资源限制与优先级,包括 CPU资源、内存、网络等。确保容器在共享主机上合理利用系统资源,避免资源竞争和过度使用。
2.docker的优势
docker因自身的特性,相比虚拟机有很多优势:
- 运行在容器上的Docker的程序,直接使用的都是宿主机的硬件资源,而且Docker比虚拟机有更少的抽象层,因此在cpu、内存、利用率上,Docker将会在效率上具有更大的优势。(效率高)
- Docker直接利用宿主机的系统内核,避免了虚拟机启动时所需要的系统引导时间和操作系统运行的资源消耗,利用Docker能够在几秒钟之内启动大量的容器,是虚拟机无法办到的。快速启动低资源消耗的优点,使Docker在弹性云平台自动运维系统方面具有很好的应用场景。(速度快)
- 容器的启动时间是秒级的,大量节约开发、测试、部署的时间。而且因为Docker以统一的方式进行操作和部署,更方便自动化持续集成/持续部署。
- 还有一个非常关键的点,就是Docker能够高效地部署和扩容,Docker容器几乎可以在任意平台上运行,包括虚拟机、物理机、公有云、私有云、个人电脑、服务器等,这种兼容性,可以让用户把一个应用程序从一个平台直接迁移到另外一个平台。(部署简单)
但是,虚拟机的安全性比容器好一些,docker与宿主机共享内核、文件系统等资源,更有可能对其他容器、宿主机造成影响。
3.docker的基本组成
3.1 简述
docker包括客户端docker client、服务端docker host,以及远程镜像仓库docker registry。
平时我们使用的docker命令(包括docker-compose
工具),其实操作的都是 docker cli
客户端,它的作用是把这些命令发送给本地的 docker.sock
服务端,让服务端来执行docker容器的创建、运行、维护等操作。
- 镜像 Images:可以认为是一个docker构建环境的系统模板,镜像中包括相关的依赖项、项目文件、如何开始运行等信息。镜像是由一层一层的文件系统构建而成的,每一层文件系统是下一层的增量变化。每个镜像都是静态的,被创建后就无法改变;
- 容器 Container:容器是某个正在运行的镜像,是镜像的实体。同一个镜像可以创建出多个不同的容器,且不同容器之间通过Linux内核的namespace和cgroup等技术实现相互隔离,拥有自己独立的文件系统、进程地址空间、网络空间等。
有了镜像,我们可以在不同的Linux主机上,创建容器来统一项目运行的环境,避免在不同系统上安装项目依赖项的繁琐。这可以降低项目部署的难度,提高效率。同时,让项目在容器中运行,也能减少对宿主机的影响,可以避免出现两个不同的项目依赖项冲突而无法运行的情况。
本项目使用docker,就是为了实现不同机器上运行环境的统一,毕竟整个项目涉及到了很多依赖项,如果一个一个安装的话,较为麻烦且容易出错。
3.2 docker镜像是什么?
Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。
我们都知道,操作系统分为 内核 和 用户空间。对于 Linux
而言,内核启动后,会挂载 root
文件系统为其提供用户空间支持。而 Docker 镜像(Image
),就相当于是一个 root
文件系统。比如官方镜像 ubuntu:18.04
就包含了完整的一套 Ubuntu 18.04 最小系统的 root
文件系统。
Docker镜像是由多层的文件系统构建而成的,每一层文件系统是下一层的增量变化。对于Ubuntu/CentOS这种基础镜像而言,它们一般都只有一层。但对于基于这两个镜像构建的其他Docker镜像,则会出现多层,具体取决于镜像构建使用的DockerFile。
3.3 docker容器是什么?
容器是镜像的实体。
容器在运行的时候,会在镜像文件的只读层上方创建一个读写层,并在这个读写层中进行文件的读写和运行。同时容器也可以暴露出端口,或与宿主机的某个文件/文件路径绑定,来对外提供网络服务,和数据的持久化。
4.docker run的运行过程
当我们执行 docker run 命令创建一个容器的时候,会执行如下操作。
当本机找不到指定的镜像时,会自动去docker hub或者预先配置好的docker镜像源仓库中,查询这个镜像并进行下载(查询不到则提示“镜像不存在”)。找到指定的镜像后,会自动执行docker pull操作将镜像下载至本地,随后以该镜像,按docker run命令给定参数创建容器并运行。