首页 > 其他分享 >【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)

时间:2024-09-04 20:52:24浏览次数:16  
标签:原生 容器 daemon 架构 err run Docker docker

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?

Docker架构

Docker采用client/server架构,客户端向服务器发送请求,服务器负责构建、运行和分发容器:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_容器技术

Docker架构说明:

  • 我们日常使用各种docker命令,如docker run、docker pull等,其实就是在使用Docker客户端(Docker CLI);
  • 客户端将用户输入指令解析发送到docker后端服务docker daemon;
  • 比如是创建容器指令,daemon进程先去在本地镜像库中查找依赖的镜像文件(Image),如果不存在还需到远程仓库(Registry)拉取到本地;
  • 镜像准备完成后,daemon进程根据用户指令参数就可以创建容器(Container)。

通信协议

Docker客户端和后端daemon进程默认运行在同一台服务节点上,并采用UNIX本地套接字方式进行通信,这样可以省略掉网络协议栈流程,性能和安全性更高。可以查看Docker启动日志:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_云原生_02

从上面启动日志最后一行"API listen on /run/docker.sock"可以看到daemon进程采用UNIX本地套接字,套接字文件为:/run/docker.sock。

注意:/var/run/docker.sock和/run/docker.sock是一致的,因为/var/run目录链接到/run目录下:

[root@docker01 docker]# ls -lt /var/run lrwxrwxrwx. 1 root root 6 6月 17 2020 /var/run -> ../run [root@docker01 docker]# ls -lah /run/docker.sock srw-rw----. 1 root docker 0 8月 27 18:25 /run/docker.sock

注意:从上面看到/run/docker.sock文件属主为root,用户组为docker,并且只有属主用户和组用户有rw读写权限,这就是为啥非root用户运行docker会出错,除非创建的用户添加到docker属组里。

如何在容器里使用Docker?

后端daemon进程套接字文件为/var/run/docker.sock,只需要将该文件挂载到容器中,同时这个容器里安装了docker客户端,那它就可以做任何我们在宿主机上docker命令可以做的事情。

下面案例演示下:创建容器并挂载 /var/run/docker.sock 文件,然后在容器里就可以运行docker命令了,比如创建容器,而且这个容器是创建在了宿主机上,而不是这个container里:

1、使用docker镜像创建一个容器
# docker container run --rm -it -v /var/run/docker.sock:/var/run/docker.sock docker:latest sh

2、在刚创建的容器里,使用docker ps可以正常与宿主机上后端daemon进程交互,查看到当前有1个正在运行中容器
/ # docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS                                   NAMES
3e72e6a9563d   docker:latest   "docker-entrypoint.s…"   14 seconds ago   Up 14 seconds                                           tender_kilby

3、在容器里使用docker run命令创建一个新容器
/ # docker container run --rm -d busybox:latest ping 1.1.1.1
b250c29051e51cb429e15471ed301c0806a29cef1ffbf388c46861a7f538f4f9

4、在容器里重新查看运行中容器,刚新建的容器也显示出来
/ # docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED              STATUS              PORTS                                   NAMES
b250c29051e5   busybox:latest   "ping 1.1.1.1"           4 seconds ago        Up 4 seconds                                                stoic_agnesi
3e72e6a9563d   docker:latest    "docker-entrypoint.s…"   About a minute ago   Up About a minute                                           tender_kilby

5、退出容器,在宿主机上使用docker ps查看运行中容器,和容器中使用docker ps命令查看内容一致
/ # exit
# docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED          STATUS         PORTS                                   NAMES
b250c29051e5   busybox:latest   "ping 1.1.1.1"           10 seconds ago   Up 9 seconds                                           stoic_agnesi
3e72e6a9563d   docker:latest   "docker-entrypoint.s…"   11 minutes ago   Up 11 minutes                                           tender_kilby

容器里使用Docker一般用于容器监控、或者DevOps场景下,比如Jenkins是以容器方式部署运行,这时可能就需要在容器里构建镜像、自动基于构建镜像部署运行等。

