Containerd容器管理
9295 字
46 分钟
Containerd容器管理

Containerd容器管理
[TOC]
概述
什么是 Containerd❓️

- https://landscape.cncf.io/
- 网址可查看👆
🧱 Containerd 是 Docker 公司从 Docker Engine 中剥离出来的轻量级容器运行时,2017 年捐献给 CNCF(云原生基金会) 组织
- 符合 OCI(容器开放提议)开发规范
Containerd vs Docker
| 维度 | Docker | Containerd |
|---|---|---|
| 网络模式 | 5 种 (bridge/host/none/container/macvlan) 让容器拥有独立 MAC 地址,直连物理网络(macvlan) | 仅 host 和 none,其余需装 ==CNI== 插件 |
| 启动容器 | 一步到位 docker run | 先 create 容器,再 start 任务 |
| 镜像构建 | 内置 docker build | ❌ 不支持,需单独工具 (如 buildkit) |
| 生态完整度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 推荐场景 | 日常开发、单机部署 | K8s 底层运行时、轻量场景 |
✅️ 结论:
- 单独使用 Containerd 功能受限
- ctr 操作容器比较底层
- 它专注容器生命周期管理,==不包含镜像构建、网络等上层功能==
- 日常开发推荐直接用 Docker
- 内部集成 Containerd
- 提供了更友好的命令行和完整生态
📌 containerd 是”引擎”,Docker 是”整车”
整车已经包含了引擎,你买整车就够了,不需要单独买个引擎自己装方向盘
角色关系图
┌──────────────┐│ Kubernetes │ ← 容器编排平台(管理大量容器)├──────────────┤│ containerd │ ← 容器运行时(管理容器的生命周期)├──────────────┤│ runc │ ← 底层工具(真正创建和运行容器)├──────────────┤│ Linux 内核 │ ← 操作系统└──────────────┘| 组件 | 通俗理解 | 实际职责 |
|---|---|---|
containerd | 容器大管家 | 拉取镜像、管理镜像、创建/启停容器,==Kubernetes 默认运行时== |
runc | containerd 的”手下” | 真正跟 Linux 内核打交道,创建出一个个容器进程 |
ctr | 遥控器 | containerd 自带的命令行客户端,敲命令跟 containerd 交互 |
📌 Docker 走的也是同一条路,只是多套了一层
dockerd:
docker CLI→dockerd(守护进程) →containerd→runc→ 容器进程
关联与区别
刚才画了两条链,放一起对比:
Kubernetes 路线: kubelet ──→ containerd ──→ runc ──→ 容器
Docker 路线: docker CLI ──→ dockerd ──→ containerd ──→ runc ──→ 容器| 对比维度 | Kubernetes 路线 | Docker 路线 |
|---|---|---|
| 调用方 | kubelet(K8s 节点代理) | docker CLI(你敲的命令) |
| 中间层 | ❌ 无 | ✅ dockerd(Docker 自己的守护进程) |
| containerd 角色 | 直接被 K8s 通过 CRI 协议调用 | 作为 Docker 内部的子组件运行 |
- 关联:containerd 最初就是 Docker 公司拆分出来的
- 本质是同一个软件
- 区别:Kubernetes 直接把 Docker 那层
dockerd给绕过去了
- 少一层中转,更轻量
📌 一句话:Docker 比 K8s 多套了一层皮,但肚子里的 containerd 是一样的
环境部署
containerd

- GitHub: Releases · containerd/containerd
- 选稳定版本

runc


