Rocky 8.10 系统搭建 Koji 1.34.0 全流程技术总结
摘要
本文详细记录了在 Rocky Linux 8.10 系统上从零搭建 Koji 1.34.0 构建系统的完整流程。Koji 是 Fedora 社区开发的 RPM 包构建与编排系统,广泛应用于 Linux 发行版维护。文章覆盖了从环境准备、SSL 证书体系搭建、PostgreSQL 数据库配置、Koji Hub/Web/Builder 全组件部署,到编译环境构建和客户端接入的完整链路。文中还对 SSL 安全配置、数据库调优等关键环节给出了优化建议,旨在帮助读者快速搭建一套可用的 Koji 构建系统。
一、认识 Koji
什么是 Koji
Koji 是 Fedora 社区维护的一套 RPM 包构建与编排系统,它提供了一套完整的解决方案来管理 RPM 包的构建、签名、发布全流程。
核心组件
| 组件 | 角色 | 说明 |
|---|---|---|
| Koji Hub | 核心服务 | 基于 XMLRPC 的调度中心,管理所有任务和主机 |
| Koji Web | Web 界面 | 基于 Apache + mod_wsgi 的 Web 管理界面 |
| Koji Builder | 编译机 | 实际执行 RPM 构建的从节点,基于 mock 隔离构建 |
| Kojira | 仓库维护 | 自动维护 YUM/DNF 仓库 (repodata) |
| PostgreSQL | 数据库 | 存储任务、主机、用户、包信息等元数据 |
| Koji CLI | 命令行 | 用户通过 koji 命令与 Hub 交互 |
架构流程
本文环境
| 项目 | 说明 |
|---|---|
| 操作系统 | Rocky Linux 8.10 |
| Koji 版本 | 1.34.0 |
| 数据库 | PostgreSQL(随系统 AppStream 仓库,建议 PG 13+) |
| Web 服务器 | Apache httpd + mod_ssl + mod_wsgi |
| 构建隔离 | mock |
| 认证方式 | SSL 证书(双向认证) |
二、环境准备
2.1 关闭防火墙与 SELinux
bash
# 关闭防火墙
systemctl disable firewalld && systemctl stop firewalld
# 关闭 SELinux(需重启后生效)
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
生产环境不建议关闭防火墙,可开放 80/443 端口替代:
bash
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=443/tcp --permanent
firewall-cmd --reload
2.2 安装软件包
先添加 EPEL 仓库:
bash
# EPEL 8 仓库地址
baseurl=https://mirrors.aliyun.com/epel/8/Everything/x86_64/
Server 端(Hub + Web + DB)安装包:
bash
dnf install -y koji koji-hub koji-web koji-utils koji-hub-plugins \
postgresql-server postgresql-contrib httpd mod_ssl python3-mod_wsgi createrepo
Builder 端(编译机)安装包:
bash
dnf install -y koji-builder mock rpm-build createrepo setarch
建议将 Koji 源码包下载至本地编译后安装,确保版本可控。
三、创建 SSL 证书体系
Koji 使用 SSL 双向认证进行通信,每个组件(hub、web、builder、kojiadmin)都需要独立的证书。
3.1 准备目录与配置文件
bash
mkdir -p /etc/pki/koji/{certs,private}
touch /etc/pki/koji/ssl.cnf
/etc/pki/koji/ssl.cnf 内容如下:
ini
HOME = .
RANDFILE = .rand
[ca]
default_ca = ca_default
[ca_default]
dir = .
certs = $dir/certs
crl_dir = $dir/crl
database = $dir/index.txt
new_certs_dir = $dir/newcerts
certificate = $dir/koji_ca_cert.pem
private_key = $dir/private/koji_ca_key.pem
serial = $dir/serial
crl = $dir/crl.pem
x509_extensions = usr_cert
name_opt = ca_default
cert_opt = ca_default
default_days = 3650
default_crl_days = 30
default_md = sha256
preserve = no
policy = policy_match
[policy_match]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[req]
default_bits = 2048
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca
string_mask = MASK:0x2002
[req_distinguished_name]
countryName = Country Name (2 letter code)
countryName_default = CN
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Beijing
localityName = Locality Name (eg, city)
localityName_default = Beijing
0.organizationName = Organization Name (eg, company)
0.organizationName_default = <ORG_NAME>
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = <ORG_TEAM>
commonName = Common Name (eg, your name or your server's hostname)
commonName_default = hub.<DOMAIN>
commonName_max = 64
emailAddress = Email Address
emailAddress_default = <ADMIN_EMAIL>
emailAddress_max = 64
[req_attributes]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[usr_cert]
basicConstraints = CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints = CA:true
安全提示: 原文
[req]段中default_bits = 1024已改为 2048。1024 位 RSA 密钥已被 NIST 认定为不安全,建议所有密钥使用 2048 位或以上。
3.2 创建 CA 认证中心
bash
cd /etc/pki/koji/
touch index.txt
echo 01 > serial
caname=koji
# 生成 CA 私钥
openssl genrsa -out private/${caname}_ca_cert.key 2048
# 生成 CA 自签名证书(有效期 10 年)
openssl req -config ssl.cnf -new -x509 -days 3650 \
-key private/${caname}_ca_cert.key -out ${caname}_ca_cert.crt -extensions v3_ca
常见问题:执行
openssl req时可能报Can't load .rand into RNG错误,原因是随机种子文件缺失。执行openssl rand -writerand .rnd生成种子文件后重试即可。
3.3 生成组件证书
创建 gencert.sh,为所有组件生成证书:
bash
#!/bin/bash
caname=koji
for user in kojira kojiweb kojihub kojiadmin kojibuilder1;
do
openssl genrsa -out certs/${user}.key 2048
openssl req -config ssl.cnf -new -nodes -out certs/${user}.csr -key certs/${user}.key
openssl ca -config ssl.cnf -keyfile private/${caname}_ca_cert.key \
-cert ${caname}_ca_cert.crt -out certs/${user}.crt -outdir certs -infiles certs/${user}.csr
cat certs/${user}.crt certs/${user}.key > ${user}.pem
done
执行时注意:
- 对话选择
[y/n]时选y - Common Name 依次填入
for user in后跟的字段:kojira、kojiweb、kojihub、kojibuilder1、kojiadmin kojiweb和kojihub的Organization Unit Name修改为对应组件名,其余字段不变
3.4 创建浏览器证书与用户证书签发脚本
创建 genperm.sh,用于新增用户签发证书:
bash
#!/bin/bash
usage(){
echo "./genperm.sh <username> <email>"
exit 1
}
[ $# != 2 ] && usage
caname=koji
user=$1
email=$2
generate_private_key() {
rm -rf index.txt*
touch index.txt
openssl genrsa -out certs/${user}.key 2048
}
generate_public_perm() {
echo -e "\n\n\n\n<ORG_NAME> Team\n${user}\n${email}\n\n\n" | \
openssl req -config ssl.cnf -new -nodes -out certs/${user}.csr -key certs/${user}.key
echo -e "y\ny\n" | \
openssl ca -config ssl.cnf -keyfile private/${caname}_ca_cert.key \
-cert ${caname}_ca_cert.crt -out certs/${user}.crt -outdir certs -infiles certs/${user}.csr
cat certs/${user}.crt certs/${user}.key > ${user}.pem
}
generate_browser_cert() {
openssl pkcs12 -export -inkey certs/${user}.key -in certs/${user}.crt \
-CAfile ${caname}_ca_cert.crt -out certs/${user}_browser_cert.p12
}
main() {
[ ! -f certs/${user}.key ] && generate_private_key
generate_public_perm
generate_browser_cert
}
main
exit $?
3.5 配置 kojiadmin 用户证书
bash
useradd kojiadmin
su - kojiadmin
mkdir ~/.koji
cp -pv /etc/pki/koji/kojiadmin.pem ~/.koji/client.crt
cp -pv /etc/pki/koji/koji_ca_cert.crt ~/.koji/clientca.crt
cp -pv /etc/pki/koji/koji_ca_cert.crt ~/.koji/serverca.crt
3.6 配置 Koji CLI 全局配置
/etc/koji.conf:
ini
[koji]
server = http://<SERVER_IP>/kojihub
weburl = http://<SERVER_IP>/koji
topurl = http://<SERVER_IP>/kojifiles
topdir = /mnt/koji
authtype = ssl
cert = ~/.koji/client.crt
serverca = ~/.koji/serverca.crt
每个用户的
~/.koji/config会覆盖全局配置。需要针对不同用户定制时,可将全局配置拷贝到用户目录后修改。
四、PostgreSQL 数据库配置
4.1 初始化并启动数据库
bash
useradd postgres
postgresql-setup --initdb --unit postgresql
systemctl enable postgresql --now
4.2 创建系统用户与数据库
bash
useradd koji
passwd -d koji
su - postgres
createuser koji # 全部填 n
createdb -O koji koji # 创建名为 koji 的数据库,由 koji 用户管理
psql -c "alter user koji with encrypted password '<DB_PASSWORD>';"
exit
4.3 配置数据库访问权限
修改 /var/lib/pgsql/data/pg_hba.conf:
python
# 手动添加这一行且必须放在最前面
host koji koji <SERVER_IP>/32 trust
# 修改以下三行,trust 允许本地免密登录
local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
修改 /var/lib/pgsql/data/postgresql.conf:
ini
listen_addresses = '*'
max_connections = 200
注意: 原文配置了
max_connections = 20000和superuser_reserved_connections = 10000,这对普通服务器来说过高。默认 100 个连接大多数场景够用,建议按需调整。reserved_user_name并非标准 PostgreSQL 配置项(某些发行版内核有定制),标准 PostgreSQL 中不会生效。
重启数据库使配置生效:
bash
systemctl restart postgresql
4.4 导入 Koji Schema
bash
su - koji
psql koji koji < /usr/share/doc/koji*/docs/schema.sql
如果路径不存在,Koji 1.34 可能已将 schema 安装到
/usr/share/koji/schema.sql,改用该路径即可。
4.5 添加管理员与 Builder 用户
bash
su - koji
psql
在 psql 中执行:
sql
insert into users (name, password, status, usertype) values ('kojiadmin', '<DB_PASSWORD>', 0, 0);
insert into users (name, password, status, usertype) values ('kojibuilder1', '<DB_PASSWORD>', 0, 1);
select * from users; -- 记住 kojiadmin 的 id
insert into user_perms (user_id, perm_id, creator_id) values (1, 1, 1);
insert into user_perms (user_id, perm_id, creator_id) values (2, 1, 1);
\q
4.6 数据库时区设置
数据库时区与系统时区不一致会导致 Koji 任务时间显示异常:
sql
-- 查看当前时区
show time zone;
-- 查看当前时间
select now();
-- 修改为北京时间
ALTER DATABASE koji SET timezone TO 'Asia/Shanghai';
五、Koji Hub (Server) 端配置
5.1 httpd 配置
修改 /etc/httpd/conf/httpd.conf,添加 alias 映射:
apache
Alias /kojirepo "/mnt/koji"
<Directory "/mnt/koji">
Options all
</Directory>
5.2 kojihub.conf
修改 /etc/httpd/conf.d/kojihub.conf,启用 SSL 客户端认证:
apache
<Location /kojihub/ssllogin>
SSLVerifyClient require
SSLVerifyDepth 10
SSLOptions +StdEnvVars
</Location>
5.3 hub.conf
修改 /etc/koji-hub/hub.conf:
ini
DBName = koji
DBUser = koji
DBHost = hub.<DOMAIN>
KojiDir = /mnt/koji
ProxyDNs=CN=hub.<DOMAIN>,OU=kojiweb,O=<ORG_NAME>,ST=Beijing,C=CN,emailAddress=<ADMIN_EMAIL>
KojiWebURL = http://<SERVER_IP>/koji
ProxyDNs必须与kojiweb.pem证书的Distinguished Name完全一致,否则 kojiweb 登录会失败。低版本 mod_ssl 格式为:/C=CN/ST=Beijing/O=<ORG_NAME>/CN=hub.<DOMAIN>/emailAddress=<ADMIN_EMAIL>
5.4 ssl.conf
修改 /etc/httpd/conf.d/ssl.conf:
apache
SSLCertificateFile /etc/pki/koji/certs/kojihub.crt
SSLCertificateKeyFile /etc/pki/koji/certs/kojihub.key
SSLCertificateChainFile /etc/pki/koji/koji_ca_cert.crt
SSLCACertificateFile /etc/pki/koji/koji_ca_cert.crt
SSLVerifyClient require
SSLVerifyDepth 10
5.5 SELinux 配置
如果关闭 SELinux 则跳过此节。若保持开启,需配置以下布尔值:
bash
setsebool -P httpd_can_network_connect_db 1
setsebool -P allow_httpd_anon_write=1
semanage fcontext -a -t public_content_rw_t "/mnt/koji(/.*)?"
restorecon -r -v /mnt/koji
如果 /mnt/koji 挂载在 NFS 上,还需:
bash
setsebool -P httpd_use_nfs=1
5.6 创建 Koji 文件系统
bash
mkdir -p /mnt/koji/{packages,repos,work,scratch}
chown apache:apache /mnt/koji/*
5.7 重启 httpd 并添加 kojira 用户
bash
systemctl restart httpd
在 kojiadmin 用户下添加 kojira 并授予 repo 权限:
bash
su - kojiadmin
koji add-user kojira
koji grant-permission repo kojira
六、Koji Web 界面配置
6.1 kojiweb.conf
修改 /etc/httpd/conf.d/kojiweb.conf,启用 SSL 登录:
apache
<Location /koji/login>
SSLVerifyClient require
SSLVerifyDepth 10
SSLOptions +StdEnvVars
</Location>
6.2 web.conf
编辑 /etc/kojiweb/web.conf:
ini
[web]
SiteName = koji
KojiHubURL = http://<SERVER_IP>/kojihub
KojiFilesURL = http://<SERVER_IP>/kojifiles
KojiPackagesURL = http://<SERVER_IP>/mnt/koji/packages
WebCert = /etc/pki/koji/kojiweb.pem
ClientCA = /etc/pki/koji/koji_ca_cert.crt
KojiHubCA = /etc/pki/koji/koji_ca_cert.crt
LoginTimeout = 72
LibPath = /usr/share/koji-web/lib
重启 httpd 后,访问 http://<SERVER_IP>/koji 即可看到 Koji Web 界面。
七、Kojira(仓库维护)配置
7.1 NFS 挂载
Kojira 需要读写 /mnt/koji/repos/,Builder 也需要读取 /mnt/koji/packages/,建议用 NFS 共享。
Server 端 配置 /etc/exports:
bash
/mnt/koji/repos <BUILDER_IP>(rw,no_root_squash,no_all_squash,async)
/mnt/koji/packages <BUILDER_IP>(rw,no_root_squash,no_all_squash,async)
Builder 端 挂载:
bash
mount -t nfs <SERVER_IP>:/mnt/koji/repos /mnt/koji/repos
mount -t nfs <SERVER_IP>:/mnt/koji/packages /mnt/koji/packages
配置开机自动挂载,在 /etc/fstab 中添加:
ruby
<SERVER_IP>:/mnt/koji/repos /mnt/koji/repos nfs defaults 0 0
<SERVER_IP>:/mnt/koji/packages /mnt/koji/packages nfs defaults 0 0
7.2 kojira.conf
编辑 /etc/kojira/kojira.conf:
ini
[kojira]
server=http://<SERVER_IP>/kojihub
topdir=/mnt/koji
cert=/etc/pki/koji/kojira.pem
serverca=/etc/pki/koji/koji_ca_cert.crt
logfile=/var/log/kojira.log
debug=True
启动 kojira:
bash
systemctl enable kojira --now
任何时候只允许运行一个 kojira 实例。不建议在编译机上运行 kojira。
八、Koji Builder(编译机)配置
8.1 kojid.conf
编辑 /etc/kojid/kojid.conf:
ini
[kojid]
sleeptime=15
maxjobs=16
topdir=/mnt/koji
workdir=/tmp/koji
mockdir=/var/lib/mock
mockuser=kojibuilder
vendor=<ORG_NAME>
packager=<ORG_NAME>
distribution=<OS_NAME>
server=http://<SERVER_IP>/kojihub
topurl=http://<SERVER_IP>/kojifiles
cert=/etc/pki/kojid/kojibuilder1.pem
serverca=/etc/pki/kojid/koji_ca_cert.crt
build_arch_can_fail = True
将 Server 端生成的
kojibuilder1.pem和koji_ca_cert.crt拷贝到 Builder 的/etc/pki/kojid/目录下。
8.2 主机名与 hosts 解析
Builder 的 hostname 必须与证书的 Common Name 一致,且在 /etc/hosts 中添加 Hub 的解析:
xml
<SERVER_IP> kojihub
8.3 在 Server 端添加 Builder
bash
su - kojiadmin
koji add-host kojibuilder1 x86_64
注意:host 名必须与证书 CN 一致,否则无法通信。
8.4 启动 Builder
bash
systemctl enable kojid --now
8.5 验证通信
在 Server 端查看:
bash
koji list-hosts
输出示例:
sql
Hostname Enb Rdy Load/Cap Arches Last Update
kojibuilder1 Y Y 0.0/2.0 x86_64 2024-01-06 13:44:49
Rdy=Y 表示通信成功。
九、添加编译机到 Createrepo 频道
默认情况下,Builder 被加入 default 频道。必须将至少一台 Builder 加入 createrepo 频道,否则 kojira 提交的 repo 任务无人执行:
bash
koji add-host-to-channel kojibuilder1 createrepo
调整 Capacity
bash
# 查看主机 ID
psql -U koji koji -c "select (id, name, capacity) from host;"
# 修改 capacity(默认 2,四核以上建议 16)
psql -U koji koji -c "update host set capacity = 16 where id = 1;"
# 或使用命令方式(高版本 Koji 支持)
koji edit-host --capacity=16 kojibuilder1
Capacity 控制的是负载上限:当平均负载超过 Capacity 时,kojid 不再接受新任务。它与
maxjobs(最大并发任务数)配合使用。
十、构建编译环境
以下操作在 kojiadmin 用户下执行。
10.1 创建 Tag 与 Target
bash
# 创建 tag
koji add-tag <OS_NAME>-<VERSION>
# 创建 tag-build(包含架构和继承关系)
koji add-tag --arches=x86_64 --parent=<OS_NAME>-<VERSION> <OS_NAME>-<VERSION>-build
# 创建 build-target
koji add-target <OS_NAME>-<VERSION> <OS_NAME>-<VERSION>-build
10.2 添加 Build Group
bash
# 创建 build 组
koji add-group <OS_NAME>-<VERSION>-build build
# 向 build 组添加包
koji add-group-pkg <OS_NAME>-<VERSION>-build build \
bash bzip2 coreutils cpio diffutils findutils gawk gcc gcc-c++ \
grep gzip info make patch rpm-build sed shadow-utils tar unzip \
util-linux which xz
# 创建 srpm-build 组(koji 与 git 集成时需要)
koji add-group <OS_NAME>-<VERSION>-build srpm-build
koji add-group-pkg <OS_NAME>-<VERSION>-build srpm-build \
bash curl git gnupg2 make rpm-build shadow-utils
10.3 添加外部仓库
bash
koji add-external-repo -t <OS_NAME>-<VERSION>-build External-repos \
http://<MIRROR_URL>/<OS_NAME>/\$arch/
$arch会自动替换为目标架构,用于区分不同架构的仓库。
10.4 编译 SRPM
bash
# 将当前用户加入 mock 组
usermod -aG mock kojiadmin
# 添加包到 Koji
koji add-pkg --owner kojiadmin <OS_NAME>-<VERSION> <PACKAGE_NAME>
# 编译 SRPM
koji build <OS_NAME>-<VERSION> <PACKAGE_NAME>-<VERSION>.src.rpm
编译过程中出现错误,查看构建日志:
bash
ls /var/lib/mock/<xxx>/result/
cat /var/lib/mock/<xxx>/result/root.log
常用命令速查
| 命令 | 用途 |
|---|---|
koji list-hosts |
查看编译机状态 |
koji list-tags |
查看所有 tag |
koji list-targets |
查看所有 target |
koji regen-repo --nowait <target>-build |
手动重构仓库 |
koji cancel <task_id> |
取消构建任务 |
koji call untagBuildBypass <tag> <nvr> |
删除 BUILD |
koji call deleteBuild <nvr> |
彻底删除 BUILD |
koji call resetBuild <nvr> |
重新编译 |
十一、Koji 客户端搭建
11.1 生成客户端证书
管理员在 Server 端为用户生成证书:
bash
cd /etc/pki/koji/
./genperm.sh <USERNAME> <USER_EMAIL>
生成的证书文件:
<USERNAME>.pem--- 客户端证书(含私钥)<USERNAME>_browser_cert.p12--- 浏览器证书(PKCS12 格式)config.tar.gz--- 包含config、服务器 CA、客户端 CA 的配置包
11.2 客户端配置
bash
# 在客户端解压配置包
tar xzf config.tar.gz -C ~/
# 将用户证书放至 .koji 目录并重命名
cp <USERNAME>.pem ~/.koji/client.crt
配置 /etc/hosts 添加 Server 域名解析:
xml
<SERVER_IP> hub.<DOMAIN>
11.3 导入浏览器证书
- 打开 Chrome → 设置 → 显示高级设置 → HTTPS/SSL → 管理证书
- 导入 → 选择
<USERNAME>_browser_cert.p12 - 私钥密码直接跳过(生成时未设置密码)
- 访问
https://hub.<DOMAIN>/koji/登录
十二、学习总结
本文详细记录了在 Rocky Linux 8.10 上从零搭建 Koji 1.34.0 的全过程,核心要点如下:
- SSL 证书体系是 Koji 的基石 --- 所有组件通信都依赖双向 SSL 认证,证书的 Common Name 和文件路径必须一致,否则组件间无法通信。
- 数据库配置注意时区 --- PostgreSQL 时区与系统时间不一致会导致 Koji 任务时间显示异常。
- NFS 挂载是生产环境的标配 --- repos 和 packages 目录需要跨机器共享,NFS 是最简单直接的方式。
- Builder 的 hostname 必须与证书 CN 一致 --- 这是最常见的连通性问题。
- createrepo 频道不能遗漏 --- 至少一台 Builder 需要加入该频道,否则 repo 任务无人执行。
安全优化建议
| 问题 | 原文值 | 建议值 | 说明 |
|---|---|---|---|
| RSA 密钥长度 | 1024 位 | 2048 位 | 1024 位已被 NIST 认定为不安全 |
| 数据库连接数 | 20000 | 200 | 20000 连接会耗尽系统资源 |
| 数据库密码 | 12345678 | 强密码 | 生产环境使用随机生成的强密码 |
| 防火墙 | 关闭 | 开放 80/443 | 生产环境不应关闭防火墙 |
验证笔记
本机验证了以下环节(openssl 1.1.1f + bash 4.4):
- 两段 bash 脚本语法通过
bash -n检查 - SSL 证书生成全流程(CA 自签 → 组件证书签发 → 证书验证)实际跑通
openssl ca签名、openssl pkcs12导出浏览器证书均正常
由于 Koji 部署需要多台机器及 EPEL 仓库,完整部署流程的终端输出留待用户在实际环境中运行后回填。