linux学习(十六)(集装箱(ulimits,cgroups,容器运行时,Docker))

集装箱

容器化是一种虚拟化方法,涉及将应用程序封装在具有自己的隔离作环境的容器中。这种高级方法使应用程序在从一个计算环境移动到另一个计算环境时能够可靠、快速地运行。在 Linux 中,这项技术可以通过各种开源平台(如 Docker 和 Kubernetes)来利用。

容器通常与虚拟机 (VM) 进行比较。但是,与需要整个作系统来运行应用程序的 VM 不同,容器共享主机系统的用户空间。这就是为什么它们更轻、更快。

在 Linux 中,Docker 是一种用于容器化的常用工具。以下是如何在 Docker 中运行容器的基本示例:

astro-code 复制代码
docker run -it ubuntu bash

此命令从 Docker Hub 中提取 Ubuntu 映像,并将其作为容器运行在您的系统上,从而在容器内为您提供交互式终端 ()。这只是一个简单的用例,Docker 可用于管理涉及许多容器的复杂应用程序。-it

好的,用「快递集装箱」的比喻来解释容器化技术,让你轻松理解 Docker 的原理和优势:


​容器化 = 用标准化集装箱运输货物

想象你要运输一批货物(应用程序),传统方式(虚拟机)和现代方式(容器)的区别:

  • 传统轮船(虚拟机)​:每艘船都自带完整发动机、船员、燃料 → 笨重且启动慢
  • 现代集装箱(容器)​:所有货物打包进标准箱子,共用一艘大船的发动机和船员 → 轻便高效

Docker 容器就像这些标准集装箱,让应用程序在不同环境中无缝运行!


​核心概念对比表

概念 虚拟机(VM) Docker 容器
​体积 庞大(包含完整操作系统) 轻量(仅打包应用和依赖库)
​启动速度 分钟级 秒级
​资源占用 高(每个 VM 独占资源) 低(共享主机内核)
​隔离性 完全隔离 进程级隔离
​镜像来源 ISO 镜像 Docker Hub(官方仓库)

​Docker 命令解析

复制代码
docker run -it ubuntu bash
  • docker run:启动一个新容器
  • -it:开启交互式终端(就像进入集装箱内部操作)
  • ubuntu:使用 Ubuntu 官方镜像(相当于空集装箱的模板)
  • bash:在容器内运行 bash shell(拿到集装箱的控制权)

效果 :你会进入一个纯净的 Ubuntu 环境,与主机系统隔离,可以随意安装软件做测试,退出后不留痕迹。


​容器化技术的四大优势

  1. 环境一致性
    • 开发环境 → 测试环境 → 生产环境 100% 一致,告别「在我电脑上能跑」的问题
  2. 快速部署
    • 启动容器只需 1 秒,而启动虚拟机需要几分钟
  3. 资源高效
    • 一台物理机可以运行上百个容器(虚拟机通常只能跑十几个)
  4. 版本控制
    • 镜像可以像 Git 一样管理版本,随时回滚到任意历史版本

​实际应用场景

  1. 开发环境

    复制代码
    # 启动一个包含 Node.js 的容器
    docker run -it -v $(pwd):/app node:18 bash
    • -v 参数把主机当前目录挂载到容器的 /app(实时同步代码)
  2. Web 服务

    复制代码
    # 运行 Nginx 容器并映射 80 端口
    docker run -d -p 80:80 --name my-nginx nginx
    • -d 后台运行,-p 端口映射,访问 http://localhost 即可看到 Nginx 页面
  3. 批量测试

    复制代码
    # 并行启动 10 个临时容器做压力测试
    for i in {1..10}; do
      docker run --rm alpine sh -c "echo 测试容器 $i"
    done

​学习路径建议

  1. 掌握基础命令
    • docker pull(下载镜像)、docker ps(查看运行中的容器)、docker exec(进入已运行的容器)
  2. 理解镜像构建
    • 编写 Dockerfile 定制自己的镜像(类似编写集装箱的设计图纸)
  3. 学习编排工具
    • 使用 docker-compose.yml 管理多容器应用(比如同时启动 Web + 数据库)
  4. 进阶集群管理
    • 学习 Kubernetes(K8s)管理跨主机的容器集群