脚本安装
#!/bin/bash#================================# 作者: 久棹# 用途: 在 Linux 上自动安装/卸载 containerd 容器运行时# 适用系统: CentOS 7/8/9, Ubuntu 18.04+, Debian 10+ 等 systemd 系统#================================
#================================# 第一部分: 定义变量# 把常用的路径、版本号、下载地址定义为变量,方便后续修改和维护#================================
# --- containerd 相关配置 ---# containerd 的版本号(要与 download 目录下的 tar.gz 文件名匹配)CONTAINERD_VERSION=2.3.0# containerd 压缩包的文件名(拼接出来的)CONTAINERD_FILENAME=containerd-${CONTAINERD_VERSION}-linux-amd64.tar.gz# containerd 在 GitHub 上的下载地址CONTAINERD_URL=https://github.com/containerd/containerd/releases/download/v${CONTAINERD_VERSION}/${CONTAINERD_FILENAME}# containerd 二进制文件的安装目录(放到这里后,系统可以直接找到这些命令)CONTAINERD_BASEDIR=/usr/local/bin/# containerd 的 systemd 服务文件存放路径(systemd 会从这里读取服务配置)CONTAINERD_SCRIPTS=/usr/lib/systemd/system/# containerd 的配置文件目录CONTAINERD_CONFIG=/etc/containerd
# --- runc 相关配置 ---# runc 的版本号(runc 是 containerd 的底层容器运行时)RUNC_VERSION=1.4.2# runc 二进制文件的文件名RUNC_FILENAME=runc.amd64# runc 在 GitHub 上的下载地址RUN_URL=https://github.com/opencontainers/runc/releases/download/v${RUNC_VERSION}/${RUNC_FILENAME}# runc 的安装目录(同为二进制)RUNC_BASEDIR=/usr/sbin
# --- 本地目录 ---# 预下载文件的存放目录DOWNLOAD=./download
#================================# 第二部分: 工具函数#================================
#---------------------------------------------------------------------------# 函数名: prepare# 作用: 下载指定的文件(如果本地已经有了就跳过下载,节省时间)# 参数:# $1 - 要下载的文件名# $2 - 文件的下载地址(URL)# 使用示例: prepare "containerd-2.3.0-linux-amd64.tar.gz" "https://..."#---------------------------------------------------------------------------function prepare(){ # 如果 download 目录下还没有这个文件,就用 wget 从网上下载 if [ ! -f ${DOWNLOAD}/$1 ]; then echo "正在下载 $1 ..." # wget 参数说明: # -T 10 : 超时时间设为 10 秒(网络不好时可以适当调大) # -t 3 : 下载失败后最多重试 3 次 # -O(大写) : 指定下载后保存的文件路径(并重命名) # 类比curl -o(小写) wget -T 10 -t 3 "$2" -O ${DOWNLOAD}/$1
# 检查 wget 的退出状态码 # 0 表示成功,非 0 表示失败 if [ $? -ne 0 ]; then # 下载失败时 --> 删残留文件 --> 报错退出 rm -f ${DOWNLOAD}/$1 # 用红色字体提示错误信息 tput setaf 1 echo "错误: 无法下载 $1 ,请检查网络连接!" echo "下载地址: $2" tput sgr0 # 重置颜色 # exit 100(自定义错误码) # 表示因下载失败而退出(非零退出码表示异常退出) exit 100 fi echo "下载完成: $1" else echo "文件已存在,跳过下载: $1" fi}
#================================# 第三部分: 安装函数#================================
function InstallContainerd(){ # --- 第 1 步: 检查是否以 root 用户运行 --- # 安装系统服务、写入 /usr 目录等操作都需要 root 权限 if [ "$(id -u)" -ne 0 ]; then tput setaf 1 echo "错误: 请使用 root 用户或 sudo 运行此脚本!" echo "用法: sudo bash $0 install" tput sgr0 exit 1 fi
echo "=========================================" echo " 开始安装 containerd v${CONTAINERD_VERSION}" echo "========================================="
# --- 第 2 步: 下载 containerd 压缩包 --- prepare ${CONTAINERD_FILENAME} ${CONTAINERD_URL}
# --- 第 3 步: 解压 containerd 到安装目录 --- echo "正在解压 containerd ..."
tar xf ${DOWNLOAD}/${CONTAINERD_FILENAME} \ -C ${CONTAINERD_BASEDIR} \ bin/containerd \ bin/ctr \ bin/containerd-shim-runc-v2 \ --strip-components=1 # --strip-components=1 : 去掉压缩包内第一层目录 # 例如 bin/containerd → containerd # 为什么不全部解压? # 跳过 containerd-stress 压力测试工具,生产环境用不上 echo "containerd 二进制文件已安装到: ${CONTAINERD_BASEDIR}"
# --- 第 4 步: 生成 systemd 服务文件 --- # 直接写入,不依赖外部文件,脚本自包含 cat > ${CONTAINERD_SCRIPTS}/containerd.service <<EOF[Unit]Description=containerd container runtimeDocumentation=https://containerd.io# 等网络就绪后再启动After=network.target local-fs.target
[Service]# 准备必要的系统环境ExecStartPre=-/sbin/modprobe overlay# 启动时执行的命令ExecStart=/usr/local/bin/containerd
Type=notifyDelegate=yesKillMode=processRestart=alwaysRestartSec=5# 用 cgroups 做容器级资源统计,这里放开内核限制LimitNPROC=infinityLimitCORE=infinityLimitNOFILE=infinity# systemd 226+ 才支持,低版本需注释掉TasksMax=infinityOOMScoreAdjust=-999
[Install]WantedBy=multi-user.targetEOF echo "systemd 服务文件已生成: ${CONTAINERD_SCRIPTS}/containerd.service"
# --- 第 5 步: 创建配置目录并生成默认配置 --- # mkdir -p 的作用: 如果目录不存在就创建,如果已存在也不报错 mkdir -p ${CONTAINERD_CONFIG}
# 生成 containerd 的默认配置文件 # 使用完整路径,即使containerd服务没有运行,也是可以使用的 ${CONTAINERD_BASEDIR}containerd config default > /etc/containerd/config.toml echo "已生成默认配置文件: /etc/containerd/config.toml"
# --- 第 6 步: 修改配置文件 --- # 6.1 开启 SystemdCgroup(让容器使用 systemd 管理 cgroup,推荐配置) # sed -ri 正确 # -ir 错误 # -i.bak 修改之前先备份 sed -ri 's#(SystemdCgroup = )false#\1true#' /etc/containerd/config.toml echo "已开启 SystemdCgroup" # 为什么改这个? # SystemdCgroup 让容器使用 systemd 来管理 cgroup(控制组),这样可以避免 cgroup 管理混乱,是 Kubernetes 官方推荐配置
# 6.2 替换容器镜像仓库为国内镜像源(解决国内访问 k8s.gcr.io 慢或不通的问题) # 这样在国内拉取 Kubernetes 相关镜像会快很多 # "遇到 registry.k8s.io,别去连国外的,去阿里云杭州拉" sed -ri 's#registry\.k8s\.io#registry.cn-hangzhou.aliyuncs.com/google_containers#g' /etc/containerd/config.toml # 相当于一个自动翻译器 echo "已将镜像仓库替换为阿里云国内镜像源"
# --- 第 7 步: 启动 containerd 并设置开机自启 --- systemctl daemon-reload systemctl enable --now containerd echo "containerd 服务已启动并设置为开机自启"
# --- 第 8 步: 安装 runc --- # runc 是实际创建和运行容器的底层工具,containerd 依赖它 prepare ${RUNC_FILENAME} ${RUN_URL}
# 复制 runc(单个文件) 到系统路径 cp ${DOWNLOAD}/${RUNC_FILENAME} ${RUNC_BASEDIR}/runc # 给 runc 添加可执行权限 # wget 下载的单个文件默认不带执行权限(通常是644),而 containerd 从 tar.gz(bin目录下) 解压会保留原始权限 chmod +x ${RUNC_BASEDIR}/runc echo "runc 已安装到: ${RUNC_BASEDIR}/runc"
# --- 第 9 步: 验证安装结果 --- echo "" echo "=========================================" tput setaf 2 # 设置字体为绿色 echo " 安装完成!版本信息如下:" tput sgr0 # 重置字体颜色 echo "========================================="
# ctr 是 containerd 自带的命令行客户端,version 命令显示版本信息 ctr version echo ""
# runc -v 显示 runc 的版本号 runc -v echo ""
tput setaf 3 # 设置字体为黄色 echo "安装成功!欢迎使用久棹 containerd 二进制安装脚本~" tput sgr0}
#===============================================================================# 第四部分: 卸载函数#===============================================================================
function UninstallContainerd(){ # 检查 root 权限 # root 用户的 UID 永远是 0 if [ "$(id -u)" -ne 0 ]; then tput setaf 1 echo "错误: 请使用 root 用户或 sudo 运行此脚本!" echo "用法: sudo bash $0 remove" tput sgr0 exit 1 fi
echo "=========================================" echo " 开始卸载 containerd" echo "========================================="
# --- 第 1 步: 停止服务并禁用开机自启 --- # systemctl disable --now: 同时完成 disable(取消开机自启)和 stop(停止服务) systemctl disable --now containerd.service echo "已停止 containerd 服务"
# --- 第 2 步: 删除所有安装的文件 --- # 用反斜杠 \ 换行可以让长命令更易读 # 同时卸载了runc rm -f \ ${RUNC_BASEDIR}/runc \ /etc/containerd/config.toml \ ${CONTAINERD_BASEDIR}/containerd \ ${CONTAINERD_BASEDIR}/ctr \ ${CONTAINERD_BASEDIR}/containerd-shim-runc-v2 \ ${CONTAINERD_SCRIPTS}/containerd.service
# 清理空的配置目录(我们手动mkdir的)(rmdir 只删除空目录,不会误删有其他文件的目录) # 如果这个目录下有其他文件或者目录,则报错 # rmdir: failed to remove '目录名': Directory not empty rmdir /etc/containerd >/dev/null # 只有正确的1>定向到空 --> 如果有错误则输出到屏幕上!
echo "已删除所有 containerd 相关文件"
# --- 第 3 步: 重新加载 systemd 配置 --- systemctl daemon-reload # 让 systemd 知道服务已经被删除了
tput setaf 5 # 设置字体为紫色 echo "卸载成功!欢迎再次使用久棹 containerd 二进制安装脚本~" tput sgr0}
#===============================================================================# 第五部分: 主函数 —— 脚本的入口#===============================================================================
function main(){ # $1 是运行脚本时传入的第一个参数 # case 语句相当于其他语言中的 switch,根据不同的参数执行不同的操作 case $1 in # 如果参数是 install 或 i,执行安装 install|i) InstallContainerd ;; # 如果参数是 remove 或 r,执行卸载 remove|r) UninstallContainerd ;; # 如果参数不是以上任何一种,打印使用说明 *) echo "用法: $0 {install|i|remove|r}" echo "" echo " 示例:" echo " 安装: sudo bash $0 install" echo " 卸载: sudo bash $0 remove" echo " 简写: sudo bash $0 i (安装)" echo " 简写: sudo bash $0 r (卸载)" ;; esac}
# 调用 main 函数,把命令行的第一个参数传进去# $1 就是运行脚本时跟在脚本名后面的第一个词,比如 install 或 removemain $11)先卸载 Docker 环境'打个快照'[root@Docker ~]# systemctl stop docker.socket[root@Docker ~]# systemctl stop docker# 先停止socket,在停止服务[root@Docker ~]# yum remove -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin# 删除软件包[root@Docker ~]# rm -rf /var/lib/docker# 主要数据存储目录(仅有镜像的元数据)[root@Docker ~]# rm -rf /var/lib/containerd# 镜像层数据
2)安装包上传&分析[root@Docker ~]# tar tf /tmp/containerd-2.3.0-linux-amd64.tar.gzbin/bin/containerd ✅️ 服务端(守护进程)bin/containerd-stress ❌ 压力测试工具,生产环境用不上bin/ctr ✅️ 客户端(命令行工具)bin/containerd-shim-runc-v2 ✅️ 垫片,让 containerd 能调用 runc'从tar包中解压保留执行权限'[root@Docker ~]# ls -lh /tmp/runc.amd64-rw-r--r-- 1 root root 12M May 12 08:13 /tmp/runc.amd64'我们下载的单文件,默认644,需要+x赋权'
3)运行脚本[root@Docker ~]# mkdir ./download[root@Docker ~]# mv /tmp/{containerd-2.3.0-linux-amd64.tar.gz,runc.amd64} ./download/[root@Docker ~]# vim install-containerd.sh[root@Docker ~]# chmod +x install-containerd.sh'加执行权限,否则跑不了'autoinstall-containerd-v2.3.0/├── install-containerd.sh ← 主脚本├── download/ ← 预下载文件(离线安装用)│ ├── containerd-2.3.0-linux-amd64.tar.gz│ ├── runc.amd64# 项目文件结构"可以提前把安装包下载好"# 如果网络稳定,脚本中也可以自行下载[root@Docker ~]# ./install-containerd.sh install# 简写: bash install-containerd.sh i========================================= 开始安装 containerd v2.3.0=========================================文件已存在,跳过下载: containerd-2.3.0-linux-amd64.tar.gz正在解压 containerd ...containerd 二进制文件已安装到: /usr/local/bin/systemd 服务文件已生成: /usr/lib/systemd/system//containerd.service已生成默认配置文件: /etc/containerd/config.toml已开启 SystemdCgroup已将镜像仓库替换为阿里云国内镜像源Created symlink '/etc/systemd/system/multi-user.target.wants/containerd.service' → '/usr/lib/systemd/system/containerd.service'.containerd 服务已启动并设置为开机自启文件已存在,跳过下载: runc.amd64runc 已安装到: /usr/sbin/runc
========================================= 安装完成!版本信息如下:=========================================Client: Version: v2.3.0 Revision: 2976f38ccbfcda5ef1364d63d60b0a304e4bf94a Go version: go1.26.2
Server: Version: v2.3.0 Revision: 2976f38ccbfcda5ef1364d63d60b0a304e4bf94a UUID: e45cb117-da93-4bb0-b32d-f716be31467e'两个都能输出版本号就没问题'# 两个都正常输出版本号 → 安装成功 ✅️runc version 1.4.2commit: v1.4.2-0-gc241c0bbspec: 1.3.0go: go1.25.8libseccomp: 2.6.0安装成功!欢迎使用久棹 containerd 二进制安装脚本~
4)验证[root@Docker ~]# systemctl is-active containerdactive'看到 active就对了'
5)卸载[root@Docker ~]# ./install-containerd.sh remove# 简写: install-containerd.sh r========================================= 开始卸载 containerd=========================================Removed '/etc/systemd/system/multi-user.target.wants/containerd.service'.已停止 containerd 服务已删除所有 containerd 相关文件卸载成功!欢迎再次使用久棹 containerd 二进制安装脚本~[root@Docker ~]# systemctl is-active containerdinactive[root@Docker ~]# systemctl status containerdUnit containerd.service could not be found.常见问题排查
- 下载失败
# 先尝试能否访问 GitHub[root@Docker ~]# curl -I https://github.com
# 如果访问不了,直接把 download/ 目录的文件提前拷贝进去# 脚本检测到本地已有文件就会跳过下载[root@Docker ~]# ls -lh ./download/total 45M-rw-r--r-- 1 root root 33M May 11 11:37 containerd-2.3.0-linux-amd64.tar.gz-rw-r--r-- 1 root root 12M May 11 14:56 runc.amd64- containerd 服务启动失败
[root@Docker ~]# journalctl -u containerd -n 50 --no-pagerMay 12 08:32:43 Docker systemd[1]: containerd.service: Deactivated successfully. ✅️ 成功安装'看日志定位问题'
# 常见原因: 1.配置文件语法错误 → 检查 /etc/containerd/config.toml 2.端口被占用 → netstat -tlnp | grep containerd- ctr version 正常但 runc -v 报错
[root@Docker ~]# ls -l /usr/sbin/runc'检查文件在不在、有没有 x 权限'-rwxr-xr-x 1 root root 12233104 May 12 08:37 /usr/sbin/runc
[root@Docker ~]# file /usr/sbin/runc# 应输出 x86-64 字样,确认架构匹配Namespace
⚠️ Containerd 的设计哲学和 Docker 有本质差异:
| Docker | Containerd |
|---|---|
docker run 一步启动 | 先创建容器 → 再创建任务 → 再启动 |
默认 default 命名空间 | 命名空间隔离镜像/容器/任务 |
docker image 管理镜像 | ctr image 管理镜像 |
docker ps 查看容器 | ctr container ls + ctr task ls |
💡 Linux 的 namespace 隔离系统资源(net/pid/mnt/uts),而Containerd 的 namespace 隔离容器、镜像、任务等元数据
1)查看所有命名空间[root@Docker ~]# ctr ns ls# namespace 简写为 nsNAME LABELS# 刚装完是空的,没有默认命名空间
2)创建命名空间[root@Docker ~]# ctr ns create kpyun[root@Docker ~]# ctr ns lsNAME LABELSkpyun
3)为命名空间打标签<namespace> key=value 1.可以打多个标签(空格隔开)(或分多次打) 2.默认 value 为 true[root@Docker ~]# ctr ns label kpyun class=C413[root@Docker ~]# ctr ns lsNAME LABELSkpyun class=C413[root@Docker ~]# ctr ns label kpyun env=Jiu secure# 等价于 secure=true, 空格隔开[root@Docker ~]# ctr ns lsNAME LABELSkpyun class=C413,env=Jiu,secure=true
4)移除标签(值为空即删除)[root@Docker ~]# ctr ns label kpyun secure="" env=""[root@Docker ~]# ctr ns lsNAME LABELSkpyun class=C413
5)删除命名空间⚠️ 命名空间必须为空才能删除(没有镜像、容器残留)[root@Docker ~]# ctr ns rm kpyunkpyun[root@Docker ~]# ctr ns lsNAME LABELS镜像管理
镜像仓库
脚本中👆替换为阿里镜像 --> 相当于一个自动翻译器"registry.k8s.io 就是 Kubernetes 官方镜像仓库"# 用于拉k8s的相关组件(所有 k8s 核心组件都在上面)
1)第三方应用仓库# 我还是选择 "轩辕镜像"[root@Docker ~]# ctr images pull -u user:passwd docker.xuanyuan.run/nginx:latest✅️ ctr忽略了配置的认证信息 --> 必须-u指定用户和密码Completed pull from OCI Registry (docker.xuanyuan.run/nginx:latest)# 默认拉取到 default 命名空间[root@Docker ~]# ctr ns lsNAME LABELSdefault
2)查看镜像[root@Docker ~]# ctr image ls'可以简写为i' --> ctr i ls# 同理,默认查看的是default下的镜像docker.xuanyuan.run/nginx:latest sha256:18819xxx 60.0 MiB
3)拉取镜像到指定命名空间[root@Docker ~]# ctr -n xix images pull -u user:passwd docker.xuanyuan.run/redis:latest-n # 指定命名空间,如果命名空间不存在会自动创建'-n选项要紧跟在ctr后面'[root@Docker ~]# ctr images ls# 并没有redis镜像# 查看的是默认default命名空间下的镜像# 等同于 ctr -n default images ls[root@Docker ~]# ctr -n xix images ls# 需要指定命名空间 --> 才能查看到镜像docker.xuanyuan.run/redis:latest sha256:0c34xxx 50.8 MiB📌 不同命名空间 --> 隔离镜像[root@Docker ~]# ctr images pull -u user:passwd docker.xuanyuan.run/wordpress:6.8.0-php8.3Completed pull from OCI Registry ✅️[root@Docker ~]# ctr ns lsNAME LABELSdefaultxix
4)给镜像打标签# -n default[root@Docker ~]# ctr image tag docker.xuanyuan.run/nginx:latest nginx:latestnginx:latest[root@Docker ~]# ctr image lsnginx:latest基本操作
"镜像导出/导入"⚠️ images, image, i ⚠️ 都是一个意思
1)导出镜像[root@Docker ~]# ctr -n xix image export redis-latest.tar.gz docker.xuanyuan.run/redis:latest'先写tar包,后跟镜像'[root@Docker ~]# ls -lh redis-latest.tar.gz-rw-r--r-- 1 root root 51M May 12 16:58 redis-latest.tar.gz✅️ 导出为 tar.gz,命名规范:名称-版本.tar.gz
2)删除镜像'只有对应命名空间中的镜像都删除完了,才能够删除名称空间'[root@Docker ~]# ctr ns rm xixERRO[0000] unable to delete xix"xix" must be empty, but it still has images[root@Docker ~]# ctr -n xix image rm docker.xuanyuan.run/redis:latest[root@Docker ~]# ctr -n xix images lsREF TYPE DIGEST SIZE PLATFORMS LABELS[root@Docker ~]# ctr ns rm xixxix
3)导入镜像[root@Docker ~]# ctr -n haha image import redis-latest.tar.gzdocker.xuanyuan.run/redis:latest savedImporting elapsed: 1.7 s[root@Docker ~]# ctr ns lsNAME LABELSdefaulthaha[root@Docker ~]# ctr -n haha images lsdocker.xuanyuan.run/redis:latest
4)挂载"挂载镜像到本地"💡 用于查看镜像内部文件(调试/学习用)[root@Docker ~]# ls /tmpcrictl-v1.36.0-linux-amd64.tar.gz# 现在里面是一个压缩包[root@Docker ~]# ctr -n haha image mount docker.xuanyuan.run/redis:latest /tmp/sha256:ab09axxx[root@Docker ~]# ls /tmpbin boot data dev etc home ...# 镜像的文件系统直接被"展开"到 /tmp 目录[root@Docker ~]# df -h | grep "tmp$"overlay 47G 7.0G 40G 15% /tmp# 底层是通过 overlay 文件系统挂载的
2)卸载[root@Docker ~]# ctr -n haha image unmount /tmp/tmp[root@Docker ~]# df -h | grep "tmp$"容器管理
⚠️ Containerd 的容器和任务是分开的两个概念:
- 容器 (Container):静态配置(镜像、环境变量、路径挂载)
- 任务 (Task):容器的运行实例(进程)
-d(后台运行) 是在任务阶段才可以指定
容器基本操作
'我们用alpine镜像'[root@Docker ~]# ctr -n kpyun images pull -u user:passwd docker.xuanyuan.run/alpine:3.22[root@Docker ~]# ctr -n kpyun images lsdocker.xuanyuan.run/alpine:3.22
1)创建容器(仅创建,不启动)[root@Docker ~]# ctr -n kpyun container create --env SCHOOL=kpyun docker.xuanyuan.run/alpine:3.22 c1# c1 是容器名称'创建容器的同时必须指定容器名称'
2)查看容器[root@Docker ~]# ctr -n kpyun c lsc1 docker.xuanyuan.run/alpine:3.22 io.containerd.runc.v2
3)删除容器[root@Docker ~]# ctr -n kpyun c rm c1# c 是 containers 的缩写[root@Docker ~]# ctr -n kpyun c lsCONTAINER IMAGE RUNTIME任务管理(核心)
1)创建容器后启动任务(tasks)[root@Docker ~]# ctr -n kpyun container create --env SCHOOL=kpyun docker.xuanyuan.run/alpine:3.22 c1[root@Docker ~]# ctr tasks start -d c1ctr: container "c1" in namespace "default": not found'需要指定命名空间'[root@Docker ~]# ctr -n kpyun task start -d c1✅️ 此时容器已经启动了-d: # 后台运行(类似 docker run -d)tasks==task==t(简写)
2)查看任务[root@Docker ~]# ctr task lsTASK PID STATUS'同上 -n'[root@Docker ~]# ctr -n kpyun task lsTASK PID STATUSc1 1723 "RUNNING"
3)进入容器ctr -n kpyun task exec -it ❌️'没有 -i 这个选项'[root@Docker ~]# ctr -n kpyun task exec -t --exec-id $RANDOM c1 sh-t # 给个伪终端$RANDOM # 生成随机 ID,避免 exec-id 冲突'--exec-id 每次必须唯一!'/ # ip a1: lo: xxx... link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo✅️ none --> '默认只有本地回环地址' 没有自己的网卡/ # ps -efPID USER TIME COMMAND 1 root 0:00 /bin/sh ✅️ task start 时的主进程 7 root 0:00 sh ✅️ exec 再次以sh伪终端的形式进来了 14 root 0:00 ps -ef'不难发现 --> 两个终端'/bin/sh # 主进程sh # 刚exec进来的伪终端
4)对应宿主机的进程[root@Docker ~]# ctr -n kpyun task ps c1PID INFO1723 -# 1723就是容器的主进程[root@Docker ~]# ctr -n kpyun task lsTASK PID STATUSc1 1723 RUNNING ✅️[root@Docker ~]# kill -9 1723'强制杀死' --> 停止任务📌 等价ctr -n kpyun task kill c1[root@Docker ~]# ctr -n kpyun task lsTASK PID STATUSc1 1723 STOPPED ❌️[root@Docker ~]# ctr -n kpyun task start c1ctr: task c1: already exists'停止后,没有办法再次启动'# 当任务被杀死后,需要删除该任务,才能创建新的任务实例
5)删除已停止的"任务"[root@Docker ~]# ctr -n kpyun t lsTASK PID STATUSc1 1723 STOPPED ❌️'c1容器名'[root@Docker ~]# ctr -n kpyun t rm c1⚠️ 删除 task 不会删除容器,容器还在![root@Docker ~]# ctr -n kpyun task lsTASK PID STATUS[root@Docker ~]# ctr -n kpyun container lsc1 docker.xuanyuan.run/alpine:3.22 io.containerd.runc.v2[root@Docker ~]# ctr -n kpyun t start -d c1❗ 创建的容器名是c1, 启动的任务也得是c1'先create,再start'[root@Docker ~]# ctr -n kpyun t lsTASK PID STATUSc1 2049 RUNNING# 任务再次被创建出来暂停与恢复
1)暂停[root@Docker ~]# ctr -n kpyun t lsTASK PID STATUSc1 2049 RUNNING[root@Docker ~]# ctr -n kpyun t pause c1[root@Docker ~]# ctr -n kpyun t lsTASK PID STATUSc1 2049 PAUSED# 暂停后无法 exec 进入[root@Docker ~]# ctr -n kpyun t exec -t --exec-id $RANDOM c1 shctr: cannot exec in a paused state
2)恢复[root@Docker ~]# ctr -n kpyun t resume c1[root@Docker ~]# ctr -n kpyun t lsTASK PID STATUSc1 2049 "RUNNING"[root@Docker ~]# ctr -n kpyun t exec -t --exec-id $RANDOM c1 sh/ # envSCHOOL=kpyunHost 网络模式
'默认网络模式为none'# 我们这次用nginx的镜像[root@Docker ~]# ctr i ls📌 默认分区defaultdocker.xuanyuan.run/nginx:latest
1)创建容器(同时指定网络)[root@Docker ~]# ctr c create --net-host docker.xuanyuan.run/nginx:latest mynginx[root@Docker ~]# ctr c lsmynginx ocker.xuanyuan.run/nginx:latest io.containerd.runc.v2# 容器被创建出来了[root@Docker ~]# ctr t start -d task_namectr: container "task_name" in namespace "default": not found✅️ '任务名和刚创建的容器名保持一致🤝'[root@Docker ~]# ctr -n default container lsCONTAINERmynginx[root@Docker ~]# ctr t start -d mynginx[root@Docker ~]# ctr t lsTASK PID STATUSmynginx 2286 "RUNNING"✅️ 任务正在运行[root@Docker ~]# ss -lnt | grep 80LISTEN 0 511 0.0.0.0:80 0.0.0.0:*LISTEN 0 511 [::]:80 [::]:*[root@Docker ~]# curl localhost<title>Welcome to nginx!</title># 从另一台机器直接访问宿主机 IP --> 也能访问到这个容器[root@test ~]# hostname -I10.0.0.128[root@test ~]# curl 10.0.0.99<title>Welcome to nginx!</title>
2)进入到容器中验证[root@Docker ~]# ctr task exec -t --exec-id $RANDOM mynginx /bin/bash 1./etc/hosts表一致 2.网络一致root@Docker:/# hostname -I10.0.0.99 192.168.88.99root@Docker:/# find /sys/class/net -name "*"'查看网络接口信息'/sys/class/net/sys/class/net/eth1/sys/class/net/eth0/sys/class/net/lo
3)换源升级软件包root@Docker:/# ifconfigbash: ifconfig: command not foundroot@Docker:/# ip abash: ip: command not found'很多命令都没有'root@Docker:/# cat /etc/os-releasePRETTY_NAME="Debian GNU/Linux 13 (trixie)"NAME="Debian GNU/Linux"①先换源cat > /etc/apt/sources.list << EOFdeb http://mirrors.ustc.edu.cn/debian/ trixie main contrib non-freedeb http://mirrors.ustc.edu.cn/debian/ trixie-updates main contrib non-freedeb http://mirrors.ustc.edu.cn/debian-security/ trixie-security main contrib non-freeEOF②再升级软件包root@Docker:/# apt-get update && apt-get install -y net-toolsroot@Docker:/# ifconfig'和宿主机的网络保持一致'eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.0.0.99 netmask 255.255.255.0 broadcast 10.0.0.255
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.88.99 netmask 255.255.255.0 broadcast 192.168.88.255Containerd 对接 Harbor
推送镜像到 Harbor
1)环境准备# 在另一台部署Harbor[root@test /usr/local/harbor]# docker compose up -d[+] Running 10/10# http://10.0.0.128/[root@test /usr/local/harbor]# docker tag starryfrp/frpc:22.5.13 10.0.0.128/kpyun/starryfrp/frpc:22.5.13'得能够正常推送镜像' --> 这里用的是http协议[root@test /usr/local/harbor]# docker push 10.0.0.128/kpyun/starryfrp/frpc:22.5.13The push refers to repository [10.0.0.128/kpyun/starryfrp/frpc]352cd3606163: Pushed4fc242d58285: Pushed
2)给镜像打标签[root@Docker ~]# ctr i lsnginx:latest[root@Docker ~]# ctr i tag nginx:latest 10.0.0.128/kpyun/nginx:latest# 标签格式: harbor域名(ip)/项目名/镜像名:版本
3)推送镜像[root@Docker ~]# ctr i push -u admin:passwd 10.0.0.128/kpyun/nginx:latest-u # 用户名:密码⚠️ 如果是 HTTP 协议(非 HTTPS),需要加 --plain-http10.0.0.128:443: connect: connection refused'我这理解就报错了,因为我是HTTP'
4)再次推送[root@Docker ~]# ctr i push -u admin:passwd --plain-http 10.0.0.128/kpyun/nginx:latestctr: content digest sha256:8428xxx: not found ❌️# 还是报错!'去网上简单的搜索了一下' --> 我拉取的镜像是多架构镜像索引[root@Docker ~]# ctr i inspect 10.0.0.128/kpyun/nginx:latest'详情很乱,很多层!'[root@Docker ~]# ctr i lslinux/386,linux/amd64,linux/arm/v5, ...xxx, linux/arm64/v8'拉下来的镜像能用,就是自身包含很多架构,没有办法正常推送push'
4)从 Harbor 拉取[root@Docker ~]# ctr i pull -u admin:passwd --plain-http 10.0.0.128/kpyun/starryfrp/frpc:22.5.13Completed pull from OCI Registry (10.0.0.128/kpyun/starryfrp/frpc:22.5.13)[root@Docker ~]# ctr i ls10.0.0.128/kpyun/starryfrp/frpc:22.5.13 linux/amd64 '从Harbor镜像仓库拉取的只有这一个架构' 👆 ✅️ 从Harbor拉取的可以正常推送push[root@Docker ~]# ctr i inspect 10.0.0.128/kpyun/starryfrp/frpc:22.5.13'详情也非常干净'10.0.0.128/kpyun/starryfrp/frpc:22.5.13│ Created: 2026-05-13 00:22:06.254806763 +0000 UTC│ Updated: 2026-05-13 00:22:06.254806763 +0000 UTC└── application/vnd.docker.distribution.manifest.v2+json @sha256:73b01cxxx'我们试着推送一下这个镜像'⚠️ 重新打标签 --> 不同项目 /base, 镜像名也不同 ✅️[root@Docker ~]# ctr i tag 10.0.0.128/kpyun/starryfrp/frpc:22.5.13 10.0.0.128/base/frpc:v1.0
5)再次推送[root@Docker ~]# ctr i push -u admin:passwd --plain-http 10.0.0.128/base/frpc:v1.010.0.0.128/base/frpc:v1.0 pushed content└──manifest (73b01c4c8c9a) complete |++++++++++++++++++++++++++++++++++++++| ├──layer (fe73370dba55) already exists ├──layer (fe73370dba55) already exists ├──config (a8ee487ca46b) already exists └──layer (df9b9388f04a) already existsapplication/vnd.docker.distribution.manifest.v2+json sha256:73bxxxCompleted push to OCI Registry (10.0.0.128/base/frpc:v1.0) elapsed: 0.2 s total: 11.0 M (70.3 MiB/s)

