尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Ubuntu/Debian DEB包制作全流程:从Python脚本到C++项目的实战指南

Ubuntu/Debian DEB包制作全流程:从Python脚本到C++项目的实战指南 1. 项目概述为什么我们需要自己制作DEB包在Ubuntu或者Debian系Linux的日常使用中dpkg -i安装一个现成的.deb文件是再平常不过的操作。但当你从源码编译了一个好用的工具或者开发了一个小脚本希望分发给团队又或者需要对某个软件进行定制化修改时你就会发现直接扔一个二进制文件或者脚本给别人远不如提供一个可以一键安装、自动处理依赖、并能被系统包管理器干净卸载的DEB包来得专业和方便。我自己就经常遇到这种场景在服务器上调试好了一个监控脚本需要部署到几十台机器上或者为内部团队封装了一个简化的工作流工具。最初我都是写个安装脚本手动拷贝文件、设置权限。直到有一次因为一台机器的环境差异脚本跑飞了花了大半天才定位到是某个依赖库版本不对。从那以后我下定决心凡是需要重复部署或分发的软件一律打成DEB包。这不仅仅是“规范”问题更是提升效率、减少运维事故的实战需求。制作DEB包听起来像是发行版维护者才需要掌握的“高级技能”但其实它的核心逻辑非常直观把你的软件文件按照FHS文件系统层次结构标准放到该放的位置然后告诉包管理系统这些文件的元信息比如叫什么、版本多少、依赖什么以及安装前后需要做什么。dpkg和apt这些工具会帮你处理好剩下的繁琐工作。本文将基于Ubuntu 20.04 LTS这个依然广泛使用的稳定版本带你走通从零制作一个标准DEB包的全流程并重点分享那些官方文档里不会写、但一踩就痛的“雷区”。2. 打包环境准备与核心工具解析工欲善其事必先利其器。打包DEB不需要复杂的IDE但需要一套正确的工具链和对基本概念的理解。2.1 必备工具安装与验证在Ubuntu 20.04上打包所需的核心工具可以通过以下命令一键安装sudo apt update sudo apt install build-essential dh-make debhelper devscripts lintian我们来拆解一下这几个包的作用build-essential提供了gcc,g,make等基础的编译工具链。即使你的软件本身不需要编译比如纯脚本某些打包辅助工具也可能依赖它。dh-make这是我们的“脚手架”生成器。它可以帮助我们快速创建一个符合Debian政策Debian Policy的打包目录模板对于新手来说能避免很多初始目录结构的错误。debhelper这是打包过程的“灵魂工具”。它提供了一系列以dh_开头的命令如dh_auto_configure,dh_auto_build,dh_auto_install用于自动化执行编译、安装到临时目录、生成配置文件等繁琐步骤。我们后面编写的rules文件核心就是调用这些debhelper指令。devscripts包含了许多对打包开发者有用的脚本例如debuild用于在干净环境中构建包、dch编辑变更日志等。lintian“包医生”。用于在构建完成后检查你的DEB包是否符合Debian/Ubuntu的政策和最佳实践能指出许多潜在问题从严重的错误到风格警告都有。安装完成后可以通过dpkg -l | grep -E \(debhelper|dh-make)\来确认工具是否就绪。2.2 理解DEB包的核心结构一个DEB包本质上是一个ar格式的归档文件。你可以用ar x your-package.deb来解压它通常会得到三个文件debian-binary一个纯文本文件内容就是2.0\n表示包格式版本。control.tar.gz包的“大脑”。包含所有元数据如包名、版本、依赖关系、维护者信息等。data.tar.gz包的“身体”。包含所有要安装到目标系统上的实际文件并按最终安装路径组织。而我们打包者的工作就是在源代码目录下创建一个名为debian/的子目录并在其中编写一系列控制文件最终由工具链生成上述的control.tar.gz和data.tar.gz。一个标准的debian/目录通常包含以下关键文件control包的“身份证”和“说明书”定义最核心的元数据。rules打包的“Makefile”定义了如何编译、安装和清理的步骤是打包逻辑的核心。changelog包的变更日志格式严格dpkg会据此管理版本。copyright版权信息文件。compat指定debhelper的兼容性级别。source/format定义源码包的格式。其他可能有的preinst,postinst,prerm,postrm等维护者脚本用于在安装/卸载前后执行特定操作。3. 从零开始打包一个简单Python脚本的完整流程理论说得再多不如动手做一遍。我们以一个最简单的“Hello World” Python脚本为例将其打包成DEB。假设我们的项目叫myhello脚本内容如下/usr/local/bin/myhello#!/usr/bin/env python3 print(Hello from my custom DEB package!)3.1 创建项目结构与初始化首先为你的“软件”创建一个干净的目录并放入源码这里就是我们的脚本mkdir -p ~/myhello-1.0 cd ~/myhello-1.0 cat myhello \EOF #!/usr/bin/env python3 print(Hello from my custom DEB package!) EOF chmod x myhello现在使用dh_make来生成debian/模板目录dh_make --createorig -s -y -p myhello_1.0参数解析--createorig自动创建上游源码的tar包myhello_1.0.orig.tar.gz这是Debian打包规范所要求的。-s表示这是一个“单一二进制包”single binary即最终只生成一个.deb文件。如果你的软件包含多个独立组件如服务端和客户端可能需要用-i多个二进制包。-y对所有提示自动回答“yes”省去交互。-p myhello_1.0指定包名和版本。格式必须是package_version。执行后当前目录下会多出一个debian/目录里面充满了以.ex,.EX为后缀的示例文件。对于我们的简单项目可以删除这些示例cd debian rm *.ex *.EX # 删除所有示例文件 cd ..现在我们只保留最核心的几个文件进行编辑。3.2 编写核心控制文件control, rules, changelog3.2.1 编辑debian/control这是最重要的文件。打开debian/control内容大概如下我们需要修改它Source: myhello Section: unknown Priority: optional Maintainer: Your Name your.emailexample.com Build-Depends: debhelper-compat ( 13) Standards-Version: 4.6.0 Package: myhello Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, python3 Description: A simple hello world package This is a demonstration package for DEB building. It prints a friendly greeting.Source源码包名称通常与目录名或项目名一致。Section软件所属分类。可以从admin,devel,misc,python,utils等中选择。我们填utils。Priority优先级一般软件填optional。Maintainer维护者信息。务必填写有效的邮箱这是包的身份标识。Build-Depends构建依赖。debhelper-compat ( 13)表示使用debhelper第13兼容模式它会自动处理很多流程。如果软件编译需要gcc或libxxx-dev也要加在这里。Standards-Version所遵循的打包标准版本保持默认即可。Package二进制包名称即最终安装的包名。Architecture架构。any表示需要编译或与架构相关all表示纯脚本、文档或架构无关的数据包。我们的脚本是Python属于all。Depends运行时依赖。${shlibs:Depends}和${misc:Depends}是dh_shlibdeps和dh_installdeb自动生成的依赖我们保留。因为我们用Python3所以必须明确加上python3。这是第一个雷区忘记声明运行时依赖导致包在其他机器上安装后无法运行。Description描述。第一行是简短描述后续行是长描述每行前必须有一个空格。修改后的关键部分Architecture: all Depends: ${shlibs:Depends}, ${misc:Depends}, python3 ( 3.6)3.2.2 编辑debian/rulesrules文件本质上是一个Makefiledpkg-buildpackage会调用它。得益于debhelper对于简单项目它可以非常简短#!/usr/bin/make -f %: dh $这被称为“dh风格”的rules文件意思是所有步骤都委托给debhelper (dh)命令自动处理。它会按照标准的顺序dh_auto_configure,dh_auto_build,dh_auto_install,dh_install...执行。对于我们的脚本这足够了。确保文件有可执行权限chmod x debian/rules。3.2.3 编辑debian/changelog这个文件格式严格用于版本管理。我们可以用dch工具来编辑dch -v 1.0-1 Initial release. Closes: #XXXXXX或者手动编辑内容类似myhello (1.0-1) focal; urgencymedium * Initial release. * Packaged the myhello script. -- Your Name your.emailexample.com Mon, 01 Jan 2024 12:00:00 08001.0-11.0是上游版本-1是Debian修订版本。每次修改打包通常递增修订号。focal目标发行版代号Ubuntu 20.04是focal。urgency表示紧急程度。底部行必须以--空格-横线-空格开头包含维护者信息和RFC2822格式的日期。3.2.4 创建debian/install文件关键步骤debhelper的dh_install命令需要一个指引来知道将哪些文件安装到包的什么位置。我们创建一个debian/install文件myhello usr/local/bin这行表示将构建目录根下的myhello文件安装到二进制包的/usr/local/bin/目录下。注意路径是相对于最终根文件系统的。3.3 执行构建与问题排查现在回到项目根目录~/myhello-1.0执行构建命令。强烈建议使用debuild因为它会在一个更干净的环境通过pbuilder或sbuild模拟中构建并能自动调用lintian检查cd ~/myhello-1.0 debuild -us -uc参数说明-us不对源码包进行GPG签名。-uc不对.changes文件进行GPG签名。对于本地测试和内部发布跳过签名是方便的。如果一切顺利你会在上层目录~/看到生成的一系列文件其中最重要的就是myhello_1.0-1_all.deb。构建过程常见雷区与排查错误dpkg-source: error: can\t build with source format \3.0 (quilt)\: no upstream tarball found原因debian/source/format文件指定了3.0 (quilt)格式但dh_make创建的.orig.tar.gz名字不对或不存在。解决检查debian/source/format对于简单本地包可以将其内容改为1.0。或者确保myhello_1.0.orig.tar.gz存在于上层目录。dh_make --createorig应该已经创建了它。错误dh: command not found或debhelper compatibility level错误原因debian/compat文件指定的兼容级别与安装的debhelper版本不匹配或者rules文件第一行#!/usr/bin/make -f路径错误。解决检查debian/compat文件内容通常就是一个数字如13。确保已安装对应版本的debhelper。rules文件第一行必须正确。警告non-standard-dir-perm或dir-or-file-in-usr-local原因lintian检查发出的策略警告。将文件安装到/usr/local/bin在某些严格策略下不被鼓励因为/usr/local是系统管理员本地安装软件的领域而非包管理器。解决对于内部工具或确实需要放入/usr/local的软件可以忽略此警告。如果希望更规范可以考虑安装到/usr/bin或/opt下。这是一个策略选择问题而非错误。3.4 安装测试与卸载构建成功后安装并测试你的包# 安装 sudo dpkg -i ../myhello_1.0-1_all.deb # 运行 myhello # 应输出Hello from my custom DEB package! # 检查文件是否在正确位置 dpkg -L myhello # 卸载 sudo dpkg -r myhello如果安装时报告依赖问题例如dpkg: dependency problems prevent configuration...那是因为Depends字段没写对或者你本地缺少依赖。可以用sudo apt-get install -f来修复依赖并完成安装但更重要的是回头修正control文件。4. 进阶配置与维护者脚本一个基本的包已经完成了。但真实的软件往往更复杂可能需要配置文件、服务单元systemd、安装后执行脚本等。4.1 处理配置文件conffiles如果软件有配置文件如/etc/myhello.conf并且希望用户修改后在包升级时能被提示保留本地修改还是使用新版本的配置你需要将其声明为“conffile”。在debian/install中正常安装配置文件到/etc。创建debian/conffiles文件每行列出配置文件的绝对路径/etc/myhello.conf这样dpkg在升级时会特殊处理这些文件。4.2 使用维护者脚本维护者脚本是包管理系统在特定时刻自动执行的Shell脚本。debian/preinst在解压包文件之前运行。常用于停止旧版本服务、检查条件。debian/postinst在解压包文件之后运行。最常用用于更新配置文件、运行systemctl enable、更新动态链接库缓存ldconfig、或询问用户初始配置。debian/prerm在移除包文件之前运行。常用于停止服务。debian/postrm在移除包文件之后运行。用于删除运行时生成的文件、日志或缓存。重要雷区维护者脚本的健壮性必须幂等脚本应能安全地多次运行。例如在postinst中启用服务前先检查服务是否存在。处理失败脚本应有错误检查。set -e是个好习惯但要注意在需要清理的地方使用|| true。避免交互在非桌面环境的服务器上交互式提问如debconf可能导致安装卡住。如果必须交互确保通过DEBIAN_FRONTENDnoninteractive环境变量安装时能优雅降级。示例debian/postinst中启用systemd服务#!/bin/sh set -e if [ $1 configure ]; then # 仅在配置阶段执行而不是在升级或失败后的重新配置时重复执行 if systemctl is-enabled myhello.service /dev/null 21; then echo Service myhello already enabled. else systemctl enable myhello.service /dev/null 21 || echo Warning: Could not enable myhello.service (might not be installed?) fi systemctl daemon-reload /dev/null 21 || true fi4.3 打包包含编译步骤的C/C项目对于需要./configure make make install的经典C/C项目debhelper的自动化能力更显强大。rules文件依然可以很简单#!/usr/bin/make -f %: dh $ --with autoreconf--with autoreconf会自动处理可能需要的autoreconf、./configure步骤。你只需要确保debian/control中的Build-Depends包含了所有必要的开发库如libssl-dev,libxml2-dev。dh_auto_install会默认执行make install DESTDIR$(pwd)/debian/package将文件安装到临时目录。如果项目的安装路径需要调整可以使用debian/package.install文件进行更精细的控制或者覆盖dh_auto_install目标。5. 深度排雷与最佳实践总结在多次打包和踩坑之后我总结了一些必须注意的要点和技巧。5.1 依赖声明精确与宽松的平衡明确声明所有依赖不要假设目标系统上有某个库或工具。用dpkg -s package或apt-cache depends来确认包名。对于共享库通常依赖libxxx1运行时库而非libxxx-dev开发包。版本约束Depends: python3 ( 3.6)比Depends: python3更好。但也不要过度限制除非你明确使用了高版本特性。使用等于、远大于、远小于要谨慎。虚拟包和替代项Provides和Replaces字段可以用于处理包重命名或提供相同功能的包。Pre-Depends慎用这是比Depends更强的依赖必须在解包前满足。通常只在极端情况下使用如内核模块。5.2 文件系统布局遵循FHS二进制文件用户命令放/usr/bin或/usr/local/bin后者更宽松。系统管理命令放/usr/sbin。库文件动态库放/usr/lib/x86_64-linux-gnu/或对应架构路径。静态库通常放/usr/lib/x86_64-linux-gnu/下的子目录。配置文件放/etc/package/目录下。使用conffiles声明。数据文件只读架构无关数据放/usr/share/package/。可变数据放/var/lib/package/。日志文件应由程序在/var/log/package/目录下创建包本身不应直接包含日志文件。临时文件使用/run/package/内存文件系统或/tmp/。雷区绝对不要打包到/home、/root或/boot等目录下。5.3 调试与验证工具链dpkg-deb直接操作DEB包的工具。dpkg-deb -c package.deb # 列出包内文件 dpkg-deb -I package.deb # 显示包的control信息 dpkg-deb -x package.deb extract_dir/ # 解压data部分 dpkg-deb -e package.deb extract_dir/DEBIAN # 解压control部分lintian构建后务必运行。debuild默认会跑。单独运行lintian ../myhello_1.0-1_all.deb。关注以E:开头的错误W:开头的警告根据情况决定是否处理。pbuilder或sbuild用于在纯净的chroot环境中构建包确保你没有隐式依赖本地已安装的库。这是发布前的重要一步。安装测试一定要在一个干净的虚拟机或容器如Docker中测试安装、运行、升级和卸载的全流程。验证所有维护者脚本的行为。5.4 版本管理与升级路径版本号规则上游版本-打包修订号。Ubuntu/Debian的包管理器使用这个字符串进行版本比较。遵循 Debian版本号政策 。升级测试测试从旧版本升级到新版本时配置文件conffile的处理是否符合预期服务是否正常重启数据迁移脚本如果有是否工作。Breaks和Conflicts如果你的新版本与系统中其他特定版本的软件不兼容使用Breaks:字段。如果完全不能共存使用Conflicts:。这会强制apt在安装你的包时处理冲突。5.5 一个完整的内部工具打包示例思路假设你要打包一个内部监控代理包含一个Go编写的二进制文件monitor-agent。一个配置文件/etc/monitor-agent/config.yaml。一个systemd服务文件monitor-agent.service。一个日志轮转配置/etc/logrotate.d/monitor-agent。打包目录结构规划monitor-agent-1.2.0/ ├── Makefile # 上游构建脚本目标build, install ├── monitor-agent.go ├── config.yaml.example └── debian/ ├── control # Depends: adduser, logrotate ├── rules # %: dh $ --with systemd,golang ├── changelog ├── install # monitor-agent usr/bin/ │ # config.yaml.example etc/monitor-agent/ │ # monitor-agent.service lib/systemd/system/ │ # logrotate-config etc/logrotate.d/monitor-agent ├── postinst # 添加运行用户、启用服务、询问初始配置 ├── prerm # 停止服务 ├── postrm # 删除用户可选 └── conffiles # /etc/monitor-agent/config.yaml在这个例子中--with systemd告诉debhelper我们使用了systemd集成它会自动处理服务单元的启用和重载。--with golang则提供了对Go项目构建的额外支持。打包DEB的过程本质上是一个将软件部署过程标准化、自动化的过程。一开始可能会觉得步骤繁琐但一旦形成模板和习惯它会成为你分发软件最可靠、最专业的方式。尤其是在需要持续集成/持续部署CI/CD的流水线中一个能够自动构建的debian/目录是无价之宝。记住多花时间在lintian检查和干净环境测试上能为你后续节省大量的排错时间。
返回列表