容器架构

006 容器What,Why,How

学习任何东西都可以按照3W的框架进行,容器技术也是一样,先回答 What、Why 和 How 这三个问题。
容器发展史
1979年,在Unix V7 的开发过程中,引入Chroot Jail以及Chroot系统调用Chroot jail被用于
“Change Root”,它被认为是最早的容器化技术之一。它允许您将进程及其子进程与操作系统的其余部分隔离开来。这种隔离的唯一问题是根进程(root process)可以轻松地退出chroot。它从未考虑实现安全机制。
1982年,Chroot加入BSD。
2000年,在FreeBSD OS中引入FreeBSD Jail,旨在为简单的Chroot文件隔离带来更多安全性。
FreeBSD Jail允许管理员将FreeBSD OS划分为几个独立的小型系统 - 称为“jails”,还可以为每个小型
系统分配IP地址。
2001年,Linux内核具有操作系统级的虚拟化的功能以后推出了Linux VServer。它使用了类似
chroot的机制与“安全上下文”(“security context”)以及操作系统虚拟化(容器化)相结合来提供
虚拟化解决方案,允许您在单个Linux发行版上运行多个Linux发行版。
2004年,Solaris Containers问世,它使用系统资源控制和“区域”(zone)实现边界分离。
2006年,Google推出Process Containers,旨在限制,计算和隔离一组进程的资源使用(CPU,内
存,磁盘I/O,网络)。2007年,命名为控制组-cgroups,最终合并到Linux内核2.6.24。
2008年,LXC(LinuX Containers)是第一个Linux容器管理器,使用cgroups和命名空间
(namespace)在单个Linux内核上运行多个容器。
2013年,Docker推出了的第一个版本。在LXC的基础上,Docker进一步优化了容器的使用体验。
后来用自己的库libcontainer替换了该容器管理器。
2014年,Google推出容器LMCTFY(Let me contain that for you),谷歌容器栈的开源版本,提
供Linux应用程序容器。谷歌工程师一直在与Docker合作libcontainer,并将核心概念和抽象移植到
libcontainer。因此没有积极开发LMCTFY项目,未来LMCTFY项目的核心可能会被libcontainer取
代。谷歌是容器化行业的领导者。谷歌的一切都在容器上运行。每周有超过20亿个容器在Google基
础架构上运行。
2014年12月,CoreOS发布并开始支持rkt。
What - 什么是容器?
容器是一种轻量级、可移植、自包含的软件打包技术,使应用程序可以在几乎任何地方以相同的方式运
行。开发人员在自己笔记本上创建并测试好的容器,无需任何修改就能够在生产系统的虚拟机、物理服
务器或公有云主机上运行。
容器与虚拟机
谈到容器,就不得不将它与虚拟机进行对比,因为两者都是为应用提供封装和隔离。
容器由两部分组成:

  1. 应用程序本身
  2. 依赖:比如应用程序需要的库或其他软件
    容器在 Host 操作系统的用户空间中运行,与操作系统的其他进程隔离。这一点显著区别于的虚拟机。
    传统的虚拟化技术,比如 VMWare, KVM, Xen,目标是创建完整的虚拟机。为了运行应用,除了部署应
    用本身及其依赖(通常几十 MB),还得安装整个操作系统(几十 GB)。
    下图展示了二者的区别。
    p026_img001.png
    在这里插入图片描述