通过容器化技术,你可以像搭积木一样快速构建和销毁应用环境,大大提升开发和运维效率! 🚀

了解 Ulimits

基于 Linux 的容器化技术(如 Docker)利用"ulimits"作为安全功能之一,以控制每个正在运行的容器的资源消耗。Ulimits(用户限制)是 Linux 内核的一项功能,用于限制任何单个用户可以消耗的资源。这些资源包括打开的文件句柄、锁定的物理内存等。

有效使用,ulimits 可以防止特定容器中的流氓或错误进程耗尽服务器的资源,并为其他容器或进程造成拒绝服务情况。

在容器化环境中,巧妙地管理这些资源限制以确保所有容器的最佳性能和安全性至关重要。

复制代码
# To see current ulimits:
ulimit -a

# To set a specific ulimit (soft limit), for example file handles:
ulimit -n 1024

正确配置和理解 ulimit(尤其是在容器化环境中)是 Linux 中系统管理的重要组成部分。

用快递仓库管理系统的比喻来帮你理解 ulimits 的工作原理和重要性:


📦 核心概念比喻

  • 服务器 = 物流中心总部

  • 容器 = 快递分拣区

  • ulimits = 每个分拣区的资源配额管控系统


🔑 关键限制类型(现实对照)

ulimit参数 资源类型 快递场景比喻 典型风险案例
-n 打开文件数 分拣区同时处理的包裹数量上限 某个分拣区堆积包裹导致传送带瘫痪
-u 用户进程数 分拣区允许雇佣的临时工人数 工人过多导致休息室拥挤
-m 内存锁定量 分拣区专用储物柜容量 包裹占用所有储物空间
-t CPU时间(秒) 每日最大工作时长 分拣作业超时影响其他区域
-l 内存锁定(KB) 分拣区固定使用的叉车数量 叉车被独占导致其他区域停工

🛠️ 常用命令操作

查看当前配额(类似检查分拣区监控屏)
复制代码
ulimit -a

示例输出:

复制代码
open files      (-n) 1024      # 最多同时处理1024个包裹
max user processes (-u) 512    # 最多雇佣512个临时工
virtual memory  (-v) unlimited # 储物柜容量不限
临时调整配额(分拣区主管紧急调控)
复制代码
ulimit -n 2048  # 将当前会话的文件处理上限提升到2048
永久配置(修改总部管理系统)
复制代码
# 编辑系统配置文件
sudo vim /etc/security/limits.conf

# 添加如下内容:
* soft nofile 4096   # 默认软限制
* hard nofile 65535  # 管理员可调整的上限

* 👉 代表对所有用户生效(可以指定特定用户)。
soft nofile 4096 👉 软限制,默认最多可打开 4096 个文件。
hard nofile 65535 👉 硬限制,管理员可以修改的最大值是 65535。
💡 什么是"软限制"和"硬限制"?
软限制(soft limit)
默认值,进程最多能打开的文件数。
用户可以临时提高,但不能超过硬限制。
硬限制(hard limit)
绝对上限,管理员(root)可以修改,但普通用户不能超过这个值。

🐳 容器化场景特别说明

Docker 默认继承宿主机的 ulimit 设置,但可以通过参数覆盖:

1. 运行容器时指定
复制代码
docker run --ulimit nofile=1024:2048 --ulimit nproc=512 alpine
1024:2048 表示软限制1024,硬限制2048

相当于给这个分拣区配置:

日常最多处理1024包裹(自动报警)
| 紧急情况可临时提升到2048(需主管授权)
2. 修改Docker守护进程配置
复制代码
# 编辑docker配置文件
sudo vim /etc/docker/daemon.json

# 添加默认限制
{
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 65535,
      "Soft": 32768
    }
  }
}
复制代码

⚠️ 常见问题排查

场景:容器报错 "Too many open files"

诊断步骤:

  1. 进入容器检查当前限制

    复制代码
    docker exec -it mycontainer sh
    ulimit -n
    复制代码
  2. 对比进程实际打开文件数

    复制代码
    lsof -p <进程PID> | wc -l
    复制代码
  3. 解决方案:

    复制代码
    # 重新运行容器时调整限制
    docker run --ulimit nofile=65536:65536 myimage
    复制代码

