
为什么要打包
你写了一个 Go 程序,想让别人在 Linux 上用。最简单的方法是扔个二进制文件,但这有几个问题:
- 没法通过
apt或dnf管理 - 升级得手动替换文件
- 卸载后可能留下配置文件
- 依赖关系没法声明
打成 DEB 或 RPM 包能解决这些问题。用户可以用包管理器安装、升级、卸载,系统会自动处理依赖和配置文件。
本文用一个 Go 的 HelloWorld 程序,演示如何从零开始打 DEB 和 RPM 包。所有命令都可以直接执行。
准备示例程序
先写一个简单的 Go 程序。创建 hello.go:
package main
import "fmt"
func main() {
fmt.Println("Hello from packaged binary!")
}
编译成静态二进制文件:
CGO_ENABLED=0 go build -o hello hello.go
CGO_ENABLED=0 确保不依赖 libc,打出来的包可以在任何 Linux 发行版上跑。
测试一下:
./hello
# 输出: Hello from packaged binary!
DEB 打包实战
DEB 包本质上是一个 ar 归档,包含两部分:控制信息和程序文件。
构建目录结构
创建打包目录:
mkdir -p hello-deb/DEBIAN
mkdir -p hello-deb/usr/local/bin
DEBIAN 目录存放控制文件,usr/local/bin 是程序的安装路径。
把二进制文件复制进去:
cp hello hello-deb/usr/local/bin/
chmod 755 hello-deb/usr/local/bin/hello
创建 control 文件
control 文件描述包的元数据。创建 hello-deb/DEBIAN/control:
Package: hello
Version: 1.0.0
Section: utils
Priority: optional
Architecture: amd64
Maintainer: Your Name <your@email.com>
Description: A simple hello world program
This is a demo package for learning DEB packaging.
几个关键字段:
- Package: 包名,安装后用
apt remove hello卸载 - Version: 版本号,升级时用
- Architecture:
amd64是 64 位 x86,ARM 用arm64 - Description: 第一行是简短描述,后续行要以空格开头
打包
用 dpkg-deb 打包:
dpkg-deb --build hello-deb
会生成 hello-deb.deb 文件。改个更规范的名字:
mv hello-deb.deb hello_1.0.0_amd64.deb
命名格式是 包名_版本_架构.deb。
安装测试
在 Debian/Ubuntu 系统上安装:
sudo dpkg -i hello_1.0.0_amd64.deb
运行:
hello
# 输出: Hello from packaged binary!
查看已安装的包信息:
dpkg -s hello
卸载:
sudo dpkg -r hello
RPM 打包实战
RPM 打包依赖一个 .spec 文件,所有配置都写在里面。
准备构建环境
RPM 有固定的目录结构。创建:
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
这些目录的作用:
- BUILD: 编译过程的临时目录
- RPMS: 最终生成的二进制包
- SOURCES: 源代码压缩包
- SPECS: spec 文件
- SRPMS: 源码包
准备源码包
把程序打成 tar.gz:
mkdir hello-1.0.0
cp hello hello-1.0.0/
tar czf hello-1.0.0.tar.gz hello-1.0.0/
mv hello-1.0.0.tar.gz ~/rpmbuild/SOURCES/
RPM 打包习惯从源码开始,所以要先打个源码包。
创建 spec 文件
创建 ~/rpmbuild/SPECS/hello.spec:
Name: hello
Version: 1.0.0
Release: 1%{?dist}
Summary: A simple hello world program
License: MIT
Source0: %{name}-%{version}.tar.gz
%description
This is a demo package for learning RPM packaging.
%prep
%setup -q
%install
mkdir -p %{buildroot}/usr/local/bin
install -m 755 hello %{buildroot}/usr/local/bin/hello
%files
/usr/local/bin/hello
%changelog
* Tue Jan 14 2026 Your Name <your@email.com> - 1.0.0-1
- Initial package
关键字段解释:
- %prep: 解压源码包
- %setup -q: 自动解压 Source0 并切换到解压目录
- %install: 把文件安装到 %{buildroot}(虚拟根目录)
- %files: 列出包里包含的所有文件
- %changelog: 变更日志,格式严格(星号、日期、版本号)
打包
用 rpmbuild 打包:
rpmbuild -ba ~/rpmbuild/SPECS/hello.spec
-ba 同时生成二进制包和源码包。生成的文件在:
ls ~/rpmbuild/RPMS/x86_64/
# hello-1.0.0-1.el8.x86_64.rpm
文件名包含 el8(表示 RHEL 8)或 fc36(表示 Fedora 36),这是 %{?dist} 宏自动加的。
安装测试
在 RHEL/CentOS/Fedora 系统上安装:
sudo rpm -ivh ~/rpmbuild/RPMS/x86_64/hello-1.0.0-1.*.x86_64.rpm
运行:
hello
# 输出: Hello from packaged binary!
查看包信息:
rpm -qi hello
卸载:
sudo rpm -e hello
两种方式对比
维度
DEB
RPM
配置文件
多个小文件(control, postinst 等)
一个 spec 文件
依赖声明
Depends: python3, curl
Requires: python3, curl
构建工具
dpkg-deb(轻量)
rpmbuild(需要完整环境)
源码处理
可选
强制打 tar.gz
架构命名
amd64, arm64
x86_64, aarch64
DEB 更灵活:不强制要源码包,可以直接打二进制包。适合快速打包。
RPM 更规范:spec 文件集中管理所有配置,大型项目更清晰。企业环境常用。
实战建议
- 静态编译:Go 程序用
CGO_ENABLED=0,避免 libc 版本问题 - 版本号规范:用
major.minor.patch格式,比如1.2.3 - 测试安装:在干净的容器里测试,确保依赖声明正确
- 自动化:写个 Makefile 或 shell 脚本,一键打包
一个简单的 Makefile 示例:
VERSION = 1.0.0
BINARY = hello
.PHONY: build deb rpm
build:
CGO_ENABLED=0 go build -o $(BINARY) hello.go
deb: build
mkdir -p hello-deb/DEBIAN
mkdir -p hello-deb/usr/local/bin
cp $(BINARY) hello-deb/usr/local/bin/
cp debian-control hello-deb/DEBIAN/control
dpkg-deb --build hello-deb
mv hello-deb.deb hello_$(VERSION)_amd64.deb
rpm: build
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
mkdir hello-$(VERSION)
cp $(BINARY) hello-$(VERSION)/
tar czf hello-$(VERSION).tar.gz hello-$(VERSION)/
mv hello-$(VERSION).tar.gz ~/rpmbuild/SOURCES/
cp hello.spec ~/rpmbuild/SPECS/
rpmbuild -ba ~/rpmbuild/SPECS/hello.spec
运行:
make deb # 打 DEB 包
make rpm # 打 RPM 包
常见问题
Q: 能不能一个包同时支持 DEB 和 RPM?
不行。得分别打包。但可以用 FPM 工具,它可以从一个配置生成多种格式的包。
Q: 我的程序依赖其他包怎么办?
在 control 或 spec 文件里声明。比如依赖 Python:
- DEB:
Depends: python3 - RPM:
Requires: python3
安装时包管理器会自动安装依赖。
Q: 打出来的包能跨发行版吗?
如果是静态编译的 Go 程序,理论上可以。但不同发行版的目录结构和依赖包名可能不同,最好分别打包。
Q: 生产环境怎么管理自己打的包?
搭建私有软件仓库。DEB 用 aptly 或 reprepro,RPM 用 createrepo。
以上就是从零开始打 DEB 和 RPM 包的完整流程。用一个 HelloWorld 程序演示了最核心的步骤,实际项目可能需要处理配置文件、systemd 服务等,但基本原理是一样的。