如图所示,由于所有的容器共享同一个 Host OS,这使得容器在体积上要比虚拟机小很多。另外,启动
容器不需要启动整个操作系统,所以容器部署和启动速度更快,开销更小,也更容易迁移。
Why - 为什么需要容器?
为什么需要容器?容器到底解决的是什么问题?
简要的答案是:容器使软件具备了超强的可移植能力。
容器解决的问题
我们来看看今天的软件开发面临着怎样的挑战?
如今的系统在架构上较十年前已经变得非常复杂了。以前几乎所有的应用都采用三层架构
(Presentation/Application/Data),系统部署到有限的几台物理服务器上(Web Server/Application
Server/Database Server)。
而今天,开发人员通常使用多种服务(比如 MQ,Cache,DB)构建和组装应用,而且应用很可能会部
署到不同的环境,比如虚拟服务器,私有云和公有云。
p027_img002.png
一方面应用包含多种服务,这些服务有自己所依赖的库和软件包;另一方面存在多种部署环境,服务在
运行时可能需要动态迁移到不同的环境中。这就产生了一个问题:
如何让每种服务能够在所有的部署环境中顺利运行?
于是我们得到了下面这个矩阵:
p027_img003.png
各种服务和环境通过排列组合产生了一个大矩阵。开发人员在编写代码时需要考虑不同的运行环境,运
维人员则需要为不同的服务和平台配置环境。对他们双方来说,这都是一项困难而艰巨的任务。
如何解决这个问题呢?
聪明的技术人员从传统的运输行业找到了答案。
几十年前,运输业面临着类似的问题。
p028_img004.png
每一次运输,货主与承运方都会担心因货物类型的不同而导致损失,比如几个铁桶错误地压在了一堆香
蕉上。另一方面,运输过程中需要使用不同的交通工具也让整个过程痛苦不堪:货物先装上车运到码
头,卸货,然后装上船,到岸后又卸下船,再装上火车,到达目的地,最后卸货。一半以上的时间花费
在装、卸货上,而且搬上搬下还容易损坏货物。
这同样也是一个 NxM 的矩阵。
p028_img005.png
幸运的是,集装箱的发明解决这个难题。
p029_img006.png
任何货物,无论钢琴还是保时捷,都被放到各自的集装箱中。集装箱在整个运输过程中都是密封的,只有到达最终目的地才被打开。标准集装箱可以被高效地装卸、重叠和长途运输。现代化的起重机可以自动在卡车、轮船和火车之间移动集装箱。集装箱被誉为运输业与世界贸易最重要的发明。
p029_img007.png
Docker 将集装箱思想运用到软件打包上,为代码提供了一个基于容器的标准化运输系统。Docker 可以将任何应用及其依赖打包成一个轻量级、可移植、自包含的容器。容器可以运行在几乎所有的操作系统上。
p029_img008.png
其实,“集装箱” 和 “容器” 对应的英文单词都是 “Container”。“容器” 是国内约定俗成的叫法,可能是因为容器比集装箱更抽象,更适合软件领域的原故吧。
我个人认为:在老外的思维中,“Container” 只用到了集装箱这一个意思,Docker 的 Logo 不就是一堆集装箱吗?
p030_img009.png
Docker 的特性
我们可以看看集装箱思想是如何与 Docker 各种特性相对应的。
在这里插入图片描述