📊 配置建议(物流中心优化方案)

限制类型 开发环境建议 生产环境建议 特别说明
打开文件数 4096 65535 Web服务器需要更高配置
用户进程数 1024 8192 微服务架构需适当提高
栈内存 8192KB 65535KB Java应用需要更大栈空间
锁定内存 64KB 256KB 数据库服务可能需要更高配置

💡 最佳实践技巧

  1. 软硬限制搭配:

    • 软限制(Soft Limit):日常操作阈值(自动生效)

    • 硬限制(Hard Limit):最大允许值(需root权限调整)

  2. 动态调整技巧:

    复制代码
    # 查看nginx进程的当前限制
    cat /proc/$(pgrep nginx)/limits
    
    # 在容器启动脚本中自动优化
    #!/bin/sh
    ulimit -n 65535
    exec your-app
    
    exec your-app
    
    exec 用于替换当前 shell 进程,直接执行 your-app,不会创建新的进程。
    这样,your-app 继承 ulimit -n 65535 设定的高文件句柄限制。
    避免进程嵌套,使 your-app 变成主进程,让 docker stop、systemctl 能正确管理它。
    复制代码
  3. 跨系统注意事项:

    • 在Kubernetes中需要通过 securityContext 配置

      复制代码
      securityContext:
        limits:
          cpu: "1"
          memory: "512Mi"
      复制代码

(扩展思考:结合 cgroups 理解更深层次的资源隔离,ulimit 是应用层的限制,而 cgroups 是内核级的资源控制)

Cgroups 组

Cgroups 是控制组的缩写,是一种 Linux 内核功能,允许将进程组织成分层组。它在容器化中的关键作用是它能够限制、计算和隔离这些进程组的资源使用情况(CPU、内存、磁盘 I/O 等)。

在容器化的背景下,轻量级隔离环境在同一台主机上运行,cgroups 对于高效的资源管理至关重要。通过使用 cgroups,可以确保每个容器不会垄断主机资源,从而提高整体系统的稳定性和性能。

以下是如何为容器创建新的 cgroup 的示例:

astro-code 复制代码
# Create a new cgroup for a container;
sudo cgcreate -g cpu:/my_new_container

# Assign the current shell's process to the new cgroup;
echo $$ | sudo tee /sys/fs/cgroup/cpu/my_new_container/tasks

# Limit the CPU usage of the cgroup to 20%;
echo 200000 | sudo tee /sys/fs/cgroup/cpu/my_new_container/cpu.cfs_quota_us

在此代码段中,我们用于创建新的 cgroup,然后将当前进程添加到该 cgroup,最后设置 CPU 限制。cgcreate

我试着用「分蛋糕」的比喻来解释 Cgroups 和这段代码的原理,保证小白也能秒懂:


​Cgroups 本质

想象你有一块大蛋糕(服务器资源 ),Cgroups 就像 ​切蛋糕的规则,它决定了:

  • 谁能吃多少(CPU/内存限制)
  • 谁先吃(优先级控制)
  • 谁不能吃(资源隔离)

如果没有 Cgroups,所有人(进程)都会抢蛋糕,导致有些程序撑死,有些饿死。


​代码逐行解析

​1. 创建新蛋糕分配规则
复制代码
sudo cgcreate -g cpu:/my_new_container
  • 作用 :在 CPU 资源下,新建一个叫 my_new_container 的分组规则
  • 类比:妈妈在蛋糕盒上贴一个新标签:"这是给小明(容器)的专属区域"

​2. 把熊孩子加入分组
复制代码
echo $$ | sudo tee /sys/fs/cgroup/cpu/my_new_container/tasks
  • $$:当前 shell 的进程 ID(比如你的终端窗口)
  • tasks 文件:记录这个分组要管哪些进程
  • 类比:把正在抢蛋糕的小明(当前终端)拉到他的专属座位

​3. 限制最大食量
复制代码
echo 200000 | sudo tee /sys/fs/cgroup/cpu/my_new_container/cpu.cfs_quota_us
  • cpu.cfs_quota_us:允许使用的 CPU 时间片上限
  • 200000 微秒 = 0.2 秒(每秒最多只能用 20% 的 CPU)
  • 类比:规定小明每秒最多只能吃 20% 的蛋糕,剩下的留给其他人

