-
Notifications
You must be signed in to change notification settings - Fork 0
article1_0
오픈 소스 세계에서 Ubuntu는 가장 인기 있는 Linux 배포판 중 하나입니다. CI/CD에 많이 사용되는 Github Action runner에서도 기본 Linux 이미지로 Ubuntu를 사용하고 있습니다. 따라서 우리가 만든 라이브러리를 Ubuntu 사용자들이 쉽게 설치하고 사용할 수 있게 하는 것은 중요하며, 이를 위한 가장 효과적인 방법 중 하나가 바로 deb 패키지를 통한 배포입니다.
deb 패키지는 Debian 계열 Linux 배포판(Ubuntu 포함)에서 사용되는 소프트웨어 패키지 형식입니다. 이 형식을 사용하면 사용자들이 apt
또는 dpkg
명령어를 통해 쉽게 소프트웨어를 설치, 업그레이드, 제거할 수 있습니다. 또한 의존성 관리, 설치 후 스크립트 실행 등 다양한 기능을 제공하여 소프트웨어 배포를 더욱 효율적으로 만들어줍니다.
이번 글에서는 이전에 소개한 ALN(Amazing Lucky Numbers) 라이브러리를 Ubuntu Linux용 deb 패키지로 만드는 과정을 다루겠습니다. 패키지 생성에 필요한 파일 구조, 제어 파일 작성, 빌드 과정, 그리고 최종적으로 패키지를 생성하고 배포하는 방법에 대해 설명하겠습니다.
Ubuntu에서 패키지 설치 시 사용되는 파일은 .deb
확장자를 사용합니다. 아래에서 이 파일의 형식과 구성에 대해 자세히 알아보겠습니다.
.deb
파일은 기본적으로 ar
아카이브 포맷을 사용하는 압축 파일입니다. 압축을 해제하면 다음과 같은 파일들이 생성됩니다:
-
debian-binary
- 패키지 형식의 버전을 나타내는 텍스트 파일입니다. 현재 대부분 경우 버전은
2.0
입니다.
- 패키지 형식의 버전을 나타내는 텍스트 파일입니다. 현재 대부분 경우 버전은
-
control.tar.gz
(또는control.tar.zst
)- 패키지의 메타데이터를 포함하는 압축 파일
-
data.tar.gz
(또는data.tar.zst
)- 실제 설치될 파일들을 포함하는 압축 파일입니다. 이 파일에는 실제로 시스템에 설치될 파일들이 들어 있습니다. 파일 구조는 실제 설치될 디렉토리 구조를 그대로 반영합니다.
control.tar.gz
압축 파일에 포함된 메타데이터의 주요 파일들은 다음과 같습니다:
-
control
: 패키지 이름, 버전, 의존성 등의 정보 -
md5sums
: 데이터 파일들의 MD5 체크섬 -
preinst
,postinst
,prerm
,postrm
: 설치/제거 전후에 실행되는 스크립트들
먼저 apt
도구를 이용해 임의의 패키지를 하나 다운로드 받습니다. apt download
명령으로 deb 패키지 파일을 직접 다운로드 받을 수 있습니다.
$ apt download libglib2.0-0
$ ls
libglib2.0-0_2.72.4-0ubuntu2.3_arm64.deb
이제 ar
명령을 통해 압축을 해제합니다. 위에서 설명했던 것처럼 debian-binary
, control.tar.zst
그리고 data.tar.zst
파일이 생성되었습니다.
$ ar x libglib2.0-0_2.72.4-0ubuntu2.3_arm64.deb
$ ls
control.tar.zst data.tar.zst debian-binary libglib2.0-0_2.72.4-0ubuntu2.3_arm64.deb
메타데이터를 확인하기 위해 패키지에 대한 정보가 들어있는 control.tar.zst
파일의 압축을 해제합니다.
$ tar xvf control.tar.zst
./
./control
./md5sums
./postinst
./postrm
./shlibs
./symbols
./triggers
압축 해제 후 생성된 파일들 중 control
파일의 내용을 확인해보면 이 패키지에 대한 이름, 설명, 버전, 메인테이너 및 의존성 정보를 모두 알 수 있습니다.
$ cat control
Package: libglib2.0-0
Source: glib2.0
Version: 2.72.4-0ubuntu2.3
Architecture: arm64
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Installed-Size: 4137
Depends: libc6 (>= 2.34), libffi8 (>= 3.4), libmount1 (>= 2.35.2-7~), libpcre3, libselinux1 (>= 3.1~), zlib1g (>= 1:1.2.2)
Recommends: libglib2.0-data, shared-mime-info, xdg-user-dirs
Breaks: gimp (<< 2.10.14-3~), glib-networking-tests (<< 2.70.0~), gnome-keyring (<< 40.0-3~), gnome-shell (<< 42.9-0ubuntu2.1~), libgirepository-1.0-1 (<< 1.62.0-4~), libgladeui-2-6 (<< 3.22.2), libsoup2.4-tests (<< 2.72.0-3~)
Section: libs
Priority: optional
Multi-Arch: same
Homepage: https://wiki.gnome.org/Projects/GLib
Description: GLib library of C routines
GLib is a library containing many useful C routines for things such
as trees, hashes, lists, and strings. It is a useful general-purpose
C library used by projects such as GTK+, GIMP, and GNOME.
.
This package contains the shared libraries.
Original-Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
이제 실제 설치될 파일들을 담고 있는 data.tar.zst
파일도 압축을 해제합니다.
$ tar xvf data.tar.zst
...
./usr/lib/aarch64-linux-gnu/gio/modules/
...
./usr/lib/aarch64-linux-gnu/libgio-2.0.so.0.7200.4
./usr/lib/aarch64-linux-gnu/libglib-2.0.so.0.7200.4
./usr/lib/aarch64-linux-gnu/libgmodule-2.0.so.0.7200.4
./usr/lib/aarch64-linux-gnu/libgobject-2.0.so.0.7200.4
./usr/lib/aarch64-linux-gnu/libgthread-2.0.so.0.7200.4
...
./usr/share/doc/libglib2.0-0/copyright
...
./usr/lib/aarch64-linux-gnu/libgio-2.0.so.0
./usr/lib/aarch64-linux-gnu/libglib-2.0.so.0
./usr/lib/aarch64-linux-gnu/libgmodule-2.0.so.0
./usr/lib/aarch64-linux-gnu/libgobject-2.0.so.0
./usr/lib/aarch64-linux-gnu/libgthread-2.0.so.0
이렇게 해서 패키지가 포함하고 있는 모든 파일들을 확인할 수 있었습니다.
패키지를 만들 때 이름과 버전을 정해야 하는데, deb 패키지에서 일반적으로 사용하는 규칙이 있습니다.
- 패키지 이름
- 소문자(대문자는 사용하지 않습니다.), 숫자, 기호(
+
,-
,.
,~
) - 라이브러리의 경우 일반적으로 앞에
lib
를 붙입니다. - 라이브러리의 major 버전 번호를 뒤에 붙입니다. 예:
libxxx0
,libxxx1
- 라이브러리의 헤더 파일등을 포함하고 있는 개발(빌드)용 패키지의 경우 일반적으로 뒤에
-dev
를 붙입니다.
- 소문자(대문자는 사용하지 않습니다.), 숫자, 기호(
- 버전
- 일반적으로 major.minor.patch 형식을 사용합니다. 예:
1.2.0
- 같은 버전인데 패키지 메인테이너에 의해 패키지가 수정될 경우 리비전을 붙입니다. 예:
1.2.0-1
- 보통 배포판에 맞게 패키징하는 과정에서 수정이 필요한 경우 리비전이 올라갑니다.
- Ubuntu 패키징을 위해 기존 Debian 패키지를 기반으로 수정했을 경우
ubuntu
이름과 함께 리비전 번호가 추가됩니다.-
1.2.0-2ubuntu1
: Debian에 맞게 패키징된 리비전 2를 기반으로 Ubuntu에 맞게 추가로 수정한 패키지 리비전 1을 의미합니다.
-
- 일반적으로 major.minor.patch 형식을 사용합니다. 예:
위에서 apt download
를 통한 다운로드 받은 deb 패키지 이름을 위의 규칙으로 분석해 보면 아래와 같습니다.
- 파일명:
libglib2.0-0_2.72.4-0ubuntu2.3_arm64.deb
- 패키지 이름:
libglib2.0-0
(-0
은 하위 버전 또는 패키지 버전을 나타냄) - 버전
- glib 라이브러리 버전:
2.72.4
- 리비전:
0ubuntu2.3
(0
: Debian 리비전,2.3
: Ubuntu 리비전)
- glib 라이브러리 버전:
- 종류: 라이브러리 (
lib
prefix) - 아키텍처:
arm64
라이브러리 패키지에 대해 libxxx
와 libxxx-dev
로 분리되어 있는데, 그 이유는 아래와 같습니다.
- 애플리케이션을 실행하는데 필요한 의존성 라이브러리
- 라이브러리 패키지에서 제공하는 shared library(
.so.N
) 파일만 있으면 됩니다.
- 라이브러리 패키지에서 제공하는 shared library(
- 애플리케이션 개발 및 빌드를 위해 필요한 의존성 라이브러리
- 라이브러리 패키지에서 헤더 파일, pkg-config 파일 그리고 shared library를 같이 제공해줘야 애플리케이션을 정상적으로 빌드할 수 있습니다.
deb 패키지를 만들기 위해 필요한 항목들을 확인해보고 예제 라이브러리인 ALN 기준으로 실제로 작성해 보겠습니다.
먼저 패키징을 위한 파일들은 debian
디렉토리 밑에 위치해야 합니다.
.
├── CMakeLists.txt
├── debian
│ ├── aln.install
│ ├── changelog
│ ├── compat
│ ├── control
│ ├── copyright
│ ├── libaln-dev.install
│ ├── libaln0.install
│ ├── rules
│ └── source
│ └── format
├── include
│ └── aln.h
└── src
├── CMakeLists.txt
└── aln.c
파일들을 하나씩 확인해 보면 아래와 같습니다.
-
changelog
- 패키지를 발행할 때마다 버전과 변경사항들을 이 파일에 적어야 합니다. -
compat
- 패키지를 빌드할 때debhelper
도구를 사용하는데, 이 도구의 호환성 수준을 지정합니다. 22.04 (Jammy) 기준으로13
이 사용됩니다. -
control
- 패키지에 대한 이름, 설명, 의존성, 생성할 패키지 목록등을 정의합니다. -
copyright
- 라이센스 정보 -
rules
- 패키지 빌드 과정에서 수행해야 할 스크립트를 정의합니다. -
source/format
- 소스 패키지 포맷을 정의합니다. 전통적인 포맷은1.0
이 사용되고 최신 포맷은3.0 (quilt)
또는3.0 (native)
가 사용됩니다. -
*.install
- 각 패키지별로 설치할 파일 목록을 정의합니다.
changelog
파일은 패키지의 변경 사항을 기록하는 데 사용됩니다. 이 파일은 패키지의 각 릴리즈마다 어떤 변경이 있었는지, 누가 변경을 했는지, 언제 변경이 이루어졌는지 등의 정보를 포함합니다. changelog
파일은 패키지를 유지보수하고 관리하는 데 중요한 역할을 하며, 형식은 엄격하게 정의되어 있습니다.
package (version) distribution; urgency=urgency
* change details
-- maintainer-name <maintainer-email> date
ALN 예제로 작성하면 아래와 같습니다.
aln (0.1.0~jammy) jammy; urgency=medium
* Initial release.
-- Inho Oh <webispy@gmail.com> Tue, 02 Jul 2024 00:00:00 +0900
새로운 버전을 배포하거나 리비전을 올릴 경우 기존파일의 윗부분에 내용을 추가하면 됩니다. vi
와 같은 편집기로 직접 수정을 하거나 dch
와 같은 changelog 작성 도구를 이용하면 됩니다.
aln (0.1.0-1~jammy) jammy; urgency=medium
* Fix typo in debian/control
-- Inho Oh <webispy@gmail.com> Tue, 02 Jul 2024 09:00:00 +0900
aln (0.1.0~jammy) jammy; urgency=medium
* Initial release.
-- Inho Oh <webispy@gmail.com> Tue, 02 Jul 2024 00:00:00 +0900
버전명에 ~jammy
와 같이 배포판 이름을 추가하는 이유는 PPA(Personal Package Archive)에 각 배포판(focal, jammy, noble 등)용 패키지를 추가로 업로드할 때 소스 압축파일의 이름(예: aln_0.1.0.tar.gz
)이 동일하여 발생할 수 있는 업로드 실패(Reject)를 방지하기 위해서입니다. PPA를 통해 배포할 계획이 없다면 추가하지 않아도 됩니다.
control
파일은 패키지의 메타데이터를 정의하는 파일로, 패키지의 속성, 의존성, 메인테이너 정보 등을 포함합니다. control
파일은 Source
섹션과 Package
섹션들로 구성됩니다.
Source:
Source: package-name
Section: section
Priority: required, important, standard or optional
Maintainer: full name <email>
Build-Depends: dependency1, dependency2
Standards-Version: standards-version
Homepage: homepage-url
- Source: 패키지 이름
- Section: 패키지가 속하는 섹션으로
net
,libs
등을 사용할 수 있습니다. 전체 목록은 https://packages.ubuntu.com/jammy/에서 확인할 수 있습니다. - Priority: 패키지의 중요도를 의미하는데, Ubuntu의 system 패키지가 아닌 이상 대부분
optional
을 사용하면 됩니다. - Maintainer: 패키지를 관리하는 사람의 이름과 이메일
- Build-Depends: 이 패키지를 빌드하는데 필요한 의존성 패키지들을 나열합니다.
- Standards-Version: 패키지가 준수하는 Debian 정책 버전으로 https://www.debian.org/doc/debian-policy/upgrading-checklist.html에서 버전별 변경점을 확인할 수 있습니다.
- Homepage: 패키지의 홈페이지 URL
Package:
Package: package-name
Architecture: any or all
Section: section
Depends: dependency1, dependency2
Description: short description
long description
.
blah blah
- Package: 패키지 이름
- Architecture: 패키지가 지원하는 아키텍처로 특정 아키텍처에서만 실행될 경우
any
, 아키텍처와 상관없이 사용 가능한 스크립트나 리소스 같은 패키지일 경우all
을 사용합니다. - Section: 패키지가 속하는 섹션. Source에서 정한 Section과 다르게 설정할 수 있고, Package들 사이에서도 다르게 설정할 수 있습니다.
- Depends: 이 패키지가 실행되는데 필요한 의존성 패키지들을 나열합니다.
- Description: 패키지에 대한 설명으로 첫 줄은 요약된 짧은 설명이고, 그다음 줄부터 멀티라인으로 긴 설명을 적을 수 있습니다. 긴 설명을 작성할 때 앞에 한 칸씩 공백이 있어야 합니다. 만약 공백 라인이 필요할 경우에는 한 칸 공백 뒤에
.
을 입력하면 됩니다.
아래는 ALN에서 실제로 사용하는 control 파일 예제입니다. aln
소스와 libaln0
, libaln-dev
, libaln0-dbg
, aln
, aln-dbg
바이너리 패키지를 정의하고 있습니다.
Source: aln
Section: libs
Priority: optional
Maintainer: Inho Oh <webispy@gmail.com>
Build-Depends: debhelper (>= 13), cmake, libglib2.0-dev, doxygen
Standards-Version: 4.1.4
Homepage: https://github.com/webispy/aln
Package: libaln0
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libglib2.0-0
Description: Amazing Lucky Numbers Library
A library for generating amazing lucky numbers.
Package: libaln-dev
Architecture: any
Section: libdevel
Depends: ${shlibs:Depends}, ${misc:Depends}, libaln0 (= ${binary:Version}), libglib2.0-dev
Description: Amazing Lucky Numbers Library (development files)
A library for generating amazing lucky numbers.
Package: libaln0-dbg
Section: debug
Priority: optional
Architecture: any
Depends: libaln0 (= ${binary:Version}), aln (= ${binary:Version}), ${misc:Depends}
Description: Debugging symbols for libaln0
This package contains the debugging symbols for the aln library.
Package: aln
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libaln0, libglib2.0-0
Description: Amazing Lucky Numbers Tool
Command line tool for generating and using amazing lucky numbers.
This package contains the aln command line tool that utilizes the aln library.
Package: aln-dbg
Section: debug
Priority: optional
Architecture: any
Depends: aln (= ${binary:Version}), ${misc:Depends}
Description: Debugging symbols for aln
This package contains the debugging symbols for the aln tool.
rules
파일은 패키지를 빌드하기 위해 사용하는 Makefile
형식의 스크립트 파일입니다. 이 파일은 패키지의 빌드 과정에서 어떤 작업을 수행해야 하는지 정의하며, 패키지 빌드의 핵심 부분을 담당합니다.
각 패키지마다 고유한 빌드 스크립트가 필요한데, 이를 개별적으로 rules
파일에 작성하면 복잡성이 증가하고 유지보수와 검증이 어려워질 수 있습니다. 이러한 문제를 해결하기 위해 debhelper
라는 도구가 제공됩니다. debhelper
는 표준화된 템플릿을 사용하여 빌드 및 설치 과정을 간소화하고, 일관성 있는 패키징을 가능하게 합니다.
대부분의 패키지는 아래와 같이 몇 줄 안되는 코드로 해결할 수 있습니다. dh
는 debhelper
를 의미하고, %
는 기본 타겟을 의미합니다.
rules:
#!/usr/bin/make -f
%:
dh $@
세부적으로는 configure, build, install, clean 등 복잡한 여러 단계가 사용되지만 각 단계마다 dh
가 자동으로 단계에 맞게 처리합니다. 특정 단계에서 기본과 다르게 처리하고자 할 경우 override_
prefix를 이용해서 직접 원하는 명령을 수행할 수 있습니다.
아래는 ALN에서 사용하는 rules
파일 내용입니다.
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_configure:
dh_auto_configure -- \
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
override_dh_install:
doxygen
find doc/man/man3 -type f ! -name 'aln*' -exec rm -f {} \;
install -d $(CURDIR)/debian/tmp/usr/share/man/man3
install -m 644 doc/man/man3/* $(CURDIR)/debian/tmp/usr/share/man/man3/
dh_install
override_dh_strip:
dh_strip -plibaln0 --dbg-package=libaln0-dbg
dh_strip -paln --dbg-package=aln-dbg
override_dh_auto_configure:
- configure 단계에서
dh
가 CMake를 자동으로 인식해서 처리하는데, 여기에 원하는 옵션을 추가로 설정할 수 있습니다. - CMake 뿐만 아니라 Autotools, meson 등 유명한 대부분의 빌드 시스템을 지원합니다.
override_dh_install:
- install 단계에서 원하는 명령을 수행할 수 있습니다. 위의 예제는 실제로 설치하기 전에 doxygen 명령으로 manpage를 만들어서 대상 디렉토리에 필요한 파일을 설치한 후
dh_install
을 수행하여 나머지 install 단계를 수행시킵니다.
override_dh_strip:
- 빌드가 다 끝난 후 생성된 패키지에는 디버깅 정보가 모두 제거(strip)된 상태로 복사됩니다.
- 디버깅을 위해 별도의 디버깅 패키지를 만들고 싶을 경우 위의 예제처럼
dh_strip
명령에 옵션으로 디버깅 패키지를 지정할 수 있습니다.
*.install
파일은 패키지에 포함될 파일들과 설치 위치를 지정합니다. control
파일에서 정의된 각 패키지에 대해 install
파일들을 생성하어 해당 패키지에 포함될 파일들을 정의해야 합니다. 단, debug 패키지는 자동으로 처리되므로 별도의 install
파일이 필요하지 않습니다.
빌드 과정 중 생성된 모든 파일들(예: make install 등의 명령으로 설치된 파일들)은 임시로 debian/tmp/
디렉토리에 설치됩니다. 이 파일들을 각 패키지에 적절히 분배하려면 {package-name}.install
파일을 만들고, 그 안에 해당 패키지에 포함할 파일들을 나열하면 됩니다.
ALN에서 설치하는 파일들을 정리 해보면 이 과정을 더 쉽게 이해할 수 있습니다.
배포할 파일 목록:
.
└── usr
├── bin
│ └── aln
├── include
│ └── aln
│ └── aln.h
├── lib
│ └── x86_64-linux-gnu
│ ├── libaln.so -> libaln.so.0
│ ├── libaln.so.0 -> libaln.so.0.1.0
│ ├── libaln.so.0.1.0
│ └── pkgconfig
│ └── aln.pc
└── share
└── man
├── man1
│ └── aln.1
└── man3
├── aln.h.3
├── aln_draw_all.3
├── aln_draw_number.3
├── aln_free.3
├── aln_get_number.3
├── aln_new.3
└── aln_reset.3
포함할 파일:
usr/bin/aln
usr/share/man/man1/aln.1
install 파일: aln.install
debian/tmp/usr/bin/
debian/tmp/usr/share/man/man1/aln.1
포함할 파일:
usr/lib/x86_64-linux-gnu/libaln.so.0 -> libaln.so.0.1.0
usr/lib/x86_64-linux-gnu/libaln.so.0.1.0
install 파일: libaln0.install
debian/tmp/usr/lib/*/*.so.*
포함할 파일:
usr/include/aln/aln.h
usr/lib/x86_64-linux-gnu/libaln.so
usr/lib/x86_64-linux-gnu/pkgconfig/aln.pc
usr/share/man/man3/*
install 파일: libaln-dev.install
debian/tmp/usr/lib/*/*.so
debian/tmp/usr/lib/*/pkgconfig/*
debian/tmp/usr/include/
debian/tmp/usr/share/man/man3/aln.h.3
debian/tmp/usr/share/man/man3/aln_*.3
Ubuntu는 연 2회 새 버전을 릴리즈하며, 2년마다 LTS(Long Term Support) 버전을 배포합니다. 이렇게 다양한 버전을 단일 debian 디렉토리로 관리하는 것은 어려움이 있습니다. 배포판 버전별로 패키징이 다를 수 있고, changelog도 별도로 관리해야 할 필요가 있기 때문입니다.
이러한 문제를 해결하기 위해 버전별로 다른 브랜치를 사용하거나, 별도의 저장소에서 관리하면서 CI/CD를 통해 제어하는 등 다양한 방법들이 등이 있을 수 습니다.
ALN의 경우, packaging
디렉토리 아래에 버전별로 debian
역할을 하는 디렉토리를 만들어 두고, 패키징 시 원하는 버전을 debian
디렉토리에 복사하는 방식을 채택했습니다. 또한, 향후 설명할 RPM 패키징 스크립트도 관리하기 위해 패키징 관련 파일들을 모두 packaging
디렉토리 아래에 위치시켰습니다.
디렉토리 구성:
.
├── include
├── packaging
│ ├── focal : control, rules, ...
│ ├── jammy : control, rules, ...
│ └── noble : control, rules, ...
└── src
패키징 작업:
cp -a packaging/focal debian
...패키징 명령...
Ubuntu에서는 deb 패키지를 관리 하거나 만들 수 있는 여러가지 도구들을 제공하고 있습니다.
-
dpkg
- deb 패키지 관리 -
dpkg-buildpackage
- deb 패키지를 생성하는 기본 명령어 -
gbp
- git과 연계하여 deb 패키지 생성 -
sbuild
- chroot를 통해 격리된 환경에서 패키지 생성
deb
패키지는 dpkg
도구를 이용해 설치, 제거 및 패키지 목록 확인 등을 할 수 있습니다. 앞부분에서 사용한 apt install
명령으로 패키지 설치를 진행할 경우 경우, 네트워크를 통해 필요한 패키지를 먼저 다운로드 받은 후 내부적으로 dpkg
명령을 통해 실제로 패키지를 설치하게 됩니다.
dpkg -i package.deb : 패키지 설치
dpkg -r package : 패키지 제거
dpkg -l : 설치된 전체 패키지 목록 확인
dpkg -s package : 패키지 정보 확인 (설치된 패키지 이름 기반)
dpkg -I package.deb : 패키지 정보 확인 (패키지 파일명 기반)
dpkg -L package : 패키지에 포함된 파일 목록 확인 (설치된 패키지 이름 기반)
dpkg -c package.deb : 패키지에 포함된 파일 목록 확인 (패키지 파일명 기반)
dpkg-buildpackage
는 deb 패키지를 빌드하기 위한 기본 도구입니다. 주로 개발 환경에서 직접 빌드할 때 많이 사용되는데, 현재 시스템 환경에서 바로 패키지를 빌드하기 때문에 빠르게 패키지를 만들 수 있습니다.
하지만, 내 시스템에서는 정상적으로 패키지 빌드에 성공했어도 다른 사람의 시스템 환경에서 똑같이 빌드가 성공한다고 보장할 수 없습니다. 각 시스템마다 설치된 패키지들이 다르기 때문입니다. 따라서, 정식으로 패키징 작업을 진행하기 전에 빠르게 패키징을 테스트할 목적으로 사용하기에 적합한 도구입니다.
ALN 기준으로 패키지 빌드하는 방법은 아래와 같습니다.
# 소스 다운로드
git clone https://github.com/webispy/aln.git
cd aln
# debian 패키지 디렉토리 준비 (from packaging/jammy, packaging/focal)
DIST_NAME=$(lsb_release -c | awk '{print $2}')
cp -a packaging/$DIST_NAME debian
# 패키지 빌드 (서명 없이 빌드)
# -uc: unsigned .buildinfo and .changes file.
# -us: unsigned source package.
dpkg-buildpackage -uc -us
패키지들은 dpkg-buildpackage
명령을 실행한 디렉토리의 상위 디렉토리에 생성됩니다.
$ cd ..
$ ls
aln # -> 소스 디렉토리
aln-dbg_0.1.0~jammy_arm64.deb
aln_0.1.0~jammy.dsc
aln_0.1.0~jammy.tar.gz
aln_0.1.0~jammy_arm64.buildinfo
aln_0.1.0~jammy_arm64.changes
aln_0.1.0~jammy_arm64.deb
libaln-dev_0.1.0~jammy_arm64.deb
libaln0-dbg_0.1.0~jammy_arm64.deb
libaln0_0.1.0~jammy_arm64.deb
이제 dpkg
명령으로 ALN 패키지를 시스템에 설치 및 삭제할 수 있습니다. 단, dpkg
명령은 의존성 패키지에 대해 자동으로 해결해주지 않기 때문에 설치 시 의존성 패키지 파일들도 같이 나열해 주어야 정상적으로 설치됩니다.
# 설치
sudo dpkg -i aln_0.1.0~jammy_arm64.deb libaln0_0.1.0~jammy_arm64.deb
# 삭제
sudo dpkg -r aln libaln0
gbp
는 이름에서 알 수 있듯이 dpkg-buildpackage
와 Git 리포지토리를 통합한 패키지 빌드 툴입니다. git tag, branch 등과 연계해서 더 강력하게 패키징 작업을 진행할 수 있고, 소스 수정 후 커밋을 만들지 않은 상태에서 gbp buildpackage
명령을 실행하면 수정된 파일을 알려주면서 빌드 에러가 발생하기 때문에 실수를 줄여줄 수 있습니다.
gbp 설치는 아래와 같이 apt 명령으로 가능합니다.
sudo apt install git-buildpackage
빌드 옵션은 dpkg-buildpackage
와 동일하기 때문에, 아래와 같이 명령을 실행하면 패키지를 생성할 수 있습니다.
gbp buildpackage -uc -us
자세한 옵션 및 사용법은 https://honk.sigxcpu.org/piki/projects/git-buildpackage/ 홈페이지를 참고 바랍니다.
Debian 빌드 시스템에서 사용되는 강력한 빌드 도구로 dpkg-buildpackage
와 다르게 현재 시스템 환경을 이용하지 않고 chroot
환경(change root의 약자로 격리된 파일 시스템 환경을 제공)에서 빌드합니다.
Ubuntu의 필수 패키지만 설치된 깨끗한 가상 환경에서 빌드가 진행되기 때문에, 빌드하려는 패키지의 의존성을 확실하게 점검할 수 있고 격리된 환경이기 때문에 현재 시스템에 영향을 주지 않아 안전하게 패키징 작업을 진행할 수 있습니다.
또한, chroot를 사용하기 때문에 현재 시스템에 설치된 Ubuntu 배포판 버전과 다른 버전용 패키지를 생성할 수 있고 cross-compile 환경도 지원합니다. 예를 들어 현재 시스템에는 Ubuntu 22.04 (Jammy) amd64가 설치되어 있는데, 20.04 (Focal)용 패키지를 생성할 수 있고, arm64/armhf 용 패키지도 만들 수 있습니다.
sbuild를 통해 패키징을 수행하면 아래 과정이 매번 수행됩니다.
apt update
apt upgrade
-
debian/control
에 명시한 의존성 패키지 설치 - 빌드 (
dpkg-buildpackage
) - lintian - 패키지 검사
이후 sbuild가 끝나면 패키징 과정 중 설치/생성된 모든 파일들이 제거됩니다. 따라서, 다시 패키징을 수행해도 매번 깨끗한 환경에서 위 과정이 새로 수행됩니다. 이는 sbuild 내부에서 docker와 유사하게 overlayfs를 사용하기 때문입니다.
sbuild 설치는 아래와 같이 apt
명령으로 가능합니다.
sudo apt install -y sbuild ubuntu-dev-tools
설치 후 추가 설정 작업이 필요한데, sbuild 패키지를 설치하면 sbuild
그룹이 추가됩니다. 따라서 sbuild 명령을 사용하려면 사용자를 그룹에 추가해야 합니다.
sudo sbuild-adduser $USER
참고로, 그룹에 사용자를 추가했다고 현재 쉘에 바로 그룹이 반영되지 않기 때문에 새로운 쉘을 열거나 현재 쉘에서 newgrp sbuild
와 같은 명령을 통해 그룹을 지정해주어야 정상적으로 사용이 가능합니다.
다음으로 chroot 환경을 만들어야 합니다. 패키징 하려는 배포판 버전별로 아래 명령을 통해 생성합니다.
mk-sbuild jammy
mk-sbuild focal
생성된 chroot 이름은 기본으로 이름 뒤에 -{arch}
가 추가됩니다. 위의 명령을 예로 들면, jammy-amd64
, focal-amd64
이렇게 chroot 이름을 가지게 됩니다.
아래와 같이 --target
을 지정하면 cross-compile 환경도 만들 수 있습니다.
mk-sbuild --target armhf jammy
mk-sbuild --target arm64 jammy
cross-compile 환경으로 생성했을 경우 chroot 이름에 -{target}
이 추가됩니다. jammy-amd64-armhf
, jammy-amd64-arm64
이렇게 생성한 chroot 환경을 golden-image라고 부르고, 아래 명령으로 직접 chroot snapshot 환경으로 들어갈 수 있습니다.
# 현재 사용자
schroot -c jammy-amd64
# root 사용
schroot -c jammy-amd64 -u root
참고로, 위 환경에서 파일을 만들고 수정해도 exit
로 나갔다가 다시 들어가면 모두 초기화됩니다. 영구적으로 변경하려면 아래와 같이 golden-image를 사용해서 접근해야 합니다.
sudo schroot -c source:jammy-amd64 -u root
mk-sbuild
로 chroot 이미지를 만든지 너무 오래되서 sbuild로 패키징을 수행할 때마다 apt update
및 apt upgrade
에 시간이 많이 소요된다면, 아래 명령을 통해 chroot 이미지에 설치된 패키지들을 최신으로 업그레이드 할 수 있습니다.
sudo sbuild-update --upgrade jammy-amd64
위에서 sbuild를 위한 chroot 준비가 모두 끝났으면, 이제 sbuild 명령으로 deb 패키지를 만들 수 있습니다.
먼저 ALN 소스코드를 다운로드 받고 배포판에 맞는 debian 디렉토리를 설정합니다.
# 소스 다운로드
git clone https://github.com/webispy/aln.git
cd aln
# debian 패키지 디렉토리 준비 (from packaging/jammy, packaging/focal)
cp -a packaging/jammy debian
이제 sbuild 명령으로 패키지 빌드를 수행합니다.
cd aln
sbuild --chroot jammy-amd64
앞에 mk-sbuild 과정에서 cross-compile을 위한 chroot를 만들었다면 아래 명령으로 패키지 빌드를 수행할 수 있습니다.
sbuild --chroot jammy-amd64-armhf --host armhf
일반적으로 CI/CD 환경 구성을 위해 docker 환경과 Github action을 많이 사용하는데, 패키징을 검증하기 위해 docker 안에서 sbuild를 실행하거나 Github Action runner안에서 sbuild를 실행 할 수 있습니다. 아무 이슈 없이 정상적으로 실행되면 좋겠지만, sbuild에서 overlayfs를 사용해서 chroot를 접근하기 때문에 때때로 문제가 발생합니다.
이를 해결하기 위해서는 기본으로 docker container 실행시 --privileged
옵션이 필요하고, overlayfs로 사용되는 /var/lib/schroot
처리 방법에 따라 다음과 2가지 해결 방법이 있습니다.
- overlayfs로 사용되는
/var/lib/schroot
경로를-v
옵션을 통해 volume으로 설정:
$ docker run -it --rm -v /var/lib/schroot --privileged ubuntu:latest
root: # apt update 및 sbuild 설치, 사용자 추가, sbuild 그룹 및 sudoers 설정...
user: mk-sbuild jammy
user: git clone https://github.com/webispy/aln.git
user: cd aln && cp -a packaging/jammy debian
user: sbuild -c jammy-amd64
- schroot 내부에서
tmpfs
를 사용하도록 설정:
$ docker run -it --rm --privileged ubuntu:latest
root: # apt update 및 sbuild 설치, 사용자 추가, sbuild 그룹 및 sudoers 설정...
root: vi /etc/schroot/setup.d/04tmpfs
#!/bin/sh
set -e
. "$SETUP_DATA_DIR/common-data"
. "$SETUP_DATA_DIR/common-functions"
. "$SETUP_DATA_DIR/common-config"
if [ "$STAGE" = "setup-start" ]; then
mount -t tmpfs overlay /var/lib/schroot/union/overlay
elif [ "$STAGE" = "setup-recover" ]; then
mount -t tmpfs overlay /var/lib/schroot/union/overlay
elif [ "$STAGE" = "setup-stop" ]; then
umount -f /var/lib/schroot/union/overlay
fi
root: chmod +x /etc/schroot/setup.d/04tmpfs
user: mk-sbuild jammy
user: git clone https://github.com/webispy/aln.git
user: cd aln && cp -a packaging/jammy debian
user: sbuild -c jammy-amd64
ALN에서는 Github Action을 통해 CI/CD를 사용하는데, Runner 안에서 tmpfs를 설정하여 사용하고 있습니다. 자세한 내용은 ALN의 Github Action 스크립트(https://github.com/webispy/aln/blob/master/.github/workflows/ci.yaml)를 참고 바랍니다.
지금까지 deb 패키지를 만드는 과정에 대해 알아보았습니다. deb 패키징은 처음에는 복잡해 보일 수 있지만, 각 단계를 이해하고 실습해보면 충분히 익힐 수 있는 과정입니다. 이 글을 참고하여 직접 패키지를 만들어보는 것도 좋은 경험이 될 것입니다.
다음 글에서는 만들어진 deb 패키지를 사용자들에게 효과적으로 배포하는 방법에 대해 알아보겠습니다. 자체 apt 저장소 구축, PPA(Personal Package Archive)를 이용한 배포, 그리고 패키지 서명 등 배포 과정에서 고려해야 할 중요한 사항들을 다룰 예정입니다.