Podman 容器镜像构建全流程指南
目标:从零构建
myhome:1.0镜像(基于 Ubuntu 20.04,集成 Python 2、编译工具链、repo、sudo),并运行验证。
阶段一:环境准备
第 1 步:确认 Podman 已安装
bash
podman --version
预期 :podman version 4.x.x
作用:确认容器引擎可用。
第 2 步:确认环境已清空
bash
podman ps -a
podman images
预期:两条命令都只剩表头,没有任何容器和镜像。
作用:保证从干净状态开始,避免旧镜像干扰。
第 3 步:配置镜像源(用户级,不用 sudo)
bash
mkdir -p ~/.config/containers
cat > ~/.config/containers/registries.conf << 'EOF'
unqualified-search-registries = ["docker.io"]
[[registry]]
prefix = "docker.io"
location = "docker.m.daocloud.io"
EOF
作用 :告诉 Podman------拉 docker.io 的镜像时,走 daocloud 这个国内镜像站。
验证:
bash
cat ~/.config/containers/registries.conf
应该看到刚写入的三行内容。
为什么用 daocloud? 这台机器 Docker Hub 官方源连不上(
registry-1.docker.io超时),daocloud 经测试可用。中科大docker.mirrors.ustc.edu.cn和网易hub-mirror.c.163.com在本机 DNS 解析失败。
阶段二:准备基础镜像
第 4 步:拉取 ubuntu:20.04
bash
podman pull ubuntu:20.04
预期:
Resolving "ubuntu" using unqualified-search registries (~/.config/containers/registries.conf)
Trying to pull docker.io/library/ubuntu:20.04...
Getting image source signatures
Copying blob ... done
Copying config b7bab04fd9aa done
Writing manifest to image destination
Storing signatures
b7bab04fd9aa...
作用:把基础镜像下载到本地,作为后面构建的地基。
验证:
bash
podman images
应该看到:
REPOSITORY TAG IMAGE ID SIZE
docker.io/library/ubuntu 20.04 b7bab04fd9aa 75.2 MB
阶段三:写 Containerfile
第 5 步:建构建目录并写 Containerfile
bash
mkdir -p ~/container-build
cd ~/container-build
写入 Containerfile:
bash
cat > Containerfile << 'EOF'
FROM ubuntu:20.04
# 第 1 步:换清华源(用 http,因为此时容器里还没有 ca-certificates)
RUN sed -i 's|http://archive.ubuntu.com|http://mirrors.tuna.tsinghua.edu.cn|g' \
/etc/apt/sources.list \
&& sed -i 's|http://security.ubuntu.com|http://mirrors.tuna.tsinghua.edu.cn|g' \
/etc/apt/sources.list
# 第 2 步:先装 ca-certificates 和 curl/wget(http 不需要证书)
RUN apt update && apt install -y --no-install-recommends \
ca-certificates curl wget \
&& rm -rf /var/lib/apt/lists/*
# 第 3 步:根证书已就绪,把源切换成 https
RUN sed -i 's|http://mirrors.tuna.tsinghua.edu.cn|https://mirrors.tuna.tsinghua.edu.cn|g' \
/etc/apt/sources.list
# 第 4 步:装编译工具链 + python2 + sudo(此时 https 可用)
RUN apt update && apt install -y --no-install-recommends \
git \
build-essential cpio unzip rsync bc xz-utils \
sudo vim \
python2 python2-dev \
&& ln -sf /usr/bin/python2 /usr/bin/python \
&& rm -rf /var/lib/apt/lists/*
# 第 5 步:装 repo(py2 兼容版)
RUN curl -sSL https://storage.googleapis.com/git-repo-downloads/repo-1 \
-o /usr/local/bin/repo \
|| curl -sSL https://storage.googleapis.com/git-repo-downloads/repo \
-o /usr/local/bin/repo \
&& chmod +x /usr/local/bin/repo
# 第 6 步:建用户 + 配免密 sudo
RUN useradd -m -s /bin/bash -G sudo user_name \
&& echo 'user_name ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/user_name \
&& chmod 0440 /etc/sudoers.d/user_name
USER user_name
WORKDIR /home/user_name
CMD ["bash"]
EOF
关键设计点:
| 步骤 | 为什么这样写 |
|---|---|
换源用 http:// |
容器里还没有 ca-certificates,https 会握手失败(踩过坑) |
先装 ca-certificates |
拿到根证书,后面 https 才可用 |
再切回 https:// |
走加密通道 |
ln -sf python2 python |
v821 SDK 脚本默认调 python,指向 py2 |
NOPASSWD:ALL |
容器内免密 sudo,避免编译脚本卡住要输密码 |
最后才 USER chenkang |
装软件需要 root,装完再切普通用户 |
验证:
bash
cat Containerfile
ls -la
阶段四:构建镜像
第 6 步:构建
bash
podman build -t myhome:1.0 . 2>&1 | tee build.log
参数说明:
| 参数 | 作用 |
|---|---|
-t myhome:1.0 |
给镜像起名 myhome,版本 1.0 |
. |
当前目录,作为构建上下文(找 Containerfile、传文件进去) |
| `2>&1 | tee build.log` |
预期结尾:
Successfully tagged localhost/myhome:1.0
验证:
bash
podman images
应该看到两个镜像:
REPOSITORY TAG IMAGE ID SIZE
localhost/myhome 1.0 xxxxxxxxxxxx 482 MB
docker.io/library/ubuntu 20.04 b7bab04fd9aa 75.2 MB
第 7 步:查看镜像分层
bash
podman history myhome:1.0
作用:看清镜像是怎么一层层叠出来的,对应 Containerfile 的每一步。
预期结构(从上到下 = 从新到旧):
ID CREATED BY SIZE
ecf4fc53ee06 CMD ["bash"] 0 B
<missing> WORKDIR /home/chenkang 0 B
<missing> USER chenkang 0 B
<missing> useradd -m ...chenkang 366 kB
e5accc04... curl repo 44 kB
8cfcae73... apt install python2 build-essential ... 389 MB
1affcb17... sed ... 换 https 13.8 kB
960ca9f3... apt install ca-certificates 16.9 MB
9f3ee0f6... sed ... 换 http ← FROM ubuntu:20.04
b7bab04fd9aa CMD ["/bin/bash"] 0 B
<missing> ADD file:... 75.2 MB ← ubuntu 根文件系统
...
最底下带 FROM docker.io/library/ubuntu:20.04 注释的那层,就是整个镜像的基础层。
阶段五:运行容器
第 8 步:启动容器(先验证环境)
bash
podman run -it --rm myhome:1.0 bash
参数说明:
| 参数 | 作用 |
|---|---|
-it |
交互式 + 分配伪终端(能进 bash 敲命令) |
--rm |
退出后自动删除容器(不留垃圾) |
myhome:1.0 |
用哪个镜像 |
bash |
容器启动后执行的命令(进 shell) |
执行后命令行提示符会变成容器内的,类似:
chenkang@xxxxxxxxxxx:~$
第 9 步:在容器内验证
bash
whoami
python --version
python2 --version
python -c "import zlib; print('zlib OK')"
repo --version
sudo -n true && echo "sudo OK"
预期:
chenkang
Python 2.7.18
Python 2.7.18
zlib OK
<repo 版本号>
sudo OK
退出容器:
bash
exit
第 10 步:挂载代码目录运行(实际使用场景)
bash
podman run -it --rm \
-v /home/chenkang/project:/home/chenkang/project:Z \
myhome:1.0 bash
参数说明:
| 参数 | 作用 |
|---|---|
-v 宿主机路径:容器路径:Z |
把宿主机目录挂进容器,两边共享文件 |
:Z |
让 SELinux 给挂载点打正确标签(不开 SELinux 的机器可省略) |
进容器后:
bash
cd /home/chenkang/project # 这里就是宿主机的代码
ls # 能看到宿主机 project 目录里的文件
阶段六:日常运维命令
| 场景 | 命令 |
|---|---|
| 查看运行中的容器 | podman ps |
| 查看所有容器(含已停止) | podman ps -a |
| 查看镜像 | podman images |
| 进一个正在运行的容器 | podman exec -it <容器名> bash |
| 停止容器 | podman stop <容器名> |
| 删除容器 | podman rm <容器名> |
| 删除镜像 | podman rmi myhome:1.0 |
| 清理悬空镜像/缓存 | podman image prune |
| 彻底清空所有 | podman system reset --force |
| 查看镜像分层 | podman history myhome:1.0 |
| 查看容器详细信息 | podman inspect <容器名> |
附录:核心概念速查
镜像、容器、Containerfile 的关系
Containerfile (文本) ← 菜谱:描述环境该怎么配
│ podman build
▼
镜像 myhome:1.0 (静态文件) ← 蛋糕:一次构建,永久留存,只读
│ podman run
▼
容器 (运行中的进程) ← 吃蛋糕的过程:可启动多个,用完即删
镜像分层
- 每一层是只读的,构建后不可变
- 层是共享的:两个镜像共用同一基础层,磁盘只占一份
- 容器在顶层加一个可写层:运行时产生的文件都在这一层,容器删除即消失
为什么 myhome 的 482 MB 里包含 ubuntu 的 75.2 MB
不是复制,是引用 。myhome 最底层的 ID 就是 b7bab04fd9aa(ubuntu 的 ID),两者共享那 75.2 MB。
podman build vs podman run
| 命令 | 作用 | 输入 | 输出 |
|---|---|---|---|
podman build |
造镜像 | Containerfile | 一个镜像 |
podman run |
跑容器 | 一个镜像 | 一个运行中的容器 |
- build 只在改了 Containerfile 后重跑
- run 可以跑很多次,每次基于同一镜像起新容器
-