💡 延伸知识:默认周期 cpu.cfs_period_us=100000(1秒),所以 quota=200000 就是 200000/100000=2 核?不!实际是 20% 的单核时间片(具体计算需要结合 CPU 核数)


​容器中的实际应用

当你在 Docker 中运行容器时:

复制代码
docker run --cpus=0.2 my_image  # 其实背后自动做了类似的事情!

Docker 会悄悄:

  1. 创建 Cgroup (名字像 /docker/容器ID)
  2. 把容器进程加入分组
  3. 设置 CPU 配额 (对应 --cpus 参数)

​Cgroups 核心能力

能力 类比解释 实际应用场景
​资源限制 小明最多吃 2 块蛋糕 限制容器内存为 512MB
​优先级控制 小红可以优先选蛋糕 给数据库容器分配更高 CPU 权重
​资源统计 记录小明吃了多少蛋糕 监控容器的 CPU 使用率
​隔离性 小明吃蛋糕不影响其他人 避免容器间资源争抢导致卡顿

​为什么容器需要 Cgroups?

想象你开了一家餐厅(宿主机):

  • 每桌客人(容器)都以为自己是唯一顾客
  • 但厨房(物理资源)只有一套
  • Cgroups 就是服务员 ,确保:
    • 每桌都能及时上菜(资源分配)
    • 不会有点 100 份牛排的客人吃垮厨房(资源限制)

​动手实验小贴士

  1. 用 cat /proc/cgroups 查看系统支持的 Cgroups 类型
  2. 容器运行时用 docker stats 实时观察资源限制效果
  3. 修改 cpu.shares 体验优先级控制(类比:VIP 客户优先权)

希望这个「蛋糕版 Cgroups 教程」让你吃透了核心原理! 🍰

容器运行时

Linux 中的容器运行时是指负责运行容器的软件。每个容器运行时都提供特定的功能和优势,其常见功能包括映像传输和存储、容器执行和监督、低级网络和卷交互等。

在 Linux 下,常用的容器运行时选项包括:

  • Docker:Docker 可能是最常见的容器运行时,具有出色的生态系统和出色的社区支持。
  • Containerd:Containerd 最初是作为在 Docker 中使用的高级容器运行时层开发的,现在通常用作独立的运行时。
  • CRI-O:专为 Kubernetes 优化的轻量级容器运行时。

在容器化中,了解容器运行时的作用是不可或缺的,因为它有助于更好地设计和运行容器化应用程序。这进一步确保了资源的可靠性和有效利用。每个容器运行时都适用于不同的使用案例,因此最好了解它们的优缺点以有效地使用它们。

​用「餐厅后厨」比喻容器运行时,秒懂原理!​


​容器运行时的本质

想象你要开一家餐厅(运行容器化应用 ),​容器运行时就是你的后厨团队,负责:

  • 食材采购(下载镜像)
  • 按菜谱做菜(创建容器)
  • 管理厨具(分配 CPU/内存)
  • 收拾餐盘(销毁容器)

不同餐厅规模需要不同的后厨配置,这就是为什么会有多种容器运行时!


​三大主厨对比

主厨类型 擅长领域 适合餐厅 优点 缺点
​Docker 全能型主厨 中小餐厅、快速出餐 功能全、菜谱多(镜像生态好) 团队稍臃肿
​Containerd 高效备菜助手 连锁店中央厨房 轻量专注、只做核心工作 需要自己配服务员
​CRI-O 大型宴会定制主厨 超大规模宴会(K8S) 专为宴会优化,无缝对接宴会经理 只服务高端客户

​举个栗子🌰

​1. 你开了一家网红奶茶店
  • 需求:快速推出新品,顾客喜欢一键操作

  • 选谁 :Docker(自带菜单设计+食材采购+制作一条龙)

  • 命令行体验 :

    复制代码
    docker run --name 霸气芒果冰 -p 80:80 -d 芒果冰镜像

    就像喊:"主厨!按'霸气芒果冰'菜谱做一杯,放3号窗口!"


