新闻详情 资讯动态

全面了解最新资讯与建站知识,洞察行业趋势。

行业资讯

openEuler 上 Docker 安装、镜像管理与 Dockerfile 构建实践

发布时间:2026/10/16 13:11:11
openEuler 上 Docker 安装、镜像管理与 Dockerfile 构建实践 前阵子给一台 openEuler 22.03 的服务器装 Docker本来以为一两条命令就能搞定结果照着网上“通用教程”复制粘贴光安装就折腾了一晚上。原因很简单openEuler 虽然是 RPM 体系但它不走 CentOS 那套软件源很多企业环境还要求离线部署和干净卸载这就让镜像管理、Dockerfile 构建这些后续操作变得格外讲究。这篇笔记就把我实际摸过的 Docker 安装、镜像管理和 Dockerfile 构建三条主线完整串起来手把手带你在这个系统上把容器环境真正跑起来。不管你之前用没用过 Docker只要你手上有一台 openEuler 机器不管是虚拟机还是物理机这篇内容都适合你。我会尽量把命令、参数、配置文件都写到可以直接照抄的程度同时会解释每一步为什么这么干让你改环境的时候也知道怎么调整。1. 为什么在 openEuler 上跑 Docker要先想清楚这些1.1 openEuler 和主流发行版到底差在哪openEuler 说白了是一个面向服务器场景的 Linux 发行版它底层的包管理用的是 RPM 那一套所以你看到yum、dnf、rpm会觉得亲切。但真正动手装软件时会发现它的软件仓库、包名编排和 CentOS 不一样。比如 CentOS 上可以直接装docker-ceopenEuler 官方仓库里带的是docker-engine两者功能差不多但名字和依赖细节有差异。另外 openEuler 对 SELinux 的默认策略也比较严格容器跑起来之后如果遇到权限类报错十有八九和安全上下文有关。刚上手的人容易忽略这一点所以这里先说清楚在 openEuler 上折腾 Docker不是“换个 yum 源”这么简单而是要在理解它仓库体系的前提下把安装、存储、网络、systemd 集成这四块都对齐。1.2 这套实战笔记能帮你解决哪些问题我归纳了一下大多数人在 openEuler 上用 Docker 摔跟头无非集中在三个点。第一是安装环节官方仓库里的 docker 包到底叫啥、装完怎么用 systemd 拉起来、要不要配 docker 组权限很多人卡在这里。第二是镜像管理环节镜像拉下来放哪、怎么给镜像打标签、离线环境怎么导出导入、怎么自建一个私有仓库这些属于日常高频操作但细节很多。第三是 Dockerfile 构建环节基础镜像用哪个、每条指令怎么编排才能利用缓存、多阶段构建怎么减小体积这些直接决定你构建出来的镜像能不能上生产。这篇笔记会把这三块挨个拆开每一块都附上我在 openEuler 上实际跑通的命令和配置你照着走基本不会踩坑。2. 安装前检查与方案选型2.1 系统版本、内核和磁盘状态确认不管多简单的安装先确认系统底子是个好习惯。在 openEuler 上打开终端依次执行下面几条命令花两分钟把环境摸清楚cat /etc/os-release uname -r df -h /var getenforce第一行看系统版本确认是 20.03 LTS、22.03 LTS 还是更新版本不同版本仓库里的包会有细微差别。第二行看内核Docker 的 overlay2 存储驱动对内核版本有要求openEuler 默认内核通常是 5.10 或更高问题不大但如果是旧内核建议先升级再装 Docker。第三行看磁盘剩余空间Docker 默认把镜像和容器数据放在/var/lib/docker下如果/var所在分区小于 20G后面拉几个镜像就可能撑爆建议提前挂在独立数据盘或者后面通过>yum makecache然后搜索一下仓库里有没有 docker 相关的包这一步能确认你自己的软件源是否正常也能搞清楚准确的包名yum search docker在 openEuler 22.03 官方仓库里通常能看到docker-engine或docker这个包。直接把docker-engine拉下来安装yum install -y docker-engine安装过程会拉下来一串依赖比如containerd、runc等这些是 Docker 运行容器必需的底层组件看到它们一起装是正常的。装完后执行which docker或者docker version验证一下二进制文件是否存在。这里有个小细节openEuler 仓库里装的 Docker 版本可能不是最新的但胜在稳定和系统库兼容。如果你一定要用最新版才需要考虑二进制安装路线。我个人更推荐先用仓库版跑通整个链路再考虑升级。3.2 启动服务、配置权限和自启安装完成后Docker 不会自动启动你需要用 systemd 把它拉起来并且设置开机自启systemctl enable --now docker systemctl status docker看到active (running)就说明服务起来了。如果你不信可以再跑一个最小的验证命令docker run hello-world这个命令会让 Docker 从镜像仓库拉取一个hello-world测试镜像并运行它如果输出一段带 “Hello from Docker!” 的说明文字说明整套环境已经能正常工作了。我建议你顺手把当前用户加进docker组这样以后执行docker命令时不需要每次都加sudogroupadd docker usermod -aG docker 你的用户名 newgrp docker注意usermod之后当前登录的 shell 不会立刻生效执行newgrp docker或者干脆重新登录一次。如果忘了这一步后面执行 Docker 命令时遇到 “permission denied while trying to connect to the Docker daemon socket” 这种报错不要慌先看看自己是不是不在 docker 组里。3.3 用 daemon.json 统一管理镜像加速与日志Docker 的核心配置都集中在/etc/docker/daemon.json里这个文件默认不存在需要自己创建。我通常在安装完成后的第一件事就是把整个文件写好避免后期改一处就重启一次服务。下面这份配置是我在 openEuler 上的常用模板{ data-root: /var/lib/docker, log-driver: json-file, log-opts: { max-size: 50m, max-file: 5 }, registry-mirrors: [ https://你的镜像加速地址.example ], storage-driver: overlay2 }这里几个参数逐个解释。>systemctl restart docker重启完可以执行docker info看看关键信息比如Storage Driver是不是显示overlay2日志驱动是否正确。注意daemon.json里的修改只对之后创建的容器生效已经有数据的老容器如果不重建日志参数不会变这一点排查问题的时候经常被忽略。4. 镜像管理每天都会用到的 8 个操作4.1 先搞懂镜像和容器的分层关系镜像管理听起来高大上其实可以把它理解成“软件安装包”的升级版。普通的安装包装完就完事了Docker 镜像则是一层层只读文件叠起来的每一层对应 Dockerfile 里的一条指令。容器运行的时候Docker 在最上面加一层可写层你的写入操作都发生在这层。镜像层不变所以多个容器可以共享同一个镜像底下的层不重复占空间。这个设计意味着什么呢意味着你删除镜像时如果多个镜像共享某些底层Docker 只会删除引用计数为零的层。也意味着你拉取一个新镜像的某几层如果本地已经存在Docker 会直接复用这就是“增量拉取”的原理。在 openEuler 上镜像是放在/var/lib/docker/overlay2目录下的除非你把>docker pull nginx:1.27nginx是镜像名称1.27是标签。如果不写标签Docker 会默认拉取latest标签。这里建议每次显式写明版本号尤其是生产环境因为latest会随上游更新而变化今天拉的和明天拉的可能是两个东西。查看本地镜像docker images docker image ls两条命令效果相同输出镜像名、标签、镜像 ID、创建时间和大小。如果加了-a参数会把中间层镜像也显示出来平时不用加。想要了解一个镜像的构建历史可以用docker history nginx:1.27能看到它每一层执行的指令这也是判断镜像体积来源的好方法。删除镜像docker rmi nginx:1.27如果这个镜像还有正在运行的容器引用删除会报错。这时候你可以加-f强制删除但我一般不建议因为-f可能会让运行中的容器出现奇怪行为。正确做法是先把相关容器停掉删掉再删镜像。清理一堆无用的悬空镜像可以这样docker image prune -a这个命令会删除所有没有被容器引用的镜像执行前先看清楚哪些会被删避免误删。4.3 镜像的标签、导入导出和离线分发镜像的全名格式是仓库地址/命名空间/镜像名:标签docker tag这个命令就是为了给同一个镜像打上不同的“门牌号”。比如我们想把本地openeuler/openeuler:22.03-lts这个基础镜像改个便于辨识的名字docker tag openeuler/openeuler:22.03-lts mybase/openeuler:22.03这个操作只是增加一个引用标签不会产生新的镜像数据它占用的还是同一份镜像存储。打标签的目的通常是为了推送因为 Docker Hub 或者私有仓库要求镜像路径必须符合特定格式。离线环境里镜像的传输需要用到save和load两个命令。把镜像导出成一个 tar 文件docker save -o openeuler-base.tar openeuler/openeuler:22.03-lts然后把这个 tar 文件拷贝到另一台 openEuler 机器上再导入docker load -i openeuler-base.tar这里提醒一下docker save会保留镜像的完整层数据和历史信息所以导出的文件可能很大。如果你只想要最终文件系统不考虑历史可以使用docker export结合容器来做但那样导出的就不叫镜像了而是容器文件系统快照导入回来不能直接保留原有命令配置不要搞混。4.4 裸跑一个私有镜像仓库镜像分发还有一个更常用的方式自建私有仓库。在 openEuler 上搭一个 minimal 的镜像仓库其实相当简单Docker 官方提供了一个叫registry的镜像跑起来就是一个仓库服务端。先拉镜像再启动容器docker pull registry:2 docker run -d --name my-registry \ -p 5000:5000 \ --restartalways \ -v /opt/registry-data:/var/lib/registry \ registry:2上面这个命令把仓库的存储目录挂载到了宿主机的/opt/registry-data这样仓库容器即使被删掉重建里面的镜像数据也不会丢。5000端口就是仓库对外开放的端口。使用私有仓库时镜像名的格式必须是宿主机IP:5000/镜像名:标签否则 Docker 不认。先给一个镜像打上私有仓库的完整标签docker tag openeuler/openeuler:22.03-lts 192.168.1.10:5000/openeuler-base:22.03推送到私有仓库docker push 192.168.1.10:5000/openeuler-base:22.03第一次推送时Docker 默认是不信任非 HTTPS 的仓库的如果仓库地址是裸 IP 且没有配 HTTPS需要修改/etc/docker/daemon.json添加{ insecure-registries: [192.168.1.10:5000] }然后重启 Docker。推完之后你可以用docker pull 192.168.1.10:5000/openeuler-base:22.03在任何一台配置了相同 insecure 地址的机器上拉取这个镜像实现内网分发。5. Dockerfile 构建实战从基础指令到多阶段优化5.1 把 Dockerfile 常用的指令一次讲清楚安装 Docker 和管理镜像只是热身真正体现功夫的是写 Dockerfile。Dockerfile 是描述“镜像怎么构建”的配方文件每一条指令会生成镜像的一个新层。我先把最常用的指令串一遍用一个小案例说明。假设我们要构建一个基于 openEuler 的 Nginx 镜像目录里有一个index.html文件。Dockerfile 这样写# 指定基础镜像必须是第一行有效指令 FROM openeuler/openeuler:22.03-lts # 维护者信息现在更推荐用 LABEL 代替 LABEL maintaineropeneulerexample.com # 环境变量 ENV NGINX_VERSION1.24 # 构建时执行 shell 命令 RUN yum install -y nginx yum clean all # 把宿主机的文件拷贝进镜像 COPY index.html /usr/share/nginx/html/ # 声明容器对外暴露的端口主要起说明作用 EXPOSE 80 # 指定容器启动时执行的默认命令 CMD [/usr/sbin/nginx, -g, daemon off;]这条 Dockerfile 里几个重点说明一下。FROM指定基础镜像所有操作都基于这个镜像叠加。RUN用来在构建期间执行命令注意这里的命令是在镜像内部的临时容器里执行的和宿主机无关。COPY是把构建上下文中的文件复制进镜像它和ADD的区别在于ADD还支持自动解压 tar 包和拉取远程 URL但实际项目里我一般只用COPY因为行为更可预期。CMD和ENTRYPOINT是新手最容易混淆的。简单理解CMD是容器启动时的默认命令参数它会被docker run后面的参数覆盖ENTRYPOINT是固定入口不会被覆盖。如果两个都用CMD的内容会作为参数传给ENTRYPOINT。一般场景下用CMD就可以了需要固定进程入口时才组合用。构建这个镜像的命令是docker build -t my-nginx:1.0 .别小看最后这个点它代表“构建上下文”告诉 Docker 把当前目录作为打包给构建进程的内容。构建时Docker 会把上下文里面的所有文件都传输给 Docker daemon所以尺寸大的目录、临时文件都应该排除这就是5.2的内容。5.2 构建上下文和 .dockerignore 的坑构建上下文是个非常容易被忽略的坑。很多人以为docker build .里的.只是指定 Dockerfile 路径其实它是在告诉 Docker把当前目录整个打包发送给 Docker 服务端再在服务端完成构建。这意味着什么呢如果你的目录里有几个 GB 的视频文件或者node_modules即使 Dockerfile 里根本没有用到它们这些文件也会被打包传输构建速度慢到离谱还会产生环境敏感信息泄露的风险。解决办法是写.dockerignore文件作用类似.gitignore.git node_modules build *.log *.tar构建时 Docker 会自动读取.dockerignore把这些文件排除在上下文之外。我建议从一开始建项目就顺手加上这个文件不然以后目录一复杂构建性能下降到一个层次你都不知道是哪来的。还有一点构建上下文只包含当前目录Dockerfile 里的COPY ../something这种写法是不被允许的因为构建上下文之外的文件 Docker 根本看不到。遇到这种需求要把需要拷贝的文件放进当前目录再构建。5.3 多阶段构建让镜像体积小一半我之前给一个 Go 服务写 Dockerfile第一版镜像打完 900 多 MB后来用多阶段构建压缩到 40 多 MB效果非常夸张。多阶段构建的核心理念是用一套完整的构建环境完成编译再把编译产物复制到一个干净的运行环境镜像里中间产生的临时文件全都丢掉。看一个实际例子。项目目录下有一个main.gopackage main import ( fmt net/http ) func main() { http.HandleFunc(/, func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, Hello, openEuler) }) http.ListenAndServe(:8080, nil) }Dockerfile 这样写# 第一阶段编译环境 FROM golang:1.21 AS build WORKDIR /src COPY main.go . RUN CGO_ENABLED0 go build -o myapp . # 第二阶段运行环境 FROM openeuler/openeuler:22.03-lts WORKDIR /opt COPY --frombuild /src/myapp . EXPOSE 8080 ENTRYPOINT [./myapp]关键在COPY --frombuild /src/myapp .这一句它直接从前面的编译阶段镜像里把二进制拷贝过来不需要经过宿主机中转。最终生成的运行镜像只包含一个静态二进制文件加一个精简的 openEuler 基础系统体积自然小很多。这里有一个细节值得说Go 程序我加了CGO_ENABLED0来编译成静态二进制这样运行阶段镜像里就不用装 glibc 依赖了。如果用 CGO 编译动态链接的二进制放到精简镜像里会报not found之类的问题排查起来很麻烦。非 Go 语言项目也一样比如 Java 可以用多阶段先编译出 jar 再拷到 JRE 基础镜像里Node 可以先把依赖安装完再做npm prune。5.4 openEuler 基础镜像的构建优化技巧针对 openEuler 上的 Dockerfile 构建我额外总结了几个优化点。第一善用镜像层缓存。Docker 构建时会缓存每一步结果只要 Dockerfile 中的指令和上下文文件没变缓存就能命中。基于这个原理应该把变化频率最低的指令放在前面。常见的做法是把yum install这类安装依赖的指令放在COPY代码之前。如果先把代码复制进去再安装依赖那么每次修改代码都会导致依赖安装步骤重新执行白白浪费几分钟。第二安装完软件包记得清理缓存。openEuler 的yum在安装时会下载元数据和 rpm 包到本地不清理的话这些内容会进镜像层拉大体积。所以在 Dockerfile 的RUN里一条命令完成安装和清理比如RUN yum install -y nginx \ yum clean all \ rm -rf /var/cache/yum第三基础镜像标签要写具体版本不要写latest。比如只写FROM openeuler/openeuler:latest今天构建出来的镜像和半年后构建的镜像可能底层不一致出了问题很难复现。用FROM openeuler/openeuler:22.03-lts这种带明确版本的标签可追溯性才有保障。第四生产环境尽量启用--no-cachefalse之前先想清楚。默认的缓存机制在本地操作很舒服但在构建机或者 CI 流水线里旧缓存可能让镜像产生不符合预期的历史遗留需要主动用docker build --no-cache强制重新构建时直接加参数即可。6. 常见问题速查与排查思路6.1 启动失败和权限类问题我见过最多的是这个报错Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock这就是用户不在docker组里或者 Docker 服务没有启动。顺序排查先systemctl status docker看服务状态再用id 你的用户名看用户是否在 docker 组。服务没启动就启动它用户不在组里就用usermod -aG docker 你的用户名加组然后重新登录。另一个常见报错是Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?基本就是 docker 服务没起来。用journalctl -u docker -n 50看日志常见原因是daemon.json配置错误比如 JSON 格式不对、路径没有权限等。6.2 磁盘、存储驱动和日志增长容器跑久了磁盘满了这是生产环境最常见的事故之一。先定位是谁占了空间du -sh /var/lib/docker/*如果发现containers目录异常大多半是容器日志没设轮转。修改daemon.json加上日志大小限制后对存量容器要重建才生效这个我前面强调过。如果发现overlay2目录特别大多半是镜像和容器层堆积太多用docker system df查看空间占用情况再用docker system prune清理悬空资源。偶尔会遇到 overlay2 存储驱动初始化失败的情况。openEuler 默认内核一般能支持 overlay2但如果报错说内核模块不支持检查一下内核是否加载了overlay模块modprobe overlay如果你用的是老内核又不方便升级临时方案是把存储驱动改成vfs但vfs性能和存储利用率都比较差只适合应急。6.3 容器网络和 DNS 问题容器里访问外网失败但宿主机没问题常见的坑是 DNS 配置。容器默认从宿主机继承 DNS 配置如果宿主机的systemd-resolved状态不对容器里解析域名就会失败。快速验证docker run --rm --dns 你本地可用的 DNS 地址 你的镜像 ping 某个域名如果指定 DNS 后可以解析说明默认 DNS 有问题。可以在daemon.json里增加{ dns: [你本地的 DNS 地址] }然后重启 Docker让所有容器默认使用这个 DNS。另外如果容器需要和宿主机网络互通用--network host可以让容器直接用宿主机网络单容器场景非常好用但注意端口占用冲突问题。6.4 systemd 集成和开机自启的边界情况openEuler 上 Docker 服务是通过 systemd 管理的开机自启用systemctl enable docker就能配置好。但如果你的机器配置了私有仓库依赖的存储盘是外挂的比如网络文件系统systemd 启动顺序可能导致 Docker 先于挂载完成启动结果数据目录不可用。处理办法是写一个 systemd 的RequiresMountsFor/opt/registry-data配置或者把挂载做成开机自动挂载保证目录就绪再启动服务。还有一种场景是机器上同时装了多个容器运行环境比如既有 docker 又有其他容器引擎它们的 systemd 服务文件可能会互相覆盖。遇到docker.service被占用或者服务文件路径被篡改的情况先用systemctl cat docker查看当前生效的服务文件内容确认 ExecStart 指向的路径是否正确再用systemctl daemon-reload重新加载。7. 我踩过坑之后留下的几点实操建议最后分享几条实际体会不算什么高深理论但都是真金白银换来的经验。第一个建议是别急着在生产环境装最新版。openEuler 仓库里的 docker-engine 虽然版本老但它是和系统整体兼容过的你先用它把业务逻辑跑通再把“用新版”作为一个单独的目标去推进不要把两个变量一起引入。第二个建议是每天看一眼docker system df和磁盘使用率很多故障都不是突然发生的而是在日志和镜像堆积中缓慢逼近临界点。第三个建议是凡是改动/etc/docker/daemon.json之后一定记得备份原文件确认 Docker 服务能正常启动后再继续避免手滑把配置写坏导致整套环境瘫痪。容器这条路一旦跑通第一个镜像后面就是正反馈了。希望这篇基于 openEuler 的实战笔记能帮你少走一点弯路把时间花在真正有业务价值的事上。

想做一个「会获客」的企业网站?

留下需求,1 小时内获取专属建站方案与透明报价。

免费咨询方案
↑