资讯动态

openEuler 上安装配置 Docker 与 Dockerfile 构建实战指南

发布时间:2026/10/16 18:08:15 来源:尧图企业网站定制
最近给一台跑着 openEuler 的服务器折腾容器环境正好把 Docker 从安装、镜像管理到 Dockerfile 构建这一整条链路重新走了一遍。openEuler 这个开源系统在国内服务器端的普及率越来越高但它既不是 CentOS 也不是 Ubuntu如果你是第一次在它上面装 Docker难免会踩到一些跟发行版强相关的坑。这篇东西把我实际操作中的完整流程、踩过的坑、以及最后落地的构建方案都记录下来给同样需要在 openEuler 上跑容器的朋友一份可以直接抄作业的参考。内容覆盖三个阶段装环境、管镜像、写 Dockerfile。不管你是刚开始接触容器的新手还是已经在别的发行版上用过 Docker、想快速迁移到 openEuler 的同学按这篇文章的顺序走一遍基本能把这套东西跑起来。部分细节我会说明哪些是我按常见实践补的哪些是实际验证过的方便你判断。1. 环境准备先把 openEuler 的基础盘摸清楚1.1 版本选型与系统基础检查动手之前先确认两件事系统版本和内核版本。我建议直接用 openEuler 22.03 LTS 或者更新的 LTS 版本容器场景求稳LTS 的生命周期和维护周期都更长软件源里的包版本相对保守但可靠性有保障。先执行两条命令cat /etc/openEuler-release uname -r内核版本只要高于 4.19跑 Docker 都问题不大。20.03 之前的老版本内核偏旧遇到 overlay2 存储驱动兼容性问题会比较折腾能用新版就别用老版本。然后做一轮基础检查缺哪个补哪个# 磁盘空间镜像和容器日志都吃磁盘至少留出 20G 以上 df -h # 内存2G 以下跑容器会很吃力 free -m # SELinux 状态openEuler 默认是 enforcing getenforce # 防火墙状态 systemctl status firewalld这里重点说下 SELinux。openEuler 默认开启 SELinux而且级别是 enforcing这会让 Docker 在挂载宿主机目录时经常报 permission denied。很多教程直接让你setenforce 0这在测试环境没问题但生产环境我反对一关了之。更稳妥的做法是先用setenforce 0验证问题确实出在 SELinux 上然后恢复 enforcing改用 Docker 的 SELinux 标签方案比如挂载卷时加:z后缀或者针对容器目录写 SELinux 策略。这个我在后面的常见问题部分再展开。防火墙也是个容易忽略的点。openEuler 默认带 firewalld容器端口映射需要 iptables 规则配合。如果系统启用了 firewalld 并且规则很严格可能出现容器起来了、但宿主机上访问不了映射端口的情况。测试阶段可以先放行相关端口或者直接在 firewalld 里放行 Docker 使用的网段和端口不要贸然停掉防火墙服务。1.2 更新系统与软件源配置openEuler 默认的软件源是官方源安装前先把系统包索引更新一遍dnf makecache dnf update -y注意 openEuler 的包管理器虽然是 dnf但系统里同时保留了 yum 命令作为兼容入口两者底层是同一条链路你习惯用哪个都行。更新完重启一次系统确保内核和用户态组件版本一致这一步能避免后面很多莫名其妙的问题。如果你所在的网络访问官方源速度不理想可以换到配置好的镜像源。具体做法是编辑/etc/yum.repos.d/openEuler.repo把 baseurl 指到可用的镜像站。改完执行dnf makecache重新生成缓存。我不在这里写死某个镜像站地址因为不同网络环境可达性差异很大你根据自己的实际情况选一个响应快的就行。核心原则只有一个dnf install能正常解析和下载包后续装 Docker 才顺畅。还有一个小点容易被忽略时区。容器里跑日志应用很依赖时间同步建议确保宿主机已经配置好 chrony 或者 systemd-timesyncd。服务器时间不准容器内的时间同样不准日志乱序查起来非常痛苦。2. Docker 引擎安装三种方式选哪种怎么选2.1 系统源直接安装省心首选openEuler 的官方软件源里直接带了 docker-engine 包这是我认为最适合大多数人的安装方式dnf install -y docker-engine这种方式的优势在于所有依赖都由 openEuler 源统一管理不会出现把系统依赖搞乱的问题。装完之后配置文件、systemd 服务单元、命令行补全都会一并给你准备好。我实测下来22.03 LTS 里带的 docker-engine 版本对于常规容器使用完全够用。缺点也有版本相对滞后不会第一时间跟上 Docker 上游的新版本。如果你不需要实验性功能完全不用担心这一点。生产环境我反而更喜欢滞后几个小版本的包因为经过了发行版的测试和修修补补稳定性更有保证。2.2 配置 Docker 官方仓库安装最新版想用最新版的话可以配置 Docker 官方软件源。这里有个细节要注意官方源默认按 CentOS/RHEL 的版本号来下发仓库文件openEuler 虽然能兼容大部分 RPM 包但直接套用官方脚本大概率会失败因为发行版标识对不上。手动配置时先新建仓库文件然后编辑 baseurl 字段。关键操作是把$releasever手动替换成与当前 openEuler 兼容的 CentOS 版本号。比如 openEuler 22.03 的系统我会在 baseurl 里写死 el 对应的版本号可以用 CentOS 8 或更高版本的仓库地址来试探兼容性。配置完成后dnf makecache dnf install -y docker-ce docker-ce-cli containerd.io这条路线我实际试过能装上能用但在装依赖的过程里要留意冲突提示特别是 libseccomp、iptables 这类系统级组件如果 dnf 提示要升级或替换建议逐个确认后再继续。这种方式适合你有明确的版本需求、并且愿意自己处理兼容性问题的场景。2.3 离线二进制安装应急备用还有一种情况内网环境完全上不了外网。这时可以提前在有网的机器上下载 Docker 静态二进制包然后拷贝进内网解压使用tar xzvf docker-版本.tgz cp docker/* /usr/bin/二进制方式只包含 Docker 核心组件没有 systemd 服务文件需要自己编写服务配置。我一般会手写一个最小可用的 docker.service放在/etc/systemd/system/下然后systemctl daemon-reload systemctl enable --now docker离线安装的坑在于静态包依赖系统的 iptables、containerd 等组件版本内网机器如果基础环境太老装完照样跑不起来。所以这个方案我定位为应急备用能用系统源装就优先用系统源。2.4 启动服务与安装验证不管哪条路装完之后的验证动作是一致的systemctl enable --now docker docker info docker versiondocker info输出里有两项需要重点检查Storage Driver正常应该是 overlay2。如果显示 vfs说明驱动没配对成功容器读写性能会非常差需要检查内核模块加载情况。Cgroup Driver建议是 systemd。这能让 Docker 的 cgroup 管理与 systemd 保持一致避免两个管理器同时操作 cgroup 导致资源统计混乱。最后跑一个经典测试docker run --rm hello-world能看到 Hello from Docker! 的输出就说明引擎工作正常了。3. 镜像管理从拉取到清理的完整套路3.1 先配置镜像加速否则第一步就卡住openEuler 在国内网络环境下默认从 Docker Hub 拉镜像的速度通常不理想甚至超时。好在 Docker 提供了 registry mirror 机制在/etc/docker/daemon.json里配置加速地址即可。编辑前先确认文件是否存在不存在就创建{ registry-mirrors: [ https://你的加速地址 ] }配置完重启 Dockersystemctl daemon-reload systemctl restart docker注意一点加速配置只管 Docker Hub 官方仓库的镜像拉取对于从第三方私有仓库拉镜像没有作用。选加速地址时建议优先挑离你网络最近的响应快和稳定比名气大更重要。我见过有人配了个远距离的公共加速结果拉一个基础镜像要等十分钟这不是工具的问题是选型的问题。另外daemon.json里如果同时配置了registry-mirrors、log-opts、storage-driver等多项内容注意 JSON 格式别出错。改完后用docker info查看 Registry Mirrors 一栏是否生效没生效就去翻 daemon 日志。3.2 拉取、查看与标记镜像镜像操作的第一步是拉取。常用的几条docker pull nginx:latest docker pull golang:1.21-alpine标签不写默认是 latest但生产环境我强烈建议写明确版本号。latest 是一个浮动标签今天拉和三个月后拉很可能不是同一个镜像这会给环境一致性埋雷。镜像管理讲究确定性我看到 Dockerfile 里写FROM nginx:latest就会下意识皱眉改成FROM nginx:1.27-alpine这种固定版本才是可靠的做法。查看本地镜像docker images docker image ls这两条命令作用一样docker image ls是更规范的子命令写法。输出里出现一堆none的仓库名、标签为空的镜像时别慌那叫悬空镜像dangling image通常是构建新镜像时产生的中间残留层后面清理环节会处理。标记镜像用 tag它的真正价值在于给镜像规范化命名。比如从公共仓库拉下来的镜像要推送到自己的私有仓库docker tag nginx:1.27 registry.internal.example.com/library/nginx:1.27标记之后就可以docker push推送到目标仓库。这个操作纯粹是给镜像加一个引用别名不会复制镜像内容所以瞬间完成。理解这一点很重要镜像只有一份tag 只是不同名字的指针。3.3 镜像导入导出离线搬运的正确姿势内网环境拷镜像要用 save 和 load 这对组合docker save -o nginx.tar nginx:1.27 docker load -i nginx.tarsave保存的是镜像的完整内容包含所有镜像层、元数据和 tag 信息load可以完整恢复。兄弟们用 gzip 压缩一下更省空间docker save nginx:1.27 | gzip nginx.tar.gz docker load -i nginx.tar.gz另一对命令是 export 和 importdocker export -o container.tar 容器ID cat container.tar | docker import - my-image:1.0export导出的不是镜像而是容器的文件系统快照。它丢掉了镜像的历史分层、CMD 等信息import 导入后得到的镜像像是从零开始的。什么时候用 export比如你想把某个容器改好后的状态固化下来分发但它原来的历史层你已经不关心了。常规镜像搬运场景优先用 save/load因为更干净、可追溯。3.4 删除与磁盘清理删除镜像用 rmidocker rmi nginx:1.27有容器正在使用这个镜像时删除会报冲突必须先把相关容器删掉。批量清理悬空镜像docker image prune加-f跳过确认。清理整个系统所有未使用的资源和悬空镜像docker system prune -a-a的含义是连没有被容器引用的镜像一起删掉这个命令非常激进执行前一定看清楚提示。我建议在本地先跑docker system df看下空间占用分布再决定清理到什么程度。镜像层多、日志文件大都可能让磁盘悄无声息地满了。4. Dockerfile 构建实战写出干净、可维护的镜像4.1 核心指令的选择逻辑Dockerfile 第一行几乎都是 FROM它决定了基础镜像。选哪个基础镜像要考虑两点是否包含你要的运行时镜像体积是否可接受。比如 Python 应用python:3.12-slim比python:3.12体积小很多适合大多数场景而alpine变体体积最小但某些依赖需要编译安装反而会增加复杂度。体积不是唯一指标构建速度和运行稳定性更重要。RUN 是执行构建期命令的指令常见的坑是层数失控。每一条 RUN 都会产生一个新的镜像层层越多镜像越大。正确做法是把相关的命令合并成一条RUN apt-get update \ apt-get install -y build-essential \ rm -rf /var/lib/apt/lists/*最后一步清理包管理器的缓存名单可以显著减小镜像体积。这个习惯我从写第一个 Dockerfile 起就保持着效果立竿见影。COPY 和 ADD 都是往镜像里加文件。我的选择原则是能用 COPY 就绝不用 ADD。ADD 自带自动解压和远程 URL 下载功能听起来方便但行为不够透明。比如你本来只想拷贝一个压缩包进去ADD 却会自动把它解压结果和预期完全不符。COPY 语义单一不出意外。从可维护性角度可预期的工具才是好工具。WORKDIR 指定工作目录后面所有 RUN、CMD、ENTRYPOINT 都基于这个目录执行。建议把它显式写出来而不是依赖默认的根目录。ENTRYPOINT 和 CMD 的配合常被误解ENTRYPOINT 定义容器的固定执行程序CMD 提供默认参数。如果只写 CMD你可以在启动命令后面直接覆盖如果只想固定程序、又允许传参就用 ENTRYPOINT 加 CMD 的组合。我常用的写法ENTRYPOINT [python, app.py] CMD [--port8080]ENV 设置环境变量USER 切换运行用户。生产环境镜像里默认用 root 跑容器不是好习惯一旦容器被攻破宿主机的访问权限直接拉满。我一般会单独建一个应用用户RUN useradd -m -s /bin/bash appuser USER appuser4.2 多阶段构建让镜像瘦身的关键技巧多阶段构建是 Docker 最重要的实践之一它解决的核心矛盾是构建过程需要完整的工具链而运行环境只需要最终产物两者需求完全不同。拿一个 Go 程序举例。构建需要 go cli、编译器等一大堆工具而运行只需要一个可执行的二进制文件。写两个阶段# 第一阶段编译 FROM golang:1.21 AS builder WORKDIR /src COPY . . RUN CGO_ENABLED0 go build -o app . # 第二阶段运行 FROM alpine:3.19 COPY --frombuilder /src/app /usr/local/bin/app ENTRYPOINT [/usr/local/bin/app]第二阶段最终生成的镜像里只有 binary 和 alpine 的运行时文件golang 工具链一点都没带进来。镜像体积可以从几百 MB 降到几十 MB而且攻击面大幅缩小。前端项目也同理第一阶段用 node 镜像执行 npm build第二阶段用 nginx 镜像跑静态文件。写多阶段构建时给第一阶段起一个有意义的别名比如AS builder后面 COPY 时一眼就能认出来。4.3 构建上下文与 .dockerignore执行docker build .时那个.是构建上下文。docker 会把整个上下文目录打包发送给 daemon。如果你的项目目录里有.git、node_modules、构建缓存目录它们都会被原封不动地传到 daemon构建速度被拖慢不说还可能把不该暴露的文件烧进镜像里。解决办法是写.dockerignore语法和.gitignore类似.git node_modules *.log build dist README.md我项目里的 .dockerignore 通常跟 .gitignore 几乎一样甚至更严格。这个文件本身不会被包含在上下文里但它决定了哪些文件能进入构建上下文。写下它之后docker build会自动跳过列表中的路径构建速度和安全性都能提升。4.4 构建缓存利用与 BuildKit 实践Docker 构建是有缓存的只要指令没变对应层的结果就会复用。这带来一个执行准则把变动频繁的 COPY 放在 Dockerfile 后面把相对不容易变化的基础依赖安装放在前面。比如先 COPY 依赖清单文件再装依赖等代码文件后 COPY这样只改代码时依赖层全走缓存构建速度快很多。如果构建依赖了外部最新状态又不想被缓存误导可以显式加参数docker build --no-cache .另外建议直接开启 BuildKitDOCKER_BUILDKIT1 docker build .BuildKit 是新一代构建引擎构建并发度更高还支持COPY --from从远程仓库取文件、支持更精细的缓存挂载等特性。现在的 Docker 默认就是 BuildKit如果你用的版本还需要手动开尽早切过去。构建日志的展示格式也跟传统模式不同一开始可能不习惯但熟悉后排查问题更快。4.5 完整构建示例结合上面的原则给一个实际项目场景构建一个带健康检查的简单 Web 服务镜像。FROM golang:1.21 AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED0 go build -o /server . FROM alpine:3.19 RUN apk add --no-cache ca-certificates addgroup -S app adduser -S app -G app WORKDIR /data COPY --frombuilder /server /usr/local/bin/server USER app EXPOSE 8080 HEALTHCHECK --interval30s --timeout3s --retries3 CMD wget -qO- http://127.0.0.1:8080/health ENTRYPOINT [server]这里有几个细节值得注意第一阶段把依赖文件单独 COPY 是充分利用缓存的写法第二阶段加了 ca-certificates因为 Alpine 默认没有 CA 证书业务代码里任何 HTTPS 出站调用都会失败HEALTHCHECK 让编排层面能感知容器健康状态。构建后运行docker build -t my-web-server:1.0 . docker run -d -p 8080:8080 my-web-server:1.0到这里一条完整的从源码到运行容器的链路就走通了。5. 常见问题与排查实录5.1 拉取镜像超时或速度极慢症状docker pull卡在 Pulling fs layer或者长时间不动。先用docker info确认加速配置是否生效。没生效就检查 daemon.json 格式然后重启 Docker。生效但还慢多半是加速地址自身质量问题换一个响应更快的。还有一种情况是系统 DNS 解析问题容器拉镜像时会查询 registry 域名/etc/resolv.conf里的 DNS 配置不对解析就卡住。检查cat /etc/resolv.conf ping registry-1.docker.io能 ping 通但拉取慢是网络链路问题ping 不通或解析失败优先修 DNS。别一开始就怀疑配置先把网络链路摸清楚。5.2 容器启动失败端口映射访问不到容器起来了但宿主机访问http://宿主机IP:端口不通。先看容器状态和端口映射docker ps docker port 容器名 ss -lntp | grep 端口如果端口没有监听说明映射没生效监听了但访问不通可能是防火墙拦截。openEuler 上 firewalld 默认规则如果没放行 Docker 网段从外部访问会被拦掉。放行方式firewall-cmd --permanent --add-port8080/tcp firewall-cmd --reload另外Docker 默认会操作 iptables 规则来打通容器网络如果你机器上用的是 nftables 后端某些旧版本 Docker 可能跟系统网络栈冲突。遇到这种情况检查dmesg里有没有 nftables 相关报错。把容器网桥和映射规则梳理清楚后这类问题不难解。5.3 目录挂载报 Permission Denied症状docker run -v /host/data:/container/data后容器内读写挂载目录提示权限不足。在 openEuler 上第一个要怀疑的就是 SELinux。验证方法setenforce 0 docker run --rm -v /host/data:/container/data 镜像 ls -l /container/data临时关闭 SELinux 后问题消失说明确实是 SELinux 策略拦截。恢复 enforcing然后给挂载卷加上合适的标签docker run -v /host/data:/container/data:z 镜像 ...:z后缀会让 Docker 自动为挂载目录设置可被容器访问的 SELinux 标签。比起直接关闭 SELinux这是两个系统能共存的平衡点。如果多个容器共享同一目录用更细粒度的:Z时要小心它会把目录所有权改到容器名下可能导致宿主机进程无法访问。5.4 日志文件无限膨胀与磁盘打满容器不断输出日志/var/lib/docker/containers下的 json 日志文件会无限增长最终把磁盘塞满。这几乎是每个容器环境都会遇到的经典问题。最有效的解法是在 daemon.json 里加上日志限制{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }配置后新创建的容器会按 10MB 一个文件、最多保留 3 个文件的策略轮转日志。老容器不受影响所以建议尽早配置。已经出现的巨大日志文件清空或删除时注意别用rm直接删文件可能仍被容器持有句柄空间不会释放。正确做法是truncate -s 0 文件路径或者重启容器后再删。5.5 排查思路速查表症状可能原因排查与处理拉镜像超时加速未生效 / DNS 异常 / 链路慢docker info 检查加速修 resolv.conf换加速地址容器启动后端口不通防火墙拦截 / 端口未映射ss -lntp 看监听firewall-cmd 放行端口挂载目录无权限SELinux 拦截setenforce 0 验证挂载加 :z 标签磁盘空间告警镜像、容器层、日志堆积docker system df 定位prune 清理限日志大小Storage Driver 为 vfs内核模块未加载确认 overlay 模块必要时 modprobe overlay这张表是我在维护容器环境时最高频遇到的问题集合。每个问题背后都是具体机制在起作用网络、SELinux、存储驱动理解了机制排查就不会慌。写在最后的小经验整套流程走下来我最深的体会是openEuler 上跑 Docker 并没有想象中那么多障碍真正需要上心的是 SELinux 和包管理器的差异这两件事。很多网上搜到的安装教程默认是 Ubuntu 的apt系命令搬过来直接跑必然失败所以我在动手前会先确认系统发行版标识所有命令都基于 dnf 体系重写一遍。最后再分享一个执行细节每次改完 daemon.json 或者安装完新包我都会执行systemctl daemon-reload再重启 Docker这两个动作顺序反了有时会读到旧的配置导致重启后不生效。还有养成给镜像打明确版本 tag 的习惯能让环境复现性和回滚操作都省心很多。容器这条路规范和好习惯比硬核技巧更值钱。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