从上面得知,Docker客户端和后端daemon进程默认运行在同一台节点,并采用UNIX本地套接字方式通信,因为这样避免复杂的网络协议栈流程,性能和安全性更高;它们还可以运行在不同服务节点上进行远程通信,后端daemon进程除了支持上面说的UNIX本地套接字外,还可以支持Socket方式,下面就来说下Docker服务端远程访问有哪些方式。

远程访问

Docker CLI和后端daemon进程默认情况下同服务器部署,采用 UNIX本地套接字方式通信,关闭了远程访问机制,下面介绍如何开启 Docker 远程访问。

1、编辑/usr/lib/systemd/system/docker.service

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_容器技术_03

2370是远程访问端口号,根据需要调整,默认使用2375端口。

2、重启Docker服务使修改生效

systemctl daemon-reload
systemctl restart docker

再查看docker启动日志:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_容器技术_04

从启动日志最后两行输出的信息来看,后端daemon进程将UNIX本地套接字和TCP两种网络监听方式都开启,下面就来看下有哪些方式可以远程访问Docker服务。

Rest API

Rest API方式通用性较高,基本所有服务进程都支持Rest API访问机制。比如之前使用 docker version 指令查看版本信息,现在可以直接通过浏览器或curl访问URL:http://192.168.31.150:2370/version获取:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_Docker_05

还比如 docker ps -a 指令查看容器信息,现在可以通过URL:http://192.168.31.150:2370/containers/json?all=true获取到:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_Docker_06

之前通过docker cli方式执行的指令,现在都可以通过Rest API方式执行,具体Rest API接口及其参数可以查看官网:https://docs.docker.com/reference/api/engine/v1.43/ 。

Docker CLI

如果Docker CLI需要访问远端Docker服务,可以通过执行指令时添加 -H 或 --host 参数设置远端服务IP和端口信息:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_Docker_07

如果不想在每次指令中都指定远程地址,也可以将其设置到环境变量中:

[root@localhost ~]# export DOCKER_HOST="tcp://192.168.31.150:2370"

注意:
-H参数也可以指定本地套接字文件,如:docker -H unix:///var/run/docker.sock ps -a
-H指定TCP协议,如:docker -H tcp://192.168.31.150:2370 ps -a,这种方式如果不指定端口,默认是2375,tcp://协议前缀可以省略。

SDK

对于开发来说,难免将docker和项目进行整合集成,官网提供SDK方式可以快速轻松地构建和扩展Docker应用,官网SDK支持Golang、Python,非官网支持语言较多,实在不支持的语言上面介绍过使用Rest API方式也很方便。

通过docker run指令可以创建运行容器,如下:

docker run -d --name nginx_from_sdk -p 18080:80 nginx

下面我们通过golang程序实现上述效果:

package main

import (
 "context"
 "fmt"
 "github.com/docker/go-connections/nat"
 "io"
 "os"

 "github.com/docker/docker/api/types/container"
 "github.com/docker/docker/api/types/image"
 "github.com/docker/docker/client"
)

func main() {
 ctx := context.Background()
 cli, err := client.NewClientWithOpts(
  client.FromEnv,
  client.WithAPIVersionNegotiation(),
  client.WithHost("tcp://192.168.31.150:2370"))
 if err != nil {
  panic(err)
 }
 defer cli.Close()

 imageName := "nginx"

 out, err := cli.ImagePull(ctx, imageName, image.PullOptions{})
 if err != nil {
  panic(err)
 }
 defer out.Close()
 io.Copy(os.Stdout, out)

 /**
 docker run -d --name nginx_from_sdk -p 18080:80 nginx
 */
 resp, err := cli.ContainerCreate(ctx, &container.Config{
  Image: imageName,
 }, &container.HostConfig{
  PortBindings: nat.PortMap{"80/tcp": []nat.PortBinding{{
   HostIP:   "0.0.0.0",
   HostPort: "18080",
  }}},
 }, nil, nil, "nginx_from_sdk")

 if err != nil {
  panic(err)
 }

 if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
  panic(err)
 }

 fmt.Println(resp.ID)
}

执行上述代码后,查看容器列表,发现已经按照指定信息创建对应容器:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_云原生_08

核心架构剖析

上面官网Docker架构图较为粗糙,从业务功能上描述了大概流程,通过该架构图并不能真正了解Docker背后运行的机制,我重新给Docker绘制了张"自画像"可更好的反应Docker背后的真实面孔:

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_云原生_09