容器的优势
对于开发人员 - Build Once, Run Anywhere
容器意味着环境隔离和可重复性。开发人员只需为应用创建一次运行环境,然后打包成容器便可在其他
机器上运行。另外,容器环境与所在的 Host 环境是隔离的,就像虚拟机一样,但更快更简单。
对于运维人员 - Configure Once, Run Anything
只需要配置好标准的 runtime 环境,服务器就可以运行任何容器。这使得运维人员的工作变得更高效,
一致和可重复。容器消除了开发、测试、生产环境的不一致性。
How - 容器是如何工作的?
从下节开始我们将学习容器核心知识的最主要部分。首先会介绍 Docker 的架构,然后分章节详细讨论
Docker 的镜像、容器、网络和存储。
容器与虚拟机比较
谈到容器,就不得不将它与虚拟机进行对比,因为两者都是为应用提供封装和隔离。
容器由两部分组成:

  1. 应用程序本身
  2. 依赖:比如应用程序需要的库或其他软件
    容器在 Host 操作系统的用户空间中运行,与操作系统的其他进程隔离。这一点显著区别于的虚拟机。
    传统的虚拟化技术,比如 VMWare, KVM, Xen,目标是创建完整的虚拟机。为了运行应用,除了部署应
    用本身及其依赖(通常几十 MB),还得安装整个操作系统(几十 GB)。
    下图展示了二者的区别:
    p031_img010.png
    如图所示:
    所有的容器共享同一个 Host OS,这使得容器在体积上要比虚拟机小很多。
    另启动容器不需要启动整个操作系统,容器部署和启动速度更快,开销更小,也更容易迁移。
    容器可以在核心CPU本地运行指令,而不需要任何专门的解释机制。与虚拟化相比,这样既不需要指令
    级模拟,也不需要即时编译。
    在这里插入图片描述
    Docker介绍
    Docker是基于Go语言实现的开源容器项目,诞生于2013年年初,最初发起者是dotCloud公司。Docker自开源后受到广泛的关注和讨论,逐渐形成了围绕Docker容器的生态体系。由于Docker在业界造成的影响力实在太大,dotCloud公司后来也直接改名为Docker Inc,并专注于 Docker相关技术和产品的开发。
    Docker提供了各种容器管理工具(如分发、版本、移植等),让用户无需关注底层的操作,可以更简单明了地管理和使用容器;其次,Docker引入分层文件系统构建和高效的镜像机制降低了迁移难度,极大地提升了用户体验。用户操作Docker容器就像操作应用自身一样简单。
    简单地讲,可以将Docker容器理解为一种轻量级的沙盒(sandbox)。每个容器内运行着一个应用,不同的容器相互隔离,容器之间也可以通过网络互相通信。容器的创建和停止都十分快速,几乎跟创建和终止原生应用一致;另外,容器自身对系统资源的额外需求也十分有限,远远低于传统虚拟机。很多时候,甚至直接把容器当作应用本身也没有任何问题。
    p032_img011.png
    Docker版本
    Docker CE在17.03版本之前叫Docker Engine,Docker Engine的版本号范围:0.1.0~1.13.1。
    在2017年3月2日,docker团队宣布企业版Docker Enterprise Edition(EE)发布.为了一致,免费的Docker
    Engine改名为Docker Community Edition(CE),并且采用基于时间的版本号方案.就在这一天,Docker EE和
    Docker CE的17.03版本发布,这也是第一个采用新的版本号方案的版本。
    p032_img012.png
    Docker CE/EE每个季度发布一次季度版本,也就是说每年会发布4个季度版本,17.03,17.06,17.09,17.12就
    是2017年的4个季度版本的版本号,同时Docker CE每个月还会发布一个EDGE版本,比如
    17.04,17.05,17.07,17.08,17.10,17.11.
    Docker CE季度版本自发布后会有4个月的维护期;Docker EE季度版本自发布后会有12个月的维护期.
    在基于时间的发布方案中,版本号格式为:YY.MM.,YY.MM代表年月,patch代表补丁号,从0开始,在
    季度版本(如17.03)的维护期内,bug修复相关的更新会以patch递增的方式发布,比如17.03.0->17.03.1->17.03.2.
    Docker核心概念
    Docker的大部分操作都围绕着它的三大核心概念——镜像、容器和仓库而展开。
    Docker镜像
    Docker镜像类似于虚拟机镜像,可以将它理解为一个只读的模板。例如,一个镜像可以包含一个基本的
    操作系统环境,里面仅安装了Apache应用程序(或用户需要的其他软件)。可以把它称为一个Apache镜
    像。镜像是创建Docker容器的基础。通过版本管理和增量的文件系统,Docker提供了一套十分简单的机
    制创建和更新现有的镜像,用户甚至可以从网上下载一个已经做好的应用镜像,并直接使用。
    Docker容器
    Docker容器类似于一个轻量级的沙箱,Docker利用容器来运行和隔离应用。容器是从镜像创建的应用运
    行实例。可以将其启动、开始、停止、删除,而这些容器都是彼此相互隔离的、互不可见的。可以把容
    器看做是一个简易版的Linux系统环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运
    行在其中的应用程序打包而成的盒子。
    Docker仓库
    Docker仓库类似于代码仓库,它是Docker集中存放镜像文件的场所。仓库注册服务器存放着很多类镜
    像,每类镜像包括多个镜像文件,通过不同的标签(tag)来进行区分。例如存放Ubuntu操作系统镜像
    的分类中可能包括1804、1604、14.04、12.04等不同版本的镜像。
    根据所存储的镜像公开分享与否,Docker仓库可以分为:
    公开仓库(Public)
    私有仓库(Private)
    目前,最大的公开仓库是官方提供的Docker Hub,其中存放了数量庞大的镜像供用户下载。国内不少云
    服务提供商(如时速云、阿里云等)也提供了仓库的本地源,可以提供稳定的国内访问。
    当然,用户如果不希望公开分享自己的镜像文件,Docker也支持用户在本地网络内创建一个只能自己访
    问的私有仓库。当用户创建了自己的镜像之后就可以使用push命令将它上传到指定的公有或者私有仓
    库。这样用户下次在另外一台机器上使用该镜像时,只需要将其从仓库上pull下来就可以了。