​2. 你开了连锁火锅店
  • 需求:后厨高效运转,不搞花里胡哨

  • 选谁 :Containerd(专心切菜煮肉,其他交给前台管理)

  • 底层原理 :

    复制代码
    ctr images pull 火锅底料镜像  # 专心备菜
    ctr run 火锅底料镜像 重庆麻辣锅  # 开火煮锅

​3. 你承包奥运会运动员食堂
  • 需求 :必须适配国际餐饮管理系统(Kubernetes)

  • 选谁 :CRI-O(专为大型系统定制)

  • 后台协作 :

    复制代码
    kubectl create -f 奥运餐标.yaml  # 告诉管理系统要500份营养餐

    CRI-O 默默按国际标准高效出餐,绝不拖后腿!


​为什么需要这么多运行时?

  • Docker :适合人类直接操作(比如开发测试)
  • Containerd :适合机器调度(藏在自动化工具背后)
  • CRI-O :专为K8s 生态系统深度优化

就像:

  • 自家做饭用多功能电饭煲(Docker)
  • 快餐店用专业蒸烤箱(Containerd)
  • 食品工厂用全自动生产线(CRI-O)

​技术底料揭秘

所有运行时都依赖Linux 底层技术:

  • Cgroups:控制每道菜用多少燃气(CPU)和冰箱空间(内存)
  • Namespace:让每道菜以为自己独享厨房(隔离性)
  • 镜像分层:像预制菜,复用通用配料节省时间

当你说"限制容器内存"时,其实是运行时在后台调用了 Cgroups 的"冰箱空间分配规则"!


​一张图看懂协作关系

复制代码
  人类点餐(docker run)
    ↓
Docker 主厨 → 喊话 Containerd 备菜小弟
    ↓               ↓
调用 Linux 厨房设备(Cgroups/Namespace)  
    ↓
上菜!🍲

Kubernetes 这类管理系统就像美团接单平台,把订单自动分给 CRI-O 这类专用厨师团队。


​小白实践指南

  1. 新手入门:直接用 Docker,官网教程多
  2. 生产环境:Containerd + 自动化工具(比如 K8s)
  3. 深度玩转 K8s:CRI-O 是官方推荐"御用厨师"

下次听到"容器运行时",记住:​它就是你的后厨总监!🍴

容器化下的 Docker

Docker 是一个广泛使用的开源平台,它利用作系统级虚拟化(通常称为"容器化")来有效地开发、交付和运行应用程序。Docker 和容器化(尤其是在 Linux 生态系统中)通过为应用程序及其依赖项提供轻量级和隔离的作环境(称为容器),彻底改变了软件开发工作流程。Docker 允许开发团队将应用程序与所需的所有部分(例如库和其他依赖项)打包在一起,并将其部署为单个包。

在 Linux 中,每个 Docker 容器都直接与 Linux 内核交互。由于巧妙地使用了命名空间和 cgroups 等 Linux 内核功能,这些容器提供了隔离的空间来运行进程,同时共享相同的作系统,从而比传统虚拟机的开销更少。

以下是在 Linux 上使用 Docker 运行应用程序(例如 hello-world)的基本示例:

复制代码
# Pull the Docker image from Docker Hub
sudo docker pull hello-world

# Run the Docker container
sudo docker run hello-world

通过上述命令,您可以下载 Docker 镜像并在 Linux 系统上运行,为在开发、测试和生产环境中部署容器提供了基础。

🚢 Docker 就像标准化集装箱运输

核心概念: 把应用程序及其运行环境打包成标准"集装箱"(容器),实现一次打包,到处运行


📦 传统运输 vs Docker集装箱

传统运输(物理机/虚拟机) Docker集装箱
每次运输都要装整个货船(完整操作系统) 共享货船引擎(主机内核),只装货物(应用+依赖)
启动耗时(分钟级) 秒级启动
资源占用大(GB级) 轻量化(MB级)
环境差异大(开发/测试/生产环境不一致) 环境一致性(集装箱密封运输)

🛠️ Docker 核心部件解析