从上面架构图看较为复杂,涉及到组件包括:docker cli、daemon、containerd、containerd-shim、runc等,还涉及CRI、OCI两个容器运行时的协议规范,这套架构的背后还涉及到诸多历史原因,下面就来缕下它们的关系,可以对Docker的发展、容器技术的演进有更加清晰的认识。

2013年Docker强势崛起一下带火了容器技术,大家纷纷转入开始关注和使用容器技术,其中就包括Google这种科技巨佬,Google 开始希望和 Docker 公司合作共同推进一个中立的容器运行时(container runtime)库作为 Docker 项目的核心依赖。Docker公司当时风头正盛,这个提议显然会削弱自身的影响力,即使Google这种科技巨佬也没放在眼里,直接拒绝了这个提议,从此也开启了容器圈后续一系列政治斗争。

Google 紧接着联合 Red Hat、IBM 等几位巨佬认为运行时标准不能被 Docker 一家公司控制, 于是就撺掇着搞了开放容器标准(Open Contianer Initiative),简称 OCI。OCI 的提出意在将容器运行时和镜像的实现从 Docker 项目中完全剥离出来。这样做,一方面可以改善 Docker 公司在容器技术上一家独大的现状;另一方面也为其他玩家不依赖于 Docker 项目构建各自的平台层能力提供了可能。这就和之前Google希望和Docker合作共同推进一个中立的容器运行时库的诉求是一致的,合作不行那就联合其它大佬一起制定接口规范。

Docker公司迫于压力权衡后将 Libcontainer 捐出,并改名为 RunC 项目,作为OCI接口标准和规范的参考实现,其实OCI接口规范本身就是依据RunC项目制定的一套容器和镜像的标准和规范,毕竟当时Docker基本是容器生态的事实标准。

Docker最开始使用的容器运行时并没有自己开发,而是使用三方的LXC产品,但从0.9版本开始使用自己开发的Libcontainer取代LXC,LXC叫做LinuX Container,简称Linux的容器。

这还不够,为了彻底扭转 Docker 一家独大的局面,Google、Microsoft、IBM、Amazon、Red Hat等几位大佬又合伙成立了一个基金会叫 CNCF,全称 Cloud Native Computing Foundation(云原生计算基金会),CNCF 的目标很明确,在容器运行时基本处于被Docker垄断状态,Docker生态成为很多容器事实标准,那就换个赛道,Docker的致命问题就是更加偏向底层部署,缺乏平台化能力,对于生产环境下大规模部署稍显不足,CNCF基金会就从容器技术上层应用发力:基于容器部署,发展使用Kubernetes进行编排与调度,顺势发布了 Kubernetes 项目。

Docker 也并没有"坐以待毙",开始主动革新,Docker 从 1.1 版本起推动自身的重构,将容器运行时核心功能拆分到 Containerd 项目中,作为Docker的核心依赖,这样做也是为了推动 Docker Swarm 项目发展,以便在容器编排上重点发力,准备和kubernetes掰掰手腕,以提升Docker的平台化能力,结果直接被秒杀以惨败收场。后来又将Docker容器运行时核心依赖 Containerd 捐赠给 CNCF 社区,开始专注于自己商业化转型。至此,容器市场的格局发生重大改变,kubernetes成为容器圈的"新宠儿"。

经过Containerd、RunC等项目从Docker中分离出来形成一个个独立的开源项目,Docker中容器相关核心技术已被掏空,这也导致了Docker的影响力已被极大的削弱,见下图。如今容器已不再与Docker紧密耦合,我们可以使用Docker或者其他非Docker工具运行容器,Docker不再代表容器技术的发展。当前的Docker也是将Containerd、RunC等容器运行时核心依赖集成进来,自身主要关注的是上层功能封装集成、用户体验优化等,docker的能力大不如从前。

【云原生•容器】Docker架构剖析,它还是从前那个Docker吗?(上)_Docker_10

OCI开发容器标准定义了容器运行时规范和容器镜像规范,那为啥又要搞出来个CRI规范呢?还有kubernetes最新版本已经弃用Docker又是怎么回事呢?下文我们继续聊起。

