Containerd容器管理实战:从架构原理到nerdctlcrictl工具链

文章目录

  • Containerd容器管理实战:从架构原理到nerdctl/crictl工具链
    • 前言
    • 一、Containerd介绍
      • [1.1 什么是Containerd?](#1.1 什么是Containerd?)
      • [1.2 Containerd的前世今生](#1.2 Containerd的前世今生)
      • [1.3 Containerd架构](#1.3 Containerd架构)
      • [1.4 性能对比](#1.4 性能对比)
    • 二、Containerd安装
      • [2.1 YUM方式安装](#2.1 YUM方式安装)
      • [2.2 二进制方式安装](#2.2 二进制方式安装)
    • 三、Containerd容器镜像管理
      • [3.1 三种CLI工具对比](#3.1 三种CLI工具对比)
      • [3.2 ctr命令帮助](#3.2 ctr命令帮助)
      • [3.3 查看镜像](#3.3 查看镜像)
      • [3.4 下载镜像](#3.4 下载镜像)
      • [3.5 镜像挂载](#3.5 镜像挂载)
      • [3.6 镜像导出](#3.6 镜像导出)
      • [3.7 镜像删除](#3.7 镜像删除)
      • [3.8 镜像导入](#3.8 镜像导入)
      • [3.9 修改镜像tag](#3.9 修改镜像tag)
    • 四、Containerd容器管理
      • [4.1 查看命令帮助](#4.1 查看命令帮助)
      • [4.2 查看容器和任务](#4.2 查看容器和任务)
      • [4.3 创建静态容器](#4.3 创建静态容器)
      • [4.4 静态容器启动为动态容器](#4.4 静态容器启动为动态容器)
      • [4.5 进入容器操作](#4.5 进入容器操作)
      • [4.6 直接运行一个动态容器](#4.6 直接运行一个动态容器)
      • [4.7 暂停和恢复容器](#4.7 暂停和恢复容器)
      • [4.8 停止和删除容器](#4.8 停止和删除容器)
    • 五、Containerd使用私有容器镜像仓库Harbor
    • [六、Containerd Namespace管理](#六、Containerd Namespace管理)
      • [6.1 列出已有namespace](#6.1 列出已有namespace)
      • [6.2 创建namespace](#6.2 创建namespace)
      • [6.3 删除namespace](#6.3 删除namespace)
      • [6.4 在指定namespace中操作](#6.4 在指定namespace中操作)
    • 七、nerdctl实践
      • [7.1 安装nerdctl](#7.1 安装nerdctl)
      • [7.2 配置镜像加速](#7.2 配置镜像加速)
      • [7.3 nerdctl管理镜像](#7.3 nerdctl管理镜像)
      • [7.4 nerdctl管理容器](#7.4 nerdctl管理容器)
      • [7.5 nerdctl管理网络](#7.5 nerdctl管理网络)
      • [7.6 nerdctl管理存储](#7.6 nerdctl管理存储)
      • [7.7 nerdctl管理namespace](#7.7 nerdctl管理namespace)
    • 八、crictl实践
      • [8.1 crictl命令介绍](#8.1 crictl命令介绍)
      • [8.2 crictl命令安装](#8.2 crictl命令安装)
      • [8.3 crictl命令配置](#8.3 crictl命令配置)
      • [8.4 crictl命令实践](#8.4 crictl命令实践)
      • [8.5 命令行对照表](#8.5 命令行对照表)
      • [8.6 nerdctl vs crictl](#8.6 nerdctl vs crictl)
    • 写在最后
  • 查看任务
  • nerdctl命令(兼容docker)
  • crictl命令

Containerd容器管理实战:从架构原理到nerdctl/crictl工具链

从Docker到Containerd,从ctr到nerdctl再到crictl,一篇文章掌握云原生时代的容器运行时管理

前言

2016年,Docker将containerd从Docker Engine中剥离出来,作为一个独立的开源项目发展。2020年,Kubernetes宣布不再支持Docker作为容器运行时,containerd成为K8s默认的容器运行时。

这意味着:Docker的时代正在过去,containerd的时代已经到来。

对于运维和开发人员来说,理解containerd的架构、掌握其管理工具(ctr、nerdctl、crictl)已经成为必备技能。

本文将系统讲解containerd的核心知识,涵盖架构原理、安装部署、镜像管理、容器管理、命名空间管理 以及nerdctl和crictl实战,帮助你完成从Docker到containerd的平滑过渡。

一、Containerd介绍

1.1 什么是Containerd?

Containerd是一个工业级的容器运行时,设计目的是为了嵌入到Kubernetes中使用。它不提供给开发人员和终端用户直接使用,这样就避免了与Docker产生竞争。

核心特点

特点 说明
独立开源项目 从Docker Engine剥离,独立发展
工业级容器运行时 目标是为K8s等编排系统提供稳定的运行基础设施
Daemon形式运行 通过暴露底层的gRPC API,上层系统可以管理容器
单机容器管理 每个containerd只负责一台机器:Pull镜像、容器操作、网络、存储
OCI规范支持 具体运行容器由runC负责,支持所有符合OCI规范的容器
轻量易管理 对于容器编排服务来说,运行时只需要containerd+runC

1.2 Containerd的前世今生

2013年:Docker公司推出Docker产品,对全球技术产生巨大影响力。

Google的应对:Google明显感觉到自己公司内部使用的Brog系统江湖地位受到威胁,希望Docker公司能够与自己联合打造一款开源的容器运行时作为Docker核心依赖,但Docker公司拒绝了。

OCI成立 :Google联合RedHat、IBM等公司说服Docker公司把其容器核心技术libcontainer捐给中立社区(OCI,Open Container Initiative),并更名为runC

CNCF成立 :为了进一步遏制Docker在未来技术市场的影响力,避免在容器市场上Docker一家独大,Google公司领导RedHat、IBM等成立了**CNCF(Cloud Native Computing Foundation)**基金会,即云原生计算基金会。Google把内部使用的Brog系统开源------Kubernetes,也就是今天所说的云原生技术生态。

Docker的反击与妥协:2016年Docker公司推出了Docker Swarm,意在一统Docker生态。经过近1年左右时间的市场验证后,发现在容器编排方面无法独立抗衡kubernetes,所以Docker公司于2017年正式宣布原生支持Kubernetes。至此,Docker在大规模容器编排应用市场败下阵来,但是Docker依然不甘心失败,把Docker核心依赖containerd捐给了CNCF,以此说明Docker依旧是一个PaaS平台。

Kubernetes弃用Docker:2020年CNCF基金会宣布Kubernetes 1.20版本将不再仅支持Docker容器管理工具。此事的起因主要与Docker捐给CNCF基金会的containerd有关。早期为了实现Kubernetes能够使用Docker实现容器管理,专门在Kubernetes组件中集成了一个shim(垫片)技术,用来将Kubernetes容器运行时接口(CRI,Container Runtime Interface)调用翻译成Docker的API,这样就可以很好地使用Docker了。但是随着Kubernetes在全球技术市场的广泛应用,有更多的容器管理工具的出现,它们都想能够借助于Kubernetes被用户所使用,所以就提出标准化容器运行时接口,只要适配了这个接口就可以集成到Kubernetes生态当中,所以Kubernetes取消了对shim的维护。并且由于containerd技术的成功,可以实现无缝对接Kubernetes,所以接下来Kubernetes容器运行时的主角是containerd。

1.3 Containerd架构

Containerd采用标准的C/S架构

  • 服务端通过gRPC协议提供稳定的API
  • 客户端通过调用服务端的API进行高级的操作

为了实现解耦,Containerd将不同的职责划分给不同的组件,每个组件就相当于一个子系统(subsystem)。连接不同子系统的组件被称为模块。

Containerd被分为三个大块:Storage、Metadata和Runtime。

核心子系统

子系统 作用
Content 提供对镜像中可寻址内容的访问,所有不可变的内容都被存储在这里
Snapshot 用来管理容器镜像的文件系统快照,镜像中的每一个layer都会被解压成文件系统快照
Runtime 用来执行Bundles,比如创建容器
Metrics 暴露各个组件的监控指标

每一个子系统的行为都由一个或多个模块协作完成(架构图中的Core部分)。每一种类型的模块都以插件的形式集成到Containerd中,而且插件之间是相互依赖的。例如:Service Plugin会依赖Metadata Plugin、GC Plugin和Runtime Plugin。Metadata Plugin依赖Containers Plugin、Content Plugin等。

常用插件

  • Content Plugin:提供对镜像中可寻址内容的访问,所有不可变的内容都被存储在这里
  • Snapshot Plugin:用来管理容器镜像的文件系统快照,类似于Docker中的graphdriver
  • Metrics:暴露各个组件的监控指标

1.4 性能对比

使用bucketbench对Docker、crio和Containerd进行性能测试,包括启动、停止和删除容器,比较它们所耗的时间:

结论 :Containerd在各个方面都表现良好,总体性能优于Docker和crio

二、Containerd安装

操作系统:CentOS Stream 8

2.1 YUM方式安装

bash 复制代码
# 1. 获取阿里云YUM源
[root@localhost ~] yum-config-manager --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
[root@localhost ~] yum makecache

# 2. 查看YUM源中containerd模块
[root@localhost ~] yum list | grep containerd
containerd.io.x86_64    1.6.32-3.1.el8    docker-ce-stable

# 3. 安装containerd.io软件
[root@localhost ~] yum -y install containerd.io

# 4. 使用rpm -qa命令查看是否安装
[root@localhost ~] rpm -qa | grep containerd
containerd.io-1.6.32-3.1.el8.x86_64

# 5. 设置containerd服务启动及开机自启动
[root@localhost ~] systemctl enable containerd --now
[root@localhost ~] systemctl status containerd

# 6. 验证
[root@localhost ~] ctr version
Client:
  Version:  1.6.32
  Revision: 8b3b7ca2e5ce38e8f31a34f35b2b68ceb8470d89
  Go version: go1.21.10

Server:
  Version:  1.6.32
  Revision: 8b3b7ca2e5ce38e8f31a34f35b2b68ceb8470d89
  UUID:     979940af-68e0-4681-a560-189eeaefbdfc

2.2 二进制方式安装

containerd有两种安装包:

  • containerd-xxx:用于单机测试,不包含runC,需要提前安装
  • cri-containerd-cni-xxx:包含runC和K8s所需的相关文件,K8s集群需要用到此包
bash 复制代码
# 1. 下载containerd安装包
[root@localhost ~] wget https://github.com/containerd/containerd/releases/download/v1.6.32/cri-containerd-cni-1.6.32-linux-amd64.tar.gz

# 2. 安装containerd
[root@localhost ~] mkdir containerd
[root@localhost ~] tar xf cri-containerd-cni-1.6.32-linux-amd64.tar.gz -C containerd/
[root@localhost ~] cd containerd/
[root@localhost containerd] ls
cri-containerd.DEPRECATED.txt  etc  opt  usr

# 目录说明:
# etc:containerd服务管理配置文件及cni虚拟网卡配置文件
# opt:gce环境中使用containerd配置文件及cni插件
# usr:containerd运行时文件,包含runc

# 查看containerd.service文件
[root@localhost containerd] cat etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
# 查看此位置,把containerd二进制文件放置于此处即可完成安装

Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=infinity
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target

# 复制containerd运行时文件至系统
[root@localhost containerd] cp usr/local/bin/* /usr/local/bin

# 添加containerd.service文件至系统
[root@localhost containerd] cp etc/systemd/system/containerd.service /usr/lib/systemd/system/containerd.service

# 生成containerd模块配置文件
[root@localhost containerd] mkdir /etc/containerd
[root@localhost containerd] containerd config default > /etc/containerd/config.toml

# 查看配置文件
[root@localhost containerd] cat /etc/containerd/config.toml
version = 2
root = "/var/lib/containerd"
state = "/run/containerd"

[grpc]
  address = "/run/containerd/containerd.sock"

[plugins]
  [plugins."io.containerd.grpc.v1.cri"]
    sandbox_image = "registry.k8s.io/pause:3.6"
    [plugins."io.containerd.grpc.v1.cri".containerd]
      snapshotter = "overlayfs"
      default_runtime_name = "runc"

三、Containerd容器镜像管理

3.1 三种CLI工具对比

工具 使用场景 说明
ctr 单机containerd管理 containerd本身的CLI
nerdctl 兼容Docker CLI 推荐使用,命令语法与docker一致
crictl K8s节点管理 Kubernetes社区的专用CLI工具

3.2 ctr命令帮助

bash 复制代码
[root@localhost ~] ctr --help
NAME:
   ctr - containerd CLI

USAGE:
   ctr [global options] command [command options] [arguments...]

VERSION:
   v1.6.32

COMMANDS:
   plugins, plugin     provides information about containerd plugins
   version             print the client and server versions
   containers, c, container  manage containers
   content             manage content
   events, event       display containerd events
   images, image, i    manage images
   leases              manage leases
   namespaces, namespace, ns  manage namespaces
   run                 run a container
   snapshots, snapshot  manage snapshots
   tasks, t, task      manage tasks
   install             install a new package
   oci                 OCI tools
   shim                interact with a shim directly
   help, h             Shows a list of commands or help for one command

3.3 查看镜像

bash 复制代码
# 查看镜像列表
[root@localhost ~] ctr images list
REF    TYPE    DIGEST    SIZE    PLATFORMS    LABELS

# 可简写为
[root@localhost ~] ctr images ls
[root@localhost ~] ctr image list
[root@localhost ~] ctr image ls
[root@localhost ~] ctr i ls

3.4 下载镜像

containerd支持OCI标准的镜像,所以可以直接使用docker官方或dockerfile构建的镜像。

bash 复制代码
# 拉取镜像(不能直接写nginx:alpine,需要完整地址)
[root@localhost ~] ctr images pull \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest

# 验证
[root@localhost ~] ctr image ls
REF                                                                              TYPE                              DIGEST                                                                  SIZE    PLATFORMS
054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest  application/vnd.oci.image.index.v1+json  sha256:84ec966e61a8c7846f509da7eb081c55c1d56817448728924a87ab32f12a72fb  68.9MiB  linux/386,linux/amd64,linux/arm/v5,linux/arm/v7,linux/arm64/v8,linux/mips64le,linux/ppc64le,linux/s390x,unknown/unknown

3.5 镜像挂载

方便查看镜像中包含的内容:

bash 复制代码
# 挂载镜像
[root@localhost ~] ctr images mount \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest /mnt
sha256:3c1159cd77f83ede793fc21502ae30b39b04378b6b1b625451d701d555cc1cb9 /mnt

# 查看挂载内容
[root@localhost ~] ls /mnt
bin  boot  dev  docker-entrypoint.d  docker-entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

# 卸载
[root@localhost ~] umount /mnt

3.6 镜像导出

bash 复制代码
# 导出指定平台镜像
[root@localhost ~] ctr i export --platform linux/amd64 nginx.tar \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest

[root@localhost ~] ls nginx.tar
nginx.tar

3.7 镜像删除

bash 复制代码
# 查看删除命令帮助
[root@localhost ~] ctr image rm --help
NAME:
   ctr images delete - remove one or more images by reference

USAGE:
   ctr images delete [command options] [flags] <ref> [<ref>, ...]

OPTIONS:
   --sync  Synchronously remove image and all associated resources

# 删除指定镜像
[root@localhost ~] ctr image rm \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest

# 验证
[root@localhost ~] ctr image ls
REF    TYPE    DIGEST    SIZE    PLATFORMS    LABELS

3.8 镜像导入

bash 复制代码
# 导入镜像
[root@localhost ~] ctr images import --platform linux/amd64 nginx.tar
unpacking 054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest (sha256:84ec966e61a8c7846f509da7eb081c55c1d56817448728924a87ab32f12a72fb)...done

# 验证
[root@localhost ~] ctr image ls
REF                                                                              TYPE                              DIGEST                                                                  SIZE    PLATFORMS
054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest  application/vnd.oci.image.index.v1+json  sha256:84ec966e61a8c7846f509da7eb081c55c1d56817448728924a87ab32f12a72fb  68.9MiB  linux/386,linux/amd64,...

3.9 修改镜像tag

bash 复制代码
# 把长镜像名修改为nginx:latest
[root@localhost ~] ctr images tag \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest \
    nginx:latest

# 验证
[root@localhost ~] ctr image ls
REF                                                                              TYPE                              DIGEST                                                                  SIZE    PLATFORMS
054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest  application/vnd.oci.image.index.v1+json  sha256:84ec966e61a8c7846f509da7eb081c55c1d56817448728924a87ab32f12a72fb  68.9MiB  linux/386,linux/amd64,...
nginx:latest                                                                     application/vnd.oci.image.index.v1+json  sha256:84ec966e61a8c7846f509da7eb081c55c1d56817448728924a87ab32f12a72fb  68.9MiB  linux/386,linux/amd64,...

四、Containerd容器管理

4.1 查看命令帮助

bash 复制代码
# 获取containerd命令帮助
[root@localhost ~] ctr --help

# 获取创建静态容器命令帮助
[root@localhost ~] ctr container --help
NAME:
   ctr containers - manage containers

COMMANDS:
   create    create container
   delete, del, remove, rm  delete one or more existing containers
   info      get info about a container
   list, ls  list containers
   label     set and clear labels for a container
   checkpoint  checkpoint a container
   restore   restore a container from checkpoint

💡 说明 :使用ctr container create命令创建容器后,容器并没有处于运行状态,其只是一个静态的容器 。这个container对象只是包含了运行一个容器所需的资源及配置的数据结构,例如:namespaces、rootfs和容器的配置都已经初始化成功了,只是用户进程(本案例为nginx)还没有启动。需要使用ctr tasks命令才能获取一个动态容器。

bash 复制代码
# 使用ctr run命令可以创建一个静态容器并使其运行,一步到位运行容器
[root@localhost ~] ctr run --help
NAME:
   ctr run - run a container

USAGE:
   ctr run [command options] [flags] Image|RootFS ID [COMMAND] [ARG...]

OPTIONS:
   --rm                remove the container after running, cannot be used with --detach
   --null-io           send all IO to /dev/null
   --log-uri value     log uri
   --detach, -d        detach from the task after it has started execution
   --fifo-dir value    directory used for storing IO FIFOs
   --cgroup value      cgroup path
   --platform value    run image for specific platform
   --cni               enable cni networking for the container
   --runc-binary value specify runc-compatible binary
   --runc-root value   specify runc-compatible root
   --runc-systemd-cgroup start runc with systemd cgroup manager
   --uidmap container-uid:host-uid:length  run inside a user namespace with the specified UID mapping range
   --gidmap container-gid:host-gid:length  run inside a user namespace with the specified GID mapping range
   --cpus value        set the CFS cpu quota (default: 0)
   --cpu-shares value  set the cpu shares (default: 1024)
   --snapshotter value snapshotter name
   --config value, -c value path to the runtime-specific spec config file
   --cwd value         specify the working directory of the process
   --env value         specify additional container environment variables
   --label value       specify additional labels
   --annotation value  specify additional OCI annotations
   --mount value       specify additional container mount
   --net-host          enable host networking for the container
   --privileged        run privileged container
   --read-only         set the containers filesystem as readonly
   --runtime value     runtime name (default: "io.containerd.runc.v2")
   --tty, -t           allocate a TTY for the container
   --pid-file value    file path to write the task's pid
   --memory-limit value memory limit (in bytes) for the container (default: 0)

4.2 查看容器和任务

bash 复制代码
# 查看容器(container表示静态容器,可用c缩写代表container)
[root@localhost ~] ctr container ls
# 可以简写为
[root@localhost ~] ctr c ls
CONTAINER    IMAGE    RUNTIME

# 查看任务(task表示容器里跑的进程,可用t缩写代表task)
[root@localhost ~] ctr task ls
# 可以简写为
[root@localhost ~] ctr t ls
TASK    PID    STATUS

4.3 创建静态容器

bash 复制代码
# 创建静态容器
[root@localhost ~] ctr container create nginx:latest nginx1

# 查看静态容器
[root@localhost ~] ctr container ls
CONTAINER    IMAGE           RUNTIME
nginx1       nginx:latest    io.containerd.runc.v2

# 查看容器详细信息
[root@localhost ~] ctr container info nginx1

4.4 静态容器启动为动态容器

bash 复制代码
# 查看任务
[root@localhost ~] ctr task ls
TASK    PID    STATUS

# 启动task,即表示在容器中运行了进程,即为动态容器
[root@localhost ~] ctr task start -d nginx1
# -d 后台运行

# 查看任务
[root@localhost ~] ctr task ls
TASK      PID      STATUS
nginx1    22614    RUNNING

# 容器是以宿主机进程的方式存在的
[root@localhost ~] ps aux | grep 22614
root     22614  0.0  0.0  11468  7196 ?        Ss   15:40   0:00 nginx: master process nginx -g daemon off;
root     22969  0.0  0.0  12216  1104 pts/0    S+   15:41   0:00 grep --color=auto 22614

4.5 进入容器操作

bash 复制代码
# 为exec进程设定一个id,可以随意输入,只要保证唯一即可,也可使用$RANDOM变量
[root@localhost ~] ctr task exec --exec-id $RANDOM -t nginx1 /bin/sh

4.6 直接运行一个动态容器

bash 复制代码
# --net-host 代表容器的IP就是宿主机的IP(相当于docker里的host类型网络)
[root@localhost ~] ctr run -d --net-host nginx:latest nginx2

4.7 暂停和恢复容器

bash 复制代码
# 暂停容器
[root@localhost ~] ctr tasks pause nginx2
[root@localhost ~] ctr task ls
TASK      PID      STATUS
nginx1    22614    RUNNING
nginx2    25569    PAUSED

# 恢复容器
[root@localhost ~] ctr tasks resume nginx2
[root@localhost ~] ctr task ls
TASK      PID      STATUS
nginx1    22614    RUNNING
nginx2    25569    RUNNING

4.8 停止和删除容器

bash 复制代码
# 停止容器
[root@localhost ~] ctr tasks kill nginx2
[root@localhost ~] ctr tasks ls
TASK      PID      STATUS
nginx1    22614    RUNNING
nginx2    25569    STOPPED

# 删除容器(必须先停止tasks或先删除task,再删除容器)
[root@localhost ~] ctr tasks delete nginx2
[root@localhost ~] ctr tasks ls
TASK      PID      STATUS
nginx1    22614    RUNNING

# 查看静态容器,确认其还存在于系统中
[root@localhost ~] ctr container ls
CONTAINER    IMAGE           RUNTIME
nginx1       nginx:latest    io.containerd.runc.v2

静态容器与动态容器转换图

五、Containerd使用私有容器镜像仓库Harbor

bash 复制代码
# 手动在containerd宿主机上添加此配置信息,如果域名解析已存在忽略
[root@localhost ~] vim /etc/hosts
192.168.108.30 my.harbor.com

💡 说明 :harbor仓库需要提前在192.168.108.30上部署(参考docker教案),镜像需要提前传到harbor上,如果没有使用https可以使用--plain-http指定http协议。

bash 复制代码
# 拉取镜像
[root@localhost ~] ctr image pull --plain-http 192.168.108.30/cloud/nginx:latest

# 上传镜像到Harbor
[root@localhost ~] ctr images tag nginx:latest my.harbor.com/cloud/nginx:latest
[root@localhost ~] ctr image push --platform linux/amd64 --plain-http \
    --user "images_admin:Cloud12#$" my.harbor.com/cloud/nginx:latest
manifest-sha256:6533ddd664582430971e93e69cf343e3bffcceedaaa97d4379c4d7a29f21d47:done
config-sha256:2cd1d97f893f70cee86a38b7160c30e5750f3ed6ad86c598884ca9c6a563a501:done
elapsed: 0.1 s

六、Containerd Namespace管理

containerd中namespace的作用为隔离运行的容器,可以实现运行多个容器。

6.1 列出已有namespace

bash 复制代码
[root@localhost ~] ctr namespace ls
NAME    LABELS
default    # containerd默认工作在default命名空间

# 在docker环境中
[root@docker ~] ctr namespace ls
NAME    LABELS
moby    # docker默认工作在moby空间

6.2 创建namespace

bash 复制代码
[root@localhost ~] ctr namespace create myns
[root@localhost ~] ctr namespace create testns
[root@localhost ~] ctr namespace ls
NAME    LABELS
default
myns
testns

6.3 删除namespace

bash 复制代码
[root@localhost ~] ctr namespace rm testns
testns
[root@localhost ~] ctr namespace ls
NAME    LABELS
default
myns

6.4 在指定namespace中操作

bash 复制代码
# 查看指定namespace中镜像
[root@localhost ~] ctr -n myns images ls
REF    TYPE    DIGEST    SIZE    PLATFORMS    LABELS

# 查看指定namespace中是否有用户进程在运行
[root@localhost ~] ctr -n myns tasks ls
TASK    PID    STATUS

# 在指定namespace中下载容器镜像
[root@localhost ~] ctr -n myns images pull \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest

# 查看
[root@localhost ~] ctr -n myns images ls
REF                                                                              TYPE                              SIZE    PLATFORMS    LABELS
054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest  application/vnd.oci.image.index.v1+json  68.9MiB  linux/386,linux/amd64,...

# 在指定namespace中创建静态容器
[root@localhost ~] ctr -n myns container create \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest mynginx

# 查看在指定namespace中创建的容器
[root@localhost ~] ctr -n myns container ls
CONTAINER    IMAGE                                                                              RUNTIME
mynginx      054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest  io.containerd.runc.v2

# 启动容器
[root@localhost ~] ctr -n myns task start -d mynginx
[root@localhost ~] ctr -n myns tasks ls
TASK      PID     STATUS
mynginx   5873    RUNNING

七、nerdctl实践

nerdctl是containerd的推荐管理工具,命令语法与docker一致。

截止2023-05-24最新版本是v1.4.0。

7.1 安装nerdctl

bash 复制代码
# 下载并安装
[root@localhost ~] wget https://github.com/containerd/nerdctl/releases/download/v1.4.0/nerdctl-1.4.0-linux-amd64.tar.gz
[root@localhost ~] tar -xf nerdctl-1.4.0-linux-amd64.tar.gz -C /usr/bin/

# 配置nerdctl命令自动补全
[root@localhost ~] nerdctl completion bash > /etc/bash_completion.d/nerdctl
[root@localhost ~] source /etc/bash_completion.d/nerdctl

# 下载nerdctl所需要的cni插件
[root@localhost ~] wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz
[root@localhost ~] mkdir -p /opt/cni/bin
[root@localhost ~] tar -xf cni-plugins-linux-amd64-v1.3.0.tgz -C /opt/cni/bin

# 如果nerdctl补全报错,安装bash-completion
[root@localhost ~] yum install -y bash-completion

7.2 配置镜像加速

bash 复制代码
# 编辑containerd配置文件
[root@localhost ~] containerd config default > /etc/containerd/config.toml
[root@localhost ~] vim /etc/containerd/config.toml
# 搜索关键字"config_path",在其下面添加镜像加速参数
[plugins."io.containerd.grpc.v1.cri".registry]
  config_path = "/etc/containerd/certs.d"

# 创建配置目录
[root@localhost ~] mkdir -p /etc/containerd/certs.d/docker.io
[root@localhost ~] vim /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com"

[host."https://054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com"]
  capabilities = ["pull", "resolve"]

# 重启containerd服务生效
[root@localhost ~] systemctl restart containerd

7.3 nerdctl管理镜像

bash 复制代码
# 查看镜像清单
[root@localhost ~] nerdctl image ls
REPOSITORY    TAG    IMAGE ID    CREATED    PLATFORM    SIZE    BLOB SIZE

# 可简写为
[root@localhost ~] nerdctl images

# 下载镜像
[root@localhost ~] nerdctl image pull busybox
[root@localhost ~] nerdctl pull httpd

# 查看
[root@localhost ~] nerdctl image ls
REPOSITORY    TAG       IMAGE ID        CREATED          PLATFORM      SIZE       BLOB SIZE
busybox       latest    f9a104fddb33    19 minutes ago   linux/amd64   4.1 MiB    2.1 MiB
httpd         latest    fbc12199ccad    44 seconds ago   linux/amd64   152.4 MiB  55.8 MiB

# 删除镜像
[root@localhost ~] nerdctl image rm httpd
[root@localhost ~] nerdctl rmi busybox

# 给镜像打标签
[root@localhost ~] nerdctl tag busybox busybox_container
[root@localhost ~] nerdctl images
REPOSITORY          TAG       IMAGE ID        CREATED          PLATFORM      SIZE       BLOB SIZE
busybox             latest    f9a104fddb33    34 minutes ago   linux/amd64   4.1 MiB    2.1 MiB
busybox_container   latest    f9a104fddb33    3 seconds ago    linux/amd64   4.1 MiB    2.1 MiB

# 导出镜像为文件
[root@localhost ~] nerdctl image save busybox -o busybox.tar
# 可简写为
[root@localhost ~] nerdctl save busybox -o busybox.tar

# 导入tar文件中的镜像
[root@localhost ~] nerdctl image load -i busybox.tar
# 可简写为
[root@localhost ~] nerdctl load -i busybox.tar

# 查看镜像构建历史
[root@localhost ~] nerdctl image history busybox
SNAPSHOT                                                                     CREATED         CREATED BY                                      SIZE      COMMENT
sha256:65014c70e84b6817fac42bb201ec5c1ea460a8da246cac0e481f5c9a9491eac0    10 months ago   BusyBox 1.37.0 (glibc), Debian 12               4.1 MiB

# 查看镜像详细信息
[root@localhost ~] nerdctl image inspect busybox

# 删除所有未使用的镜像
[root@localhost ~] nerdctl image prune --all --force

7.4 nerdctl管理容器

bash 复制代码
# 查看容器清单
[root@localhost ~] nerdctl container ls
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

# 可简写为
[root@localhost ~] nerdctl ps

# 查看所有容器(包括未运行的)
[root@localhost ~] nerdctl container ls -a

# 创建并运行容器
[root@localhost ~] nerdctl container run -it ubuntu
root@0da9aad32119:/# exit
exit

# 可简写为
[root@localhost ~] nerdctl run -it ubuntu

# 查看所有容器
[root@localhost ~] nerdctl container ls -a
CONTAINER ID    IMAGE                                COMMAND        CREATED          STATUS                      PORTS    NAMES
0da9aad32119    docker.io/library/ubuntu:latest      "/bin/bash"    3 minutes ago    Exited (130) 2 minutes ago           ubuntu-0da9a

# 删除容器
[root@localhost ~] nerdctl container rm 0da9aad32119

# 删除所有未运行的容器
[root@localhost ~] nerdctl container prune --force
Deleted Containers:
3778651cfacba1cd489b065ff7017b272b9edddc71211e2a6e567d9d0ec8ac54
3e8221845ab479f18a091c04443d26632946c9ced264c21a490f6b3052bde0b2

# 重命名容器
[root@localhost ~] nerdctl container run --name ubuntu-1 ubuntu
[root@localhost ~] nerdctl container rename ubuntu-1 ubuntu

# 停止和启动容器
[root@localhost ~] nerdctl container run -d --name nginx1 nginx
[root@localhost ~] nerdctl container ls --format "{{.Names}} {{.Status}}"
nginx1 Up
[root@localhost ~] nerdctl container stop nginx1
nginx1
[root@localhost ~] nerdctl container start nginx1
nginx1

# 重启容器
[root@localhost ~] nerdctl container restart nginx1

# 暂停和取消挂起容器
[root@localhost ~] nerdctl container pause nginx1
nginx1
[root@localhost ~] nerdctl container unpause nginx1
nginx1

# 给容器发信号,默认发KILL信号
[root@localhost ~] nerdctl container kill nginx1

# 在运行的容器内部执行命令
[root@localhost ~] nerdctl container exec -it nginx1 bash
root@945c89b61aaf:/# exit

# 将宿主机文件复制给容器
[root@localhost ~] nerdctl container cp /etc/hostname nginx1:
[root@localhost ~] nerdctl container exec nginx1 ls hostname
hostname

# 查看容器详细信息
[root@localhost ~] nerdctl container inspect nginx1

# 显示容器console终端内容
[root@localhost ~] nerdctl container logs nginx1

# 显示宿主机和容器之间端口映射关系
[root@localhost ~] nerdctl container run --name nginx -d -p 8080:80 nginx
[root@localhost ~] nerdctl container port nginx
80/tcp -> 0.0.0.0:8080

# 将容器提交为镜像
[root@localhost ~] nerdctl commit nginx nginx_containerd
sha256:6e60d18c9e7f7968f49edfacae16e39df2a995d3119b0b23356fd501cd8348a6

7.5 nerdctl管理网络

containerd中的网络与Docker类似,所有网络接口默认都是虚拟接口。

当使用nerdctl创建容器时,nerdctl命令会创建一个名称为bridge的Linux网桥(其上有一个nerdctl0内部接口),利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做veth pair)。containerd默认指定了nerdctl0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

bash 复制代码
# 运行容器
[root@localhost ~] nerdctl run -d busybox -- sleep infinity
b721795e02103578656152662f414e88f32191e64976ccafe60c4af10a8fa8c8

# 查看容器
[root@localhost ~] nerdctl container ls
CONTAINER ID    IMAGE                            COMMAND           CREATED          STATUS    PORTS    NAMES
b721795e0210    docker.io/library/busybox:latest "sleep infinity"  12 seconds ago   Up                 busybox-b7217

# 查看容器网络
[root@localhost ~] nerdctl exec busybox-b7217 -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
2: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
    link/ether f6:fc:0b:35:5e:2c brd ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff
    inet 10.4.0.18/24 brd 10.4.0.255 scope global eth0

# 查看宿主机网络
[root@localhost ~] ip a
3: nerdctl0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 3a:1a:93:b7:ea:d7 brd ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff
    inet 10.4.0.1/24 brd 10.4.0.255 scope global nerdctl0
5: veth790d9140@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master nerdctl0 state UP group default
    link/ether 3a:fa:0e:fd:27:5b brd ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff link-netnsid 0

# 查看网络清单
[root@localhost ~] nerdctl network ls
NETWORK ID      NAME      FILE
17f29b073143    bridge    /etc/cni/net.d/nerdctl-bridge.conflist
                host
                none

# 查看网络详细信息
[root@localhost ~] nerdctl network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121",
        "IPAM": {
            "Config": [
                {
                    "Subnet": "10.4.0.0/24",
                    "Gateway": "10.4.0.1"
                }
            ]
        },
        "Labels": {
            "nerdctl/default-network": "true"
        }
    }
]

# 查看网桥
[root@localhost ~] brctl show
bridge name    bridge id           STP enabled    interfaces
nerdctl0       8000.3a1a93b7ead7   no             veth790d9140

nerdctl网络架构图

7.6 nerdctl管理存储

bash 复制代码
# 查看volume命令帮助
[root@localhost ~] nerdctl volume --help
Manage volumes

Usage: nerdctl volume [flags]

Commands:
  create    Create a volume
  inspect   Display detailed information on one or more volumes
  ls        List volumes
  prune     Remove all unused local volumes
  rm        Remove one or more volumes

# 使用-v选项将本地目录挂载给容器实现数据持久化
[root@localhost ~] mkdir /data
[root@localhost ~] nerdctl run -d -v /data:/data busybox -- sleep infinity
0d0a1646169a199e3038851f86b82bf03ac2db6ffd8ea3e875789d2a6d1a000

[root@localhost ~] touch /data/f1
[root@localhost ~] nerdctl exec busybox-d00a1 -- ls /data
f1

# 直接写容器目录,会自动生成目录
[root@localhost ~] nerdctl run -d -v /data busybox -- sleep infinity
29c94622886a219c93b5f6cd1c1ab190f998c66e3b4cbce75437507803b82eea
[root@localhost ~] nerdctl exec busybox-29c94 -- touch /data/f2

# 指定宿主机生成的目录名为data
[root@localhost ~] nerdctl run -d -v data:/data busybox -- sleep infinity
1b1fc00e88471a5abd8787bae438ab8d5ab08f4ec4fa073805407f9fffe2fe73
[root@localhost ~] nerdctl exec busybox-1b1fc -- touch /data/f3

# 查看volume
[root@localhost ~] nerdctl volume ls
VOLUME NAME                                                          DIRECTORY
0c70033c26bcf456d9a0dc3f7dfe723f232e48de2c8898bf987f8aeebacc1c7    /var/lib/nerdctl/1935db59/volumes/default/0c70033c26bcf456d9a0dc3f7dfe723f232e48de2c8898bf987f8aeebacc1c7/_data
data                                                                /var/lib/nerdctl/1935db59/volumes/default/data/_data

nerdctl存储挂载示意图

7.7 nerdctl管理namespace

bash 复制代码
# 查看namespace命令帮助
[root@localhost ~] nerdctl namespace --help
Unrelated to Linux namespaces and Kubernetes namespaces

Usage: nerdctl namespace [flags]

Aliases: namespace, ns

Commands:
  create    Create a new namespace
  inspect   Display detailed information on one or more namespaces.
  ls        List containerd namespaces
  remove    Remove one or more namespaces
  update    Update labels for a namespace

# 查看namespace
[root@localhost ~] nerdctl namespace ls
NAME       CONTAINERS    IMAGES    VOLUMES    LABELS
default    1             0         4          2

八、crictl实践

8.1 crictl命令介绍

crictl命令是遵循CRI接口规范的一个命令行工具,通常用它来检查和管理kubelet节点上的容器运行时和镜像。

在kubernetes集群环境中,当我们执行kubectl命令时,kubelet代理会自动调用crictl命令管理镜像和容器。

手动执行crictl命令时,一般用于查看镜像和容器。

8.2 crictl命令安装

bash 复制代码
# 配置kubernetes源
[root@localhost ~] yum install -y cri-tools

8.3 crictl命令配置

使用crictl命令之前,需要先配置/etc/crictl.yaml

bash 复制代码
# 配置crictl后端运行时使用containerd
[root@localhost ~] vim /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 5
debug: false

# 也可以通过命令进行设置
[root@localhost ~] crictl config runtime-endpoint unix:///run/containerd/containerd.sock
[root@localhost ~] crictl config image-endpoint unix:///run/containerd/containerd.sock

8.4 crictl命令实践

bash 复制代码
# 查看帮助信息
[root@localhost ~] crictl
NAME:
   crictl - client for CRI

VERSION:
   v1.30.1

COMMANDS:
   attach      attach to a running container
   create      create a new container
   exec        Run a command in a running container
   version     Display runtime version information
   images, image, img  List images
   inspect     Display the status of one or more containers
   inspecti    Return the status of one or more images
   imagefsinfo Return image filesystem info
   inspectp    Display the status of one or more pods
   logs        Fetch the logs of a container
   port-forward Forward local port to a pod
   ps          List containers
   pull        Pull an image from a registry
   run         Run a new container inside a sandbox
   runp        Run a new pod
   rm          Remove one or more containers
   rmi         Remove one or more images
   rpm         Remove one or more pods
   pods        List pods
   start       Start one or more created containers
   info        Display information of the container runtime
   stop        Stop one or more running containers
   stopp       Stop one or more running pods
   update      Update one or more running containers
   config      Get and set crictl client configuration options
   stats       List container(s) resource usage statistics
   statsp      List pod statistics
   metricsp    List pod metrics
   completion  Output shell completion code
   checkpoint  Checkpoint one or more running containers
   runtime-config  Retrieve the container runtime configuration
   events, event  Stream the events of containers

# 拉取镜像
[root@localhost ~] crictl pull \
    054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest
Image is up to date for sha256:9f33606b36859ee2db3b761a893fb7c2fc8a13c0fe5f24e304b129f3caf499ad

# 查看镜像
[root@localhost ~] crictl images
IMAGE                    TAG       IMAGE ID        SIZE
docker.io/library/httpd  latest    65005131d37e9   45.2MB

8.5 命令行对照表

功能 docker podman nerdctl ctr crictl
查看容器状态 docker ps podman ps nerdctl ps ctr task ls / ctr container ls crictl ps
查看镜像 docker images podman images nerdctl images ctr image ls crictl images

8.6 nerdctl vs crictl

如果您还对Docker比较怀旧的话,执行alias docker=nerdctl这样的命令后,您依然可以体验到与Docker相似的感觉。

nerdctl和crictl都是用于管理和操作容器的命令行工具,但是它们在开发者、设计目的和功能上有所不同:

nerdctl

  • 开发者:由Docker的创始人之一,也是containerd项目的主要贡献者Akihiro Suda开发
  • 设计目的:nerdctl是一个兼容Docker CLI的containerd CLI,意味着大部分Docker命令可以在nerdctl中运行
  • 功能:它可以管理容器的生命周期,如创建、运行、停止和删除容器。此外,它还支持镜像管理、网络管理、卷管理等

crictl

  • 开发者:由Kubernetes项目社区开发
  • 设计目的:crictl是一个命令行接口,用于与任何实现了Kubernetes容器运行时接口(CRI)的容器运行时进行交互,例如containerd、CRI-O等
  • 功能:它主要用于调试,可以从Kubernetes API Server的角度检查和理解容器运行时的行为。它允许用户直接与容器运行时进行交互,实现容器生命周期管理、镜像管理等

总结:二者主要区别在于它们的使用场景和目标用户并不完全相同。nerdctl更适合需要Docker CLI兼容性的用户,而crictl则更适合需要调试和理解Kubernetes CRI容器运行时行为的用户。

写在最后

本文系统讲解了containerd的核心知识:

章节 核心内容
Containerd介绍 前世今生、架构原理、性能对比
Containerd安装 YUM方式、二进制方式
镜像管理 ctr images的完整操作
容器管理 静态容器、动态容器、task管理
Harbor对接 私有仓库的拉取和推送
Namespace管理 命名空间的创建、删除、隔离
nerdctl实践 兼容Docker CLI的完整操作
crictl实践 K8s节点管理工具

核心结论

  1. Containerd是K8s默认的容器运行时,性能优于Docker
  2. ctr是containerd的原生CLI,适合单机管理
  3. nerdctl兼容Docker CLI,是推荐的日常管理工具
  4. crictl是K8s专用的CLI,用于调试和管理K8s节点
  5. Namespace实现了容器隔离,不同namespace中的资源互不可见
  6. alias docker=nerdctl 可以让你继续使用熟悉的Docker命令

📌 核心命令速查

bash 复制代码
# ctr命令
ctr version                              # 查看版本
ctr images ls                            # 查看镜像
ctr images pull <image>                  # 下载镜像
ctr images tag <source> <target>         # 修改tag
ctr images export <file> <image>         # 导出镜像
ctr images import <file>                 # 导入镜像
ctr container create <image> <name>      # 创建静态容器
ctr container ls                         # 查看静态容器
ctr task start -d <container>            # 启动动态容器
ctr task ls                              # 查看任务
ctr task exec --exec-id <id> -t <container> <cmd>  # 进入容器
ctr task pause <container>               # 暂停容器
ctr task resume <container>              # 恢复容器
ctr task kill <container>                # 停止容器
ctr task delete <container>              # 删除task
ctr namespace ls/create/rm               # namespace管理

# nerdctl命令(兼容docker)
nerdctl images                           # 查看镜像
nerdctl pull <image>                     # 下载镜像
nerdctl run -it <image>                  # 运行容器
nerdctl ps -a                            # 查看所有容器
nerdctl exec -it <container> <cmd>       # 进入容器
nerdctl stop/start/restart <container>   # 停止/启动/重启
nerdctl rm <container>                   # 删除容器
nerdctl volume ls                        # 查看volume
nerdctl network ls                       # 查看网络
nerdctl namespace ls                     # 查看namespace

# crictl命令
crictl images                            # 查看镜像
crictl pull <image>                      # 下载镜像
crictl ps                                # 查看容器
crictl pods                              # 查看pod
crictl config                            # 配置

查看任务

ctr task exec --exec-id -t # 进入容器

ctr task pause # 暂停容器

ctr task resume # 恢复容器

ctr task kill # 停止容器

ctr task delete # 删除task

ctr namespace ls/create/rm # namespace管理

nerdctl命令(兼容docker)

nerdctl images # 查看镜像

nerdctl pull # 下载镜像

nerdctl run -it # 运行容器

nerdctl ps -a # 查看所有容器

nerdctl exec -it # 进入容器

nerdctl stop/start/restart # 停止/启动/重启

nerdctl rm # 删除容器

nerdctl volume ls # 查看volume

nerdctl network ls # 查看网络

nerdctl namespace ls # 查看namespace

crictl命令

crictl images # 查看镜像

crictl pull # 下载镜像

crictl ps # 查看容器

crictl pods # 查看pod

crictl config # 配置


📝 本文首发于个人技术博客,欢迎交流讨论。如有错误,恳请指正。

相关推荐
Tairitsu_H1 小时前
[Linux] 编辑器vim、编译器和动静态链接
linux·vim·gcc·g++·动态链接·静态链接
lzx_0021 小时前
Linux指令(一) 简单了解部分指令
linux
小则又沐风a1 小时前
深入理解TCP协议----滑动窗口,流量控制
linux·网络协议·tcp
酣大智4 小时前
网络协议计时器 & 发包间隔汇总
运维·网络·tcp/ip
云运维笔记10 小时前
IP地址从入门到精通:网络通信的核心基石
运维·网络·计算机网络
bosins11 小时前
WSL2 启动即崩溃?调整超时参数解决 Docker 阻塞问题
linux·docker·wsl
web守墓人12 小时前
【goed/ui】自定义组件设计思想篇
linux·windows·ui·golang
码农学院12 小时前
企业官网改版后 AI 引用归零的复盘:301 重定向断层让爬虫索引掉线,修复方案与恢复数据
运维·geo优化·ai优化aio
kuroomi12 小时前
Ingress-Nginx与kubernetes 网络
linux·运维·网络·kubernetes