数据持久化
1)环境准备'本质就是把宿主机路径挂载在容器中'📌 路径挂载 --> 会覆盖容器中的数据# 名称空间我们就用默认的default --> 如果不是还得-n 指定# 镜像用nginx# 网络类型 --net-host[root@Docker ~]# ctr i pull -u admin:passwd --plain-http 10.0.0.128/base/nginx:latest[root@Docker ~]# ctr i tag 10.0.0.128/base/nginx:latest nginx:latest
2)创建容器时 --> 指定路径挂载'只有在task任务中可以 -d 指定后台运行'[root@Docker ~]# ls /codels: cannot access '/code': No such file or directory# 宿主机中现在没有这个目录[root@Docker ~]# ctr c create --net-host --mount \type=bind,src=/code,dst=/usr/share/nginx/html,options=rbind:rw nginx:latest myweb# type=bind: 绑定挂载(类似 docker -v)# src: 宿主机目录# dst: 容器内目录# options=rbind:rw: 递归绑定 + 读写权限✅️ rbind会递归挂载目录及其包含的所有子目录 --> "嵌套挂载"[root@Docker ~]# ctr c lsCONTAINER IMAGE RUNTIMEmyweb nginx:latest io.containerd.runc.v2
3)创建任务[root@Docker ~]# ctr t start -d mywebfailed to fulfil mount request: open /code: no such file or directory✅️ 必须手动创建宿主机目录/code[root@Docker ~]# mkdir /code[root@Docker ~]# ls /code/'里面什么都没有' --> 容器中/usr/share/nginx/html原有内容被覆盖# 本质是路径挂载[root@Docker ~]# ctr t start -d myweb[root@Docker ~]# ctr t lsTASK PID STATUSmyweb 2630 "RUNNING"[root@Docker ~]# curl localhost<title>403 Forbidden</title>'主页缺失'[root@Docker ~]# echo Hello World > /code/index.html[root@Docker ~]# curl localhostHello World
4)进入容器验证[root@Docker ~]# ctr t exec -t --exec-id $RANDOM myweb /bin/bashroot@Docker:/# hostname -I10.0.0.99 192.168.88.99root@Docker:/# curl localhostHello Worldroot@Docker:/# curl 10.0.0.99Hello Worldroot@Docker:/# echo kpyun > /usr/share/nginx/html/index.htmlroot@Docker:/# curl localhostkpyunroot@Docker:/# exitexit[root@Docker ~]# cat /code/index.htmlkpyun✅️ 路径挂载 --> 数据同步# 容器内的写入立即反映到宿主机上
5)删除容器后验证数据[root@Docker ~]# ctr t kill myweb[root@Docker ~]# ctr c rm myweb[root@Docker ~]# ctr c lsCONTAINER IMAGE RUNTIME[root@Docker ~]# cat /code/index.htmlkpyun✅️ 容器删了,数据还在——持久化成功!
6)再次运行容器[root@Docker ~]# curl localhostcurl: (7) Failed to connect to localhost port 80[root@Docker ~]# ctr c create --net-host --mount \type=bind,src=/code,dst=/usr/share/nginx/html,options=rbind:rw nginx:latest haha# 换了个容器名称[root@Docker ~]# ctr t start -d haha[root@Docker ~]# curl localhostkpyunDocker 集成 Containerd
为什么需要集成❓️
💡 Containerd 作为容器运行时很优秀,但周边生态不完善(镜像构建、丰富的网络模式、Compose 编排等),用 Docker 调用 Containerd 作为底层运行时,可以取长补短
配置方法
1)确保 Containerd 服务已启动[root@Docker ~]# systemctl is-active containerd.serviceActive: active (running)[root@Docker ~]# ll /run/containerd/containerd.socksrw-rw---- 1 root root 0 Nov 14 09:17 /run/containerd/containerd.sock# socket 文件存在即表示可用'刚才我们把docker卸载'# 我们把docker重新安装上去[root@Docker ~]# tree ././├── download│ ├── docker-29.1.4.tgz│ └── docker-compose-linux-x86_64└── install-docker.sh[root@Docker ~]# ./install-docker.sh i[root@Docker ~]# systemctl is-active docker.serviceactive
2)修改 Docker service文件,指向 Containerd socket[root@Docker ~]# ls /usr/lib/systemd/system/docker.service/usr/lib/systemd/system/docker.service[root@Docker ~]# vim /usr/lib/systemd/system/docker.service...xxx[Service]Type=notifyExecStart=/usr/bin/dockerd --containerd /run/containerd/containerd.sock --debug# 关键: --containerd 参数指定 Containerd 的 socket 路径# --debug 他是一个启动参数,它的作用是启用调试模式[root@Docker ~]# systemctl daemon-reload[root@Docker ~]# systemctl restart docker.service
3)验证:Docker 拉取镜像,Containerd 也能看到[root@Docker ~]# cat /etc/docker/daemon.json{ "registry-mirrors": ["https://docker.1ms.run"]}[root@Docker ~]# docker pull docker.1ms.run/nginxStatus: Downloaded newer image for docker.1ms.run/nginx:latest[root@Docker ~]# docker imagesdocker.1ms.run/nginx:latest 1881968aff6f 237MB[root@Docker ~]# ctr ns lsNAME LABELSdefaultmoby ✅️ 多出来一个 moby 命名空间!kpyun"Docker 的容器都放在 moby 命名空间下"[root@Docker ~]# ctr -n moby i lsdocker.1ms.run/nginx:latest linux/386,linux/amd64,linux/arm/v5,linux/arm64/v8'这个拉取也是多架构的!' --> 没有办法正常push推送镜像的双重验证
# Docker 运行一个容器[root@Docker ~]# docker run -d --restart unless-stopped -p 90:80 --name v1 docker.1ms.run/nginx:latest[root@Docker ~]# docker psfd51ea98fab3 docker.1ms.run/nginx:latest'Containerd 也能看到同一个容器'[root@Docker ~]# ctr -n moby c ls'c==container容器'fd51eaxxx docker.1ms.run/nginx:latest io.containerd.runc.v2[root@Docker ~]# ctr -n moby t ls't==task任务'TASK PID STATUSfd51eaxxx 9921 RUNNING'主进程PID是9921'[root@Docker ~]# docker top v1root 9921 nginx: master process nginx -g daemon off;'从docker中看,主进程也是9921'# 进程 PID 完全一致[root@Docker ~]# ctr -n moby task exec -t -exec-id $RANDOM fd51ea..xxx /bin/bash[root@Docker ~]# ctr -n moby t exec --exec-id $RANDOM -t 81337... shroot@fd51ea98fab3:/# hostname -I 172.17.0.2# 和 docker0 网卡在同一网段root@fd51ea98fab3:/# find /sys/class/net -name "*"/sys/class/net/sys/class/net/eth0/sys/class/net/loroot@fd51ea98fab3:/# exitexit[root@Docker ~]# docker inspect -f {{.NetworkSettings.Networks.bridge.IPAddress}} $(docker ps -n=1 -q)172.17.0.2'和docker查看的IP一致'📌 综上:Docker 底层调用 Containerd,Docker 负责上层功能(网络、构建、编排),Containerd 负责底层容器运行时,各司其职
实战:招聘数据分析项目部署
手动部署Django
1)Python组件安装[root@Docker ~]# python --versionPython 3.12.11# Rocky Linux 10系统上已经安装的Python版本[root@Docker ~]# which python3/usr/bin/python3'接下来安装必要的开发工具'[root@Docker ~]# dnf install python3-pip python3-devel -y# 安装Python包管理工具和其他必需组件[root@Docker ~]# dnf install gcc openssl-devel bzip2-devel libffi-devel zlib-devel -y# 安装编译工具(用于后续可能需要编译的Python包)[root@Docker ~]# python --versionPython 3.12.12'安装工具的过程中,发现顺便还给自己的的python升了级'[root@Docker ~]# pip3 --versionpip 23.3.2 from /usr/lib/python3.12/site-packages/pip (python 3.12)# 验证pip安装Note
关于详细的python安装及虚拟化环境
✅️ 参考 Ansible自动化📚 有详细介绍
uv和venvpip与pipx
2)创建虚拟环境[root@Docker ~]# mkdir /boke# 创建项目目录[root@Docker ~]# cd /boke[root@Docker boke]# python3 -m venv myenv# 使用内置的venv模块创建虚拟环境-m: 指定模块venv: 创建虚拟环境的工具myenv: 给这个虚拟环境起的名字[root@Docker boke]# lltotal 0drwxr-xr-x 5 root root 74 May 13 17:09 myenv'当前目录下会多出来一个目录'[root@Docker boke]# source myenv/bin/activate((myenv) ) [root@Docker boke]## 激活虚拟环境=========================================✅️激活成功的标志:你的命令行提示符前面会出现一个 (myenv) 的前缀从现在开始,你使用 pip install 安装的任何库,都只会存在于这个 myenv 环境里,与你的系统全局环境完全隔离如何退出虚拟环境❓️--->"deactivate"((myenv) ) [root@Docker boke]# deactivate[root@Docker boke]#
4)安装各个组件[root@Docker boke]# source myenv/bin/activate((myenv) ) [root@Docker boke]# pip install --upgrade pip# 升级pip到最新版本Successfully installed pip-26.1.1((myenv) ) [root@Docker boke]# pip install "Django>=4.2,<5.0"✅️ 表示安装Django 4.2版本或更高,但低于5.0版本Successfully installed Django-4.2.30 asgiref-3.11.1 sqlparse-0.5.5((myenv) ) [root@Docker boke]# pip install gunicornSuccessfully installed gunicorn-26.0.0 packaging-26.2✅️ 用于部署Python Web应用具体功能: 1.接收来自Nginx等Web服务器的HTTP请求 2.将请求转发给Django应用处理 3.处理多个并发请求 4.管理多个工作进程提高性能Successfully installed gunicorn-26.0.0 packaging-26.2((myenv) ) [root@Docker boke]# pip install mysqlclient✅️ 安装MySQL客户端# 让Django能够与MySQL数据库通信=========================================中间报错了,因为少安装了一个数据库开发的工具!No match for argument: mysql-devel发现自己仓库中并没有它,当然你也可以去MySQL官网去下载# 我们可以直接安装 MariaDB 的开发包来代替[root@Docker boke]# dnf install mariadb-devel=========================================[root@Docker boke]# pip install mysqlclient -i https://pypi.tuna.tsinghua.edu.cn/simple# 后面-i指定了一个地址进行加速安装Successfully installed mysqlclient-2.2.8'只不过是不同的开发工具罢了, 依旧是mysql的组件'
5)验证安装((myenv) ) [root@Docker boke]# pip listPackage Version----------- -------asgiref 3.11.1Django 4.2.30gunicorn 26.0.0mysqlclient 2.2.8packaging 26.2pip 26.1.1sqlparse 0.5.5((myenv) ) [root@Docker boke]# python -c "import django; print(f'Django版本: {django.get_version()}')"Django版本: 4.2.30"在虚拟环境中测试Django"((myenv) ) [root@Docker boke]# python -c "import MySQLdb; print('MySQL client working')"MySQL client working"测试MySQL连接库"((myenv) ) [root@Docker boke]# gunicorn --versiongunicorn (version 26.0.0)"测试Gunicorn"
6)创建测试项目验证((myenv) ) [root@Docker boke]# django-admin startproject kpyun=========================================django-admin = Django的管理命令工具startproject = 创建新项目的命令kpyun = 您给项目起的名字((myenv) ) [root@Docker boke]# lltotal 0drwxr-xr-x 3 root root 36 May 13 17:35 kpyundrwxr-xr-x 5 root root 74 May 13 17:09 myenv"创建一个名为 kpyun 的新文件夹,并在里面生成Django项目的基本结构"((myenv) ) [root@Docker boke]# tree kpyun/kpyun/├── kpyun # 项目配置文件夹│ ├── asgi.py # ASGI配置│ ├── __init__.py│ ├── settings.py # 项目设置│ ├── urls.py # URL路由配置│ └── wsgi.py # WSGI配置└── manage.py # Django管理脚本((myenv) ) [root@Docker boke]# cd kpyun/((myenv) ) [root@Docker kpyun]# python manage.py migrateOperations to perform: Apply all migrations: admin, auth, contenttypes, sessionsRunning migrations: Applying contenttypes.0001_initial... "OK" Applying auth.0001_initial... "OK" Applying admin.0001_initial... "OK"# 运行数据库迁移=========================================python = Python解释器manage.py = Django项目自带的管理脚本migrate = 执行数据库迁移命令✅️ 什么是"迁移"?- Django使用ORM(对象关系映射)管理数据库- 每个模型(Model)都对应数据库中的表- migrate命令会根据Django内置的和您创建的模型,在数据库中创建相应的表✅️ 实际效果:在数据库中创建Django需要的系统表(如用户表、权限表等) 1.即使你创建 Django 项目时没有立即配置自己的数据库 2.或者你的应用模型(models)还是空的,你也需要执行它 3.只有这些系统表被创建出来,你才能正常使用 Django 的内置功能 # 用户认证、会话管理和管理后台等
7)启动服务((myenv) ) [root@Docker kpyun]# python manage.py runserver 0:8000May 13, 2026 - 09:49:07Django version 4.2.30, using settings 'kpyun.settings'Starting development server at http://0.0.0.0:8000/# 启动开发服务器解释:runserver = 启动Django内置开发服务器0:8000 = 监听地址和端口 0 = 0.0.0.0,表示接受所有IP地址的连接 8000 = 端口号[root@Docker ~]# ss -lntup | grep 80tcp LISTEN 0 0.0.0.0:8000 users:(("python",pid=10921,fd=4))=========================================访问页面有报错!Django 默认只允许通过 localhost、127.0.0.1 或 ::1 访问而我们是用 10.0.0.99:8000(内网IP)访问的,Django 认为这不安全,所以拒绝了请求'我是用物理机自带的浏览器访问的'((myenv) ) [root@Docker kpyun]# vim kpyun/settings.pyALLOWED_HOSTS = ['*']⚠️ 中间有引号!!!# 允许所有 Host(仅限开发测试)"再次启动"((myenv) ) [root@Docker kpyun]# python manage.py runserver 0:8000Starting development server at http://0.0.0.0:8000/
新业务部署
- 手动部署一个 Django + MySQL + MongoDB 的 Python 项目
- 后续会将业务容器化
==架构==
| 组件 | 技术 | 端口 |
|---|---|---|
| Web 框架 | Django 3.2.8 | 8000 |
| 关系数据库 | MySQL 8.0.36 | 3306 |
| 文档数据库 | MongoDB 8.2 | 27017 |
| 依赖管理 | pip + requirements.txt | - |
1)上传 & 解压项目[root@Docker ~]# cd /tmp/[root@Docker tmp]# rz....[root@Docker tmp]# ls recruitment_data_analysis.ziprecruitment_data_analysis.zip[root@Docker tmp]# rm -rf /boke[root@Docker tmp]# mkdir /py-app && unzip recruitment_data_analysis.zip -d /py-app[root@Docker tmp]# ll /py-app/total 824drwxr-xr-x 4 root root 4096 May 18 17:01 app-rw-r--r-- 1 root root 718376 Nov 8 2025 lagou_jobs.js-rw-r--r-- 1 root root 703 Nov 11 2022 manage.py-rw-r--r-- 1 root root 733 Nov 11 2022 README.md...............
2)创建虚拟环境 & 环境安装配置[root@Docker tmp]# cd /py-app/[root@Docker py-app]# python3 -m venv myenv[root@Docker py-app]# source myenv/bin/activate((myenv) ) [root@Docker py-app]# pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/.........xxxxxx[notice] A new release of pip is available: 23.3.2 -> 26.1.1[notice] To update, run: pip install --upgrade pip
3)验证[root@elk91 recruitment_data_analysis]# pip list | grep -i djangoDjango 3.2.8[notice] To update, run: pip install --upgrade pip((myenv) ) [root@Docker py-app]# pip listPackage Version------------------ -----------asgiref 3.11.1attrs 26.1.0certifi 2026.4.22charset-normalizer 3.4.7Django 3.2.8...................
4)MySQL 容器(host 网络模式)((myenv) ) [root@Docker py-app]# docker container run \ -e MYSQL_ALLOW_EMPTY_PASSWORD="yes" \ -d --name mysql-server --network host --restart unless-stopped \ -e MYSQL_DATABASE="recruitment_data_analysis" \ -e MYSQL_USER="jiu" \ -e MYSQL_PASSWORD="Oldboy123.com" \ mysql:8.0.36 \ --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci \ --default-authentication-plugin=mysql_native_password[root@Docker ~]# docker psmysql:8.0.36 "docker-entrypoint.s…" 6 seconds ago Up 6 seconds
5)MongoDB 容器((myenv) ) [root@Docker py-app]# docker pull docker.xuanyuan.run/mongodb/mongodb-community-server:8.3.2-ubuntu2204# 自行改名((myenv) ) [root@Docker py-app]# docker imagesmongodb/mongodb-community-server:8.3.2-ubuntu2204 37fxxx 1.19GB((myenv) ) [root@Docker py-app]# docker run --name mongodb-server -d --network host --restart unless-stopped mongodb-community-server:8.3.2-ubuntu2204((myenv) ) [root@Docker py-app]# docker psmongodb-community-server:8.3.2-ubuntu2204 "python3 /usr/local/…" 7 seconds ago Up 7 seconds
6)配置文件的修改((myenv) ) [root@Docker py-app]# vim ./recruitment_data_analysis/settings.py# 默认数据库设置,使用MySQL数据库DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 使用MySQL数据库驱动 'NAME': 'recruitment_data_analysis', # 数据库名称 'USER': 'jiu', # 数据库用户名 'PASSWORD': 'Oldboy123.com', # 数据库密码,此处明文存储不安全,应使用环境变量或更安全的方式存储 'HOST': 'localhost', # 数据库主机地址 'PORT': '3306', # 数据库端口 }}'上面的设置是mysql配置文件' <-- 我这里用的是host主机网络, localhost ✅️📌 MongoDB 连接地址:`mongodb://localhost:27017/`(硬编码在 `app/views.py` 中)((myenv) ) [root@Docker py-app]# grep mongodb ./app/views.py client = pymongo.MongoClient("mongodb://localhost:27017/") client = pymongo.MongoClient("mongodb://localhost:27017/") client = pymongo.MongoClient("mongodb://localhost:27017/") client = pymongo.MongoClient("mongodb://localhost:27017/")
7)Django 迁移"这一步会创建 Django 自带的表, 以及项目中定义的业务表"((myenv) ) [root@Docker py-app]# python3 manage.py makemigrationsPrefix dict has been built successfully.((myenv) ) [root@Docker py-app]# python3 manage.py migrate# 在 MySQL 中生成对应的表
8)导入业务数据两个数据库都需要导入数据: MySQL 导 `recruitment_data_analysis.sql` MongoDB 导 `lagou_jobs.js`((myenv) ) [root@Docker py-app]# docker cp recruitment_data_analysis.sql mysql-server:/tmp/Successfully copied 103kB to mysql-server:/tmp/((myenv) ) [root@Docker py-app]# docker exec -it mysql-server bashbash-4.4# mysql recruitment_data_analysis < /tmp/recruitment_data_analysis.sql((myenv) ) [root@Docker py-app]# docker cp lagou_jobs.js mongodb-server:/tmp/Successfully copied 722kB to mongodb-server:/tmp/((myenv) ) [root@Docker py-app]# docker exec -it mongodb-server bashmongodb@Docker:/$ ls /tmp/lagou_jobs.jsmongodb@Docker:/$ mongosh lagou_jobs --file /tmp/lagou_jobs.js
9)创建超级用户((myenv) ) [root@Docker py-app]# python3 manage.py createsuperuser用户名: root密码: Linux@2026'用于自带管理后台/admin登录'
10)启动项目((myenv) ) [root@Docker py-app]# python3 manage.py runserver 10.0.0.99:8000May 18, 2026 - 19:31:25Django version 3.2.8Starting development server at http://10.0.0.99:8000/
'先注册,后登录'