007 Docker架构详解

完整的Docker由以下几部分构成:
(1) 守护进程(Daemon):Docker守护进程(dockerd)侦听Docker API请求并管理Docker对象,,如图
像、容器、网络和卷。守护进程还可以与其他守护进程通信来管理Docker服务。
(2) REST API: 主要与Docker Daemon进行交互,比如Docker Cli或者直接调用REST API;
(3) 客户端(Docker Client): 它是与Docker交互的主要方式通过命令行接口(CLI)客户端(docker命
令),客户机将命令通过REST API发送给并执行其命令;
(4) Register Repository 镜像仓库: Docker注册表存储Docker镜像,可以采用Docker Hub是公共
注册仓库,或者采用企业内部自建的Harbor私有仓库;
(5) Image 镜像: 映像是一个只读模板,带有创建Docker容器的指令。映像通常基于另一个映像,还
需要进行一些额外的定制,你可以通过Docker Hub公共镜像仓库进行拉取对应的系统或者应用镜
像;
(6) Container 容器: 容器是映像的可运行实例。您可以使用Docker API或CLI创建、启动、停止、
移动或删除容器。您可以将一个容器连接到一个或多个网络,将存储附加到它,甚至根据它的当前
状态创建一个新映像。
(7) Services : Docker引擎支持集群模式服务允许您跨多个Docker守护进程()扩展管理容器,服务允
许您定义所需的状态,例如在任何给定时间必须可用的服务副本的数量。默认情况下,服务在所有
工作节点之间进行负载平衡。对于使用者来说Docker服务看起来是一个单独的应用程序;
p034_img013.png
Docker 的核心组件包括:

  1. Docker 客户端 - Client
  2. Docker 服务器 - Docker daemon
  3. Docker 镜像 - Image
  4. Registry
  5. Docker 容器 - Container
    Docker 架构如下图所示:
    p035_img014.png
    Docker 采用的是 Client/Server 架构。客户端向服务器发送请求,服务器负责构建、运行和分发容器。
    客户端和服务器可以运行在同一个 Host 上,客户端也可以通过 socket 或 REST API 与远程的服务器通
    信。
    Docker 内部具体实现:
  6. 用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。
  7. Docker Daemon作为Docker架构中的主体部分,首先提供Docker Server的功能使其可以接受
    Docker Client的请求。
  8. Docker Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。
  9. Job的运行过程中,当需要容器镜像时,则从DockerRegistry中下载镜像,并通过镜像管理驱动
    Graph driver将下载镜像以Graph的形式存储。
  10. 当需要为Docker创建网络环境时,通过网络管理驱动Networkdriver创建并配置Docker容器网络环
    境。
  11. 当需要限制Docker容器运行资源或执行用户指令等操作时,则通过Exec driver来完成。
  12. Libcontainer是一项独立的容器管理包,Networkdriver以及Execdriver都是通过Libcontainer来实
    现具体对容器进行的操作。
    p036_img015.png
    Docker 客户端
    最常用的 Docker 客户端是 docker 命令。通过 docker 我们可以方便地在 Host 上构建和运行容器。
    docker 支持很多操作(子命令),后面会逐步用到。
  # docker:直接输入 docker 命令,显示帮助信息
  #   Usage: docker [OPTIONS] COMMAND :用法,docker 加选项加子命令
  # Common Commands(常用命令):
  #   run :从镜像创建并运行新容器;exec :在运行中的容器里执行命令
  #   ps :列出容器;build :根据Dockerfile构建镜像
  #   pull :从仓库下载镜像;push :上传镜像到仓库;images :列出镜像
  #   login/logout :登录/退出镜像仓库;search :在Docker Hub搜索镜像
  #   version :查看版本;info :查看系统信息
  # Management Commands(管理命令):container/image/network/volume等对象管理
  # Swarm Commands(集群命令):swarm :管理Swarm集群
  # 目的:查看docker支持的所有命令
  [root@docker ~]# docker
  Usage:  docker [OPTIONS] COMMAND
  A self-sufficient runtime for containers
  Common Commands:
  run         Create and run a new container from an image
  exec        Execute a command in a running container
  ps          List containers
  build       Build an image from a Dockerfile
  pull        Download an image from a registry
  push        Upload an image to a registry
  images      List images
  login       Log in to a registry
  logout      Log out from a registry
  search      Search Docker Hub for images
  version     Show the Docker version information
  info        Display system-wide information
  Management Commands:
  builder     Manage builds
  buildx*     Docker Buildx
  compose*    Docker Compose
  container   Manage containers
  context     Manage contexts
  image       Manage images
  manifest    Manage Docker image manifests and manifest lists
  network     Manage networks
  plugin      Manage plugins
  system      Manage Docker
  trust       Manage trust on Docker images
  volume      Manage volumes
  Swarm Commands:
  swarm       Manage Swarm
  Commands:
  attach      Attach local standard input, output, and error streams to a running
  container
  commit      Create a new image from a container's changes
  cp          Copy files/folders between a container and the local filesystem
  create      Create a new container
  diff        Inspect changes to files or directories on a container's filesystem
  events      Get real time events from the server
  export      Export a container's filesystem as a tar archive
  history     Show the history of an image
  import      Import the contents from a tarball to create a filesystem image
  inspect     Return low-level information on Docker objects
  kill        Kill one or more running containers
  load        Load an image from a tar archive or STDIN
  logs        Fetch the logs of a container
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping for the container
  rename      Rename a container
  