组件 作用 生活比喻
镜像(Image) 集装箱设计图纸 包含货物清单和摆放规则
容器(Container) 实际运行的集装箱 根据图纸生成的实体货箱
仓库(Registry) 集装箱存储港口(如Docker Hub) 全球集装箱共享中心
Dockerfile 自动建造集装箱的说明书 装配线操作指南

🚀 快速体验Docker

复制代码
# 从港口拉取一个测试集装箱
docker pull hello-world

# 运行这个集装箱(拆箱展示)
docker run hello-world
# 输出:Hello from Docker! → 你的第一个集装箱成功运行!
复制代码

🌰 实际应用场景

场景1:部署Web应用
复制代码
# 拉取Nginx集装箱
docker pull nginx

# 运行并映射端口(把集装箱的80端口接到主机8080)
docker run -d -p 8080:80 --name my-web nginx
# 访问 http://localhost:8080 即可看到Nginx欢迎页
复制代码
场景2:开发环境搭建
复制代码
# Dockerfile 内容
FROM python:3.9-slim           # 基础镜像:带Python的轻量系统
WORKDIR /app                   # 设置工作目录
COPY requirements.txt .        # 复制依赖清单
RUN pip install -r requirements.txt  # 安装依赖
COPY . .                       # 复制代码
CMD ["python", "app.py"]       # 启动命令

构建命令:

复制代码
docker build -t my-python-app .  # 按图纸建造集装箱
docker run -it my-python-app     # 运行
复制代码

🔧 Docker 管理工具箱

命令 用途 示例
docker ps 查看运行中的集装箱 docker ps -a(显示所有)
docker logs 查看集装箱日志 docker logs -f my-web
docker exec 进入运行的集装箱 docker exec -it my-web bash
docker-compose 管理多集装箱舰队 编排数据库+后端+前端服务

⚠️ 集装箱安全须知

  1. 不要运输危险品:避免在容器中运行root权限进程

  2. 定期检查货物 :更新基础镜像(docker pull)

  3. 隔离重要物资:使用独立网络和数据卷(Volume)

    复制代码
    docker run -v /宿主机路径:/容器路径 ...  # 数据持久化存储
    复制代码

💡 高级技巧

  • 镜像瘦身:使用Alpine基础镜像

    复制代码
    FROM node:18-alpine  # 仅5MB的基础镜像
    复制代码
  • 多阶段建造:减少最终镜像体积

    复制代码
    # 第一阶段:使用完整环境编译
    FROM golang:1.20 AS builder
    RUN go build -o app .
    
    # 第二阶段:仅保留编译结果
    FROM alpine:latest  
    COPY --from=builder /app .
    复制代码
  • 集群运输:结合Kubernetes管理集装箱舰队


总结: Docker通过:

  • 📦 环境标准化 → 解决"在我机器上能跑"的问题

  • ⚡ 快速部署 → 秒级启动成百上千容器

  • 🔄 持续集成 → 开发→测试→生产环境无缝衔接

  • 🛡️ 资源隔离 → 利用Linux内核特性实现安全隔离

让软件运输像现代物流一样高效可靠! 🚚💨

相关推荐
huainingning27 分钟前
RJ SW Console口忘记密码处理方法
linux·运维·服务器
布裘38 分钟前
【银河麒麟】桌面系统循环登录,无法进入桌面?
linux·运维·服务器
传奇开心果编程40 分钟前
【SwiftUI提高练中学】第8课 网络层架构:拦截器、重试、缓存与离线优先
学习·macos·ui·ios·swiftui
慧都小项40 分钟前
程序到了Linux才出错?用CLion把调试接到目标环境
linux·运维·服务器
一条破秋裤41 分钟前
内核驱动的意义与课程学习规划
学习
Bug收容所1 小时前
学习LangChain day1
学习·langchain·llm·agent
传奇开心果编程1 小时前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack
guo_wen_qiang2 小时前
docker的备份与迁移
运维·docker·容器
Ruiery3 小时前
Linux 6.6内核内存管理深度解析(一):物理内存初始化 — memblock 怎么把内存交给 buddy
linux·运维·服务器
我命由我123453 小时前
Photoshop - Photoshop 矢量蒙版
学习·职场和发展·产品运营·求职招聘·职场发展·产品经理·photoshop