Django 后台: http://10.0.0.99:8000/admin用户名: root密码: Linux@2026'我们刚自己设置的'

今日回顾
- Containerd 容器管理工具:
- 环境部署 →
ctr version验证 - 命名空间 →
ctr ns隔离镜像/容器/任务 - 镜像管理 →
ctr image pull/ls/export/import/mount - 容器管理 →
ctr c create/ls/rm - 任务管理 →
ctr task start/ls/kill/rm/pause/resume/ps - 存储卷 →
--mount type=bind,src=...,dst=...,options=rbind:rw - 推送镜像到 Harbor →
ctr i push -u admin:pass
- 环境部署 →
- Docker 集成 Containerd:
dockerd --containerd /run/containerd/containerd.sock - 手动部署 Python Django 项目(MySQL + MongoDB)
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章智能推荐
1
函数与数组
Shell脚本Shell函数定义与数组操作,涵盖局部/全局变量、递归函数及数组遍历
2
循环与case多分支
Shell脚本Shell循环结构与case多分支语句,涵盖for/while/until循环及实战脚本
3
Docker结课实践考核
Docker容器Docker容器技术结课实践考核,涵盖容器化部署、编排及运维实战
4
数值运算与if条件判断
Shell脚本Shell数值运算方法与if条件判断结构,涵盖整数/字符串比较及文件测试
5
传统业务容器化
Docker容器以RuoYi-Vue为例演示Java项目全流程容器化,涵盖Maven打包、Vue前端与Compose多服务编排部署
随机文章随机推荐