restart     Restart one or more containers
rm          Remove one or more containers
rmi         Remove one or more images
save        Save one or more images to a tar archive (streamed to STDOUT by
default)
start       Start one or more stopped containers
stats       Display a live stream of container(s) resource usage statistics
stop        Stop one or more running containers
tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top         Display the running processes of a container
unpause     Unpause all processes within one or more containers
update      Update configuration of one or more containers
wait        Block until one or more containers stop, then print their exit
codes
Global Options:
--config string      Location of client config files (default
"/root/.docker")
-c, --context string     Name of the context to use to connect to the daemon
(overrides
DOCKER_HOST env var and default context set with
"docker context use")
-D, --debug              Enable debug mode
-H, --host list          Daemon socket to connect to
-l, --log-level string   Set the logging level ("debug", "info", "warn",
"error", "fatal")
(default "info")
--tls                Use TLS; implied by --tlsverify
--tlscacert string   Trust certs signed only by this CA (default
"/root/.docker/ca.pem")
--tlscert string     Path to TLS certificate file (default
"/root/.docker/cert.pem")
--tlskey string      Path to TLS key file (default "/root/.docker/key.pem")
--tlsverify          Use TLS and verify the remote
-v, --version            Print version information and quit
Run 'docker COMMAND --help' for more information on a command.
For more help on how to use Docker, head to https://docs.docker.com/go/guides/
除了 docker 命令行工具,用户也可以通过 REST API 与服务器通信。
Docker 服务器
Docker daemon 是服务器组件,以 Linux 后台服务的方式运行。
```bash
  # systemctl:system control,系统服务管理命令
  #   status :查看服务状态
  #   docker.service :docker服务的systemd单元名
  # 输出字段解释:
  #   Loaded :服务是否已加载(enabled=开机自启)
  #   Active: active (running) :服务正在运行
  #   Main PID: 2309 (dockerd) :主进程PID,dockerd是docker守护进程
  #   Memory: 136.0M :占用内存;Tasks: 10 :任务数
  #   CGroup :控制组,显示dockerd进程树
  # 目的:确认docker守护进程正常运行
  [root@docker ~]# systemctl status docker.service
  ● docker.service - Docker Application Container Engine
  Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor
  preset: disabled)
  Active: active (running) since Mon 2024-09-09 21:40:20 CST; 24h ago
  Docs: https://docs.docker.com
  Main PID: 2309 (dockerd)
  Tasks: 10
Memory: 136.0M
CGroup: /system.slice/docker.service
└─2309 /usr/bin/dockerd -H fd:// --
containerd=/run/containerd/containerd.sock
Sep 09 21:40:16 docker systemd[1]: Starting Docker Application Container
Engine...
Sep 09 21:40:17 docker dockerd[2309]: time="2024-09-09T21:40:17.800157335+08:00"
level=info msg="Star>
Sep 09 21:40:18 docker dockerd[2309]: time="2024-09-09T21:40:18.196566715+08:00"
level=info msg="[gra>
Sep 09 21:40:18 docker dockerd[2309]: time="2024-09-09T21:40:18.208666863+08:00"
level=info msg="Load>
Sep 09 21:40:19 docker dockerd[2309]: time="2024-09-09T21:40:19.587647943+08:00"
level=info msg="Defa>
Sep 09 21:40:19 docker dockerd[2309]: time="2024-09-09T21:40:19.881267095+08:00"
level=info msg="Load>
Sep 09 21:40:19 docker dockerd[2309]: time="2024-09-09T21:40:19.956577565+08:00"
level=info msg="Dock>
Sep 09 21:40:19 docker dockerd[2309]: time="2024-09-09T21:40:19.957045036+08:00"
level=info msg="Daem>
Sep 09 21:40:20 docker dockerd[2309]: time="2024-09-09T21:40:20.014584201+08:00"
level=info msg="API >
Sep 09 21:40:20 docker systemd[1]: Started Docker Application Container Engine.

Docker 镜像
可将 Docker 镜像看着只读模板,通过它可以创建 Docker 容器。
例如某个镜像可能包含一个 Ubuntu 操作系统、一个 Apache HTTP Server 以及用户开发的 Web 应用。
镜像有多种生成方法:

  1. 可以从无到有开始创建镜像
  2. 也可以下载并使用别人创建好的现成的镜像
  3. 还可以在现有镜像上创建新的镜像
    我们可以将镜像的内容和创建步骤描述在一个文本文件中,这个文件被称作 Dockerfile,通过执行
    docker build 命令可以构建出 Docker 镜像,后面我们会讨论。
    Docker 容器
    Docker 容器就是 Docker 镜像的运行实例。
    用户可以通过 CLI(docker)或是 API 启动、停止、移动或删除容器。可以这么认为,对于应用软件,镜
    像是软件生命周期的构建和打包阶段,而容器则是启动和运行阶段。
    Registry
    Registry 是存放 Docker 镜像的仓库,Registry 分私有和公有两种。
    Docker Hub(https://hub.docker.com/) 是默认的 Registry,由 Docker 公司维护,上面有数以万计
    的镜像,用户可以自由下载和使用。
    出于对速度或安全的考虑,用户也可以创建自己的私有 Registry。后面我们会学习如何搭建私有
    Registry。
    docker pull 命令可以从 Registry 下载镜像。
    docker run 命令则是先下载镜像(如果本地没有),然后再启动容器。
    下一节我们通过一个例子来看各个组件是如何协调工作的。

008 Docker组件如何协作?

还记得我们运行的第一个容器吗?现在通过它来体会一下 Docker 各个组件是如何协作的。
容器启动过程如下:
p040_img016.png

  1. Docker 客户端执行 docker run 命令。
  2. Docker daemon 发现本地没有 httpd 镜像。
  3. daemon 从 Docker Hub 下载镜像。
  4. 下载完成,镜像 httpd 被保存到本地。
  5. Docker daemon 启动容器。
    docker images 可以查看到 httpd 已经下载到本地。
  # docker images:列出本地已有的镜像
  #   REPOSITORY :镜像名;TAG :标签;IMAGE ID :镜像ID;SIZE :大小
  # 目的:确认httpd镜像已从Docker Hub下载到本地
  [root@docker ~]# docker images
  REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
  httpd        latest    9cb0a2315602   7 weeks ago   148MB

docker ps 或者 docker container ls 显示容器正在运行。
p040_img017.png
小结
Docker 借鉴了集装箱的概念。标准集装箱将货物运往世界各地,Docker 将这个模型运用到自己的设计哲学中,唯一不同的是:集装箱运输货物,而 Docker 运输软件。
每个容器都有一个软件镜像,相当于集装箱中的货物。容器可以被创建、启动、关闭和销毁。和集装箱一样,Docker 在执行这些操作时,并不关心容器里到底装的什么,它不管里面是 Web Server,还是Database。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