标签:原生,容器,daemon,架构,err,run,Docker,docker
From: https://blog.51cto.com/u_16014310/11920082

相关文章

  • 在本地通过Docker安装RocketMQ
    拉取镜像&部署这里选用foxiswho/rocketmq:server-4.5.1版本,在官方镜像没出来前,foxiswho是一个比较靠谱的第三方镜像。执行下面的命令直接启动NameServer。dockerrun-d-p9876:9876--namermqnamesrvfoxiswho/rocketmq:server-4.5.1接下来执行下面的命令启动Broker,......
  • Java软件架构师-25个关注点
        Java软件架构师需要掌握的25个关注点,包括微服务、云原生应用、反应式编程和区块链技术等。其中,采用微服务架构是当前Java架构师必须具备的能力之一,因为它能够帮助设计出更加灵活、可扩展和可靠的系统。此外,一些与微服务相关的技术和工具,如SpringBoot、Quarkus和OpenShif......
  • 如何使用Docker Compose运行Nexus并将本地仓库管理工具发布至公网
    文章目录前言1.Docker安装Nexus2.本地访问Nexus3.Linux安装Cpolar4.配置Nexus界面公网地址5.远程访问Nexus界面6.固定Nexus公网地址7.固定地址访问Nexus前言本文主要介绍在Linux中使用Docker来一键部署NexusMaven私有仓库工具并结合Cpolar内网穿透实现远程......
  • Centos7.9安装Docker和Docker compose
    什么是docker环境Docker环境是指在计算机中安装和配置了Docker引擎的运行环境。Docker是一种容器化平台,它提供了一种轻量级的虚拟化技术,能够将应用程序及其依赖项打包成一个独立的容器,以实现快速部署、可移植性和易于管理的优势。(Docker环境提供了一种方便、可移植和隔离的方式来......
  • win11+docker desktop导入镜像运行容器
    一:配置环境前置条件:Win11环境,docker注册账号1.安装WSL(1)在windowsstore安装Ubuntu20.04.6(2)设置“控制面板”-“程序”-“程序和功能”-左侧“启用或关闭Windows功能”-打开“适用于Linux的Windows子系统”,“Windows虚拟机监控程序平台”(3)终端执行:sudoaptupdatesudoapt......
  • docker命令行随记
    CentOS部署sudoyuminstall-yyum-utilsdevice-mapper-persistent-datalvm2sudoyum-config-manager--add-repohttp://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.reposudoyummakecachefastsudoyuminstalldocker-cedocker-ce-clicontainerd.iosud......
  • docker安装logstash7
     一、创建网络dockernetworkcreate-dbridgeelastic#和elasticsearch使用同一个网络 二、拉取logstash镜像dockerpulllogstash:7.1.1 三、创建logstash容器dockerrun-it\--namelogstash\-p9600:9600\-p5044:5044\--netelast......
  • 如何从Docker镜像中提取恶意文件
    原创Bypass当发生容器安全事件时,需要从容器或镜像中提取恶意文件进行分析和处理。本文主要介绍3种常见的方法:(1)从运行的容器中复制文件首先,需要从镜像运行启动一个容器,然后,使用dockercp命令从容器中提取文件到宿主机。dockerrun-d--nametesttest:v1.0//运行......
  • 从0到1落地微前端架构:‌MicroApp实战招聘网站
    从0到1落地微前端架构:‌MicroApp实战招聘网站随着前端技术的快速发展和复杂化,‌微前端架构作为一种新兴的前端架构模式,‌正在逐渐受到开发者的关注和应用。‌本文将详细介绍如何从零开始,‌利用微前端架构和MicroApp技术实战开发一个招聘网站,‌并分析其设计思路、‌实施步骤及优势......
  • 手游后端架构中,用命令模式解决什么问题
    Hello,大家好,我是V哥。命令模式(CommandPattern)是一种行为设计模式,它将一个请求封装为一个对象,从而允许用户使用不同的请求、队列或日志请求来参数化其他对象。命令模式也支持可撤销的操作。在手游后端架构中,命令模式可以将玩家的操作请求(如移动、攻击、技能释放等)封装成......