Linux From Scratch - Version 12.1-systemd
Chapter 8. Installing Basic System Software
이전	위로 / 처음으로	다음
개요	Man-pages-6.06

8.2. 패키지 관리

패키지 관리는 LFS Book에 자주 요청되는 기능입니다. 패키지 관리자는 파일 설치를 추적하여 패키지를 쉽게 제거하고 업그레이드할 수 있게 해줍니다. 또한 좋은 패키지 관리자는 패키지를 재설치하거나 업그레이드할 때 사용자 구성을 유지하기 위해 구성 파일을 특별히 처리합니다. 이 섹션에서는 특정 패키지 관리자에 대해 이야기하거나 추천하지 않습니다. 다만 가장 많이 사용되는 기술과 그 작동 방식에 대해 소개할 뿐입니다. 여러분에게 가장 적합한 패키지 관리자는 이러한 기법 중 하나이거나 두 가지 이상의 기법을 조합한 것일 수도 있습니다. 이 섹션에서는 패키지를 업그레이드할 때 발생할 수 있는 문제에 대해 간략하게 언급합니다.

LFS 또는 BLFS에 패키지 관리자가 언급되지 않은 몇 가지 이유는 다음과 같습니다.

패키지 관리를 다루면 이 책의 목표인 Linux 시스템 구축 방법을 가르치는 데 초점을 맞추기 힘들게 됩니다.

패키지 관리를 위한 여러 솔루션이 있으며, 각 솔루션에는 장단점이 있습니다. 모든 대상을 만족시키는 하나의 솔루션을 찾는 것은 어렵습니다.

패키지 관리 주제에 대한 몇 가지 힌트가 있습니다. 힌트 프로젝트를 방문하여 어떤 솔루션이 여러분의 필요에 맞는지 확인하세요.

8.2.1. 업그레이드 문제

패키지 관리자를 사용하면 최신 버전이 출시될 때 쉽게 업그레이드할 수 있습니다. 일반적으로 LFS 및 BLFS 책에 있는 지침을 사용하여 최신 버전으로 업그레이드할 수 있습니다. 다음은 특히 실행 중인 시스템에서 패키지를 업그레이드할 때 주의해야 할 몇 가지 사항입니다.

Linux 커널을 업그레이드해야 하는 경우(예: 5.10.17에서 5.10.18 또는 5.11.1로) 다른 것은 다시 빌드할 필요가 없습니다. 커널과 사용자 공간 사이에 잘 정의된 인터페이스 덕분에 시스템이 계속 정상적으로 작동합니다. 특히 Linux API 헤더는 커널과 함께 업그레이드할 필요가 없습니다. 업그레이드된 커널을 사용하려면 시스템을 재부팅하기만 하면 됩니다.

Glibc를 최신 버전으로 업그레이드해야 하는 경우(예: Glibc-2.36에서 Glibc-2.39로 업그레이드) 시스템 중단을 방지하기 위해 몇 가지 추가 단계가 필요합니다. 자세한 내용은 8.5, "Glibc-2.39"를 참조하세요.

공유 라이브러리가 포함된 패키지가 업데이트되고 라이브러리 이름이 변경되면 라이브러리에 동적으로 링크된 모든 패키지를 다시 컴파일하여 최신 라이브러리에 링크해야 합니다. (패키지 버전과 라이브러리 이름 사이에는 상관 관계가 없습니다.) 예를 들어 이름이 libfoo.so.1인 공유 라이브러리를 설치하는 foo-1.2.3 패키지를 생각해 봅시다. 이 패키지를 libfoo.so.2라는 이름의 공유 라이브러리를 설치하는 최신 버전 foo-1.2.4로 업그레이드한다고 가정합니다. 이 경우, 새 라이브러리 버전을 사용하려면 libfoo.so.1에 동적으로 링크된 모든 패키지를 다시 컴파일하여 libfoo.so.2에 링크해야 합니다. 모든 종속 패키지가 다시 컴파일될 때까지 이전 라이브러리를 제거해서는 안 됩니다.

패키지가 공유 라이브러리의 이전 이름과 새 이름 모두에 (직간접적으로) 링크되어 있는 경우(예: 패키지가 libfoo.so.2와 libbar.so.1 모두에 링크되어 있고 후자는 libfoo.so.3에 링크되어 있는 경우) 공유 라이브러리의 다른 개정판이 일부 기호 이름에 호환되지 않는 정의를 제시하므로 패키지가 오작동할 수 있습니다. 이 문제는 공유 라이브러리를 제공하는 패키지가 업그레이드된 후 이전 공유 라이브러리에 연결된 패키지의 일부(전부는 아님)를 다시 컴파일할 때 발생할 수 있습니다. 이 문제를 방지하려면 사용자는 공유 라이브러리에 연결된 모든 패키지를 가능한 한 빨리 업데이트된 버전(예: libfoo.so.2에서 libfoo.so.3으로)으로 다시 빌드해야 합니다.

공유 라이브러리가 포함된 패키지가 업데이트되었는데 라이브러리 이름은 변경되지 않고 라이브러리 파일의 버전 번호가 감소하는 경우(예: 라이브러리 이름은 여전히 libfoo.so.1이지만 라이브러리 파일 이름은 libfoo.so.1.25에서 libfoo.so.1.24로 변경됨) 이전에 설치된 버전(이 경우 libfoo.so.1.25)에서 라이브러리 파일을 제거해야 합니다. 그렇지 않으면 명령줄에서 직접 호출하거나 일부 패키지를 설치하여 호출한 ldconfig 명령이 심볼릭 링크 libfoo.so.1이 이전 라이브러리 파일을 가리키도록 재설정되므로 버전 번호가 더 큰 “최신” 버전인 것처럼 보입니다. 패키지를 다운그레이드해야 하거나 작성자가 라이브러리 파일의 버전 관리 체계를 변경하는 경우 이러한 상황이 발생할 수 있습니다.

공유 라이브러리가 포함된 패키지가 업데이트되고 라이브러리 이름은 변경되지 않았지만 심각한 문제(특히 보안 취약점)가 수정된 경우 공유 라이브러리에 연결된 실행 중인 모든 프로그램을 다시 시작해야 합니다. 업데이트가 완료된 후 root로 실행하는 다음 명령은 해당 라이브러리의 이전 버전을 사용하는 프로세스를 나열합니다(libfoo를 라이브러리 이름으로 바꾸세요).
시스템에 액세스하는 데 OpenSSH를 사용 중이고 업데이트된 라이브러리에 연결되어 있는 경우, sshd 서비스를 다시 시작한 다음 로그아웃하고 다시 로그인한 후 앞의 명령을 다시 실행하여 삭제된 라이브러리를 사용하는 것이 없는지 확인해야 합니다.
systemd 데몬(PID 1로 실행 중)이 업데이트된 라이브러리에 연결되어 있는 경우 root 사용자로 systemctl daemon-reexec을 실행하여 재부팅하지 않고도 다시 시작할 수 있습니다.

실행 프로그램이나 공유 라이브러리를 덮어쓰면 해당 프로그램이나 라이브러리의 코드나 데이터를 사용하는 프로세스가 충돌할 수 있습니다. 프로세스 충돌을 일으키지 않고 프로그램이나 공유 라이브러리를 업데이트하는 올바른 방법은 먼저 해당 프로그램이나 라이브러리를 제거한 다음 새 버전을 설치하는 것입니다. coreutils에서 제공하는 install 명령은 이미 이 기능을 구현하고 있으며 대부분의 패키지는 이 명령을 사용하여 바이너리 파일과 라이브러리를 설치합니다. 따라서 대부분의 경우 이 문제로 인해 어려움을 겪지 않을 것입니다. 하지만 일부 패키지(특히 BLFS의 SpiderMonkey)의 설치 프로세스는 파일이 있는 경우 해당 파일을 덮어쓰기 때문에 충돌이 발생할 수 있습니다. 따라서 패키지를 업데이트하기 전에 작업을 저장하고 실행 중인 불필요한 프로세스를 닫는 것이 더 안전합니다.

8.2.2. 패키지 관리 기법

다음은 몇 가지 일반적인 패키지 관리 기법입니다. 패키지 관리자를 결정하기 전에 다양한 기법, 특히 각 기법의 단점에 대해 조사해 보세요.

8.2.2.1. 모든 것이 내 머릿속에 있다!

네, 이것은 패키지 관리 기법입니다. 어떤 사람들은 패키지를 잘 알고 있고 각 패키지가 어떤 파일을 설치하는지 알고 있기 때문에 패키지 관리자가 필요하지 않습니다. 패키지가 변경될 때마다 전체 시스템을 다시 빌드할 계획이기 때문에 패키지 관리가 필요 없는 사용자도 있습니다.

8.2.2.2. 별도의 디렉터리에 설치

이 방법은 패키지를 관리하기 위한 특별한 프로그램이 필요 없는 간단한 패키지 관리 기법입니다. 각 패키지는 별도의 디렉터리에 설치됩니다. 예를 들어 패키지 foo-1.1은 /opt/foo-1.1에 설치되고 /opt/foo에서 /opt/foo-1.1로 심볼릭 링크가 만들어집니다. 새 버전 foo-1.2가 나오면 /opt/foo-1.2에 설치되고 이전 심볼릭 링크는 새 버전에 대한 심볼릭 링크로 대체됩니다.

PATH, MANPATH, INFOPATH, PKG_CONFIG_PATH, CPPFLAGS, LDFLAGS와 같은 환경 변수와 구성 파일 /etc/ld.so.conf는 /opt/foo-x.y의 해당 하위 디렉터리를 포함하도록 확장해야 할 수 있습니다.

이 방식은 BLFS 책에서 매우 큰 패키지를 설치하여 쉽게 업그레이드할 수 있도록 하는 데 사용됩니다. 패키지를 몇 개 이상 설치하면 이 체계를 관리할 수 없게 됩니다. 그리고 일부 패키지(예: Linux API 헤더 및 Glibc)는 이 체계에서 제대로 작동하지 않을 수 있습니다. 이 체계를 시스템 전체에 사용하지 마세요.

8.2.2.3. 심볼릭 링크 스타일 패키지 관리

이 방식은 이전 패키지 관리 기법의 변형된 방식입니다. 각 패키지는 이전 방식과 동일하게 설치됩니다. 그러나 일반 패키지 이름을 통해 심볼릭 링크를 만드는 대신 각 파일을 /usr 계층 구조로 심볼릭 링크합니다. 이렇게 하면 환경 변수를 확장할 필요가 없습니다. 심볼릭 링크는 사용자가 직접 만들 수도 있지만, 많은 패키지 관리자가 이 방식을 사용하여 심볼릭 링크 생성을 자동화합니다. 널리 사용되는 몇 가지로는 Stow, Epkg, Graft, Depot 등이 있습니다.

설치 스크립트를 속여야 하므로 실제로는 /usr/pkg 계층 구조에 설치되어 있지만 패키지는 /usr에 설치되었다고 생각합니다. 이러한 방식으로 설치하는 것은 일반적으로 간단한 작업이 아닙니다. 예를 들어 libfoo-1.1 패키지를 설치한다고 가정해 보겠습니다. 다음 명령어를 사용하면 패키지가 제대로 설치되지 않을 수 있습니다. 설치는 작동하지만 종속 패키지가 예상대로 libfoo에 링크되지 않을 수 있습니다. libfoo에 링크하는 패키지를 컴파일하면 예상대로 /usr/lib/libfoo.so.1이 아닌 /usr/pkg/libfoo/1.1/lib/libfoo.so.1에 링크되어 있는 것을 볼 수 있습니다. 올바른 접근 방식은 DESTDIR 변수를 사용하여 설치를 지시하는 것입니다. 이 방법은 다음과 같이 작동합니다. 대부분의 패키지는 이 방법을 지원하지만 일부 패키지는 지원하지 않습니다. 호환되지 않는 패키지의 경우 수동으로 패키지를 설치해야 하거나 문제가 있는 일부 패키지를 /opt에 설치하는 것이 더 쉬울 수 있습니다.

8.2.2.4. 타임스탬프 기반

이 기법에서는 패키지를 설치하기 전에 파일에 타임스탬프를 찍습니다. 설치 후 적절한 옵션과 함께 find 명령을 사용하면 타임스탬프 파일이 생성된 후 설치된 모든 파일의 로그를 생성할 수 있습니다. 이 방식을 사용하는 패키지 관리자는 install-log입니다.

이 방식은 간단하다는 장점이 있지만 두 가지 단점이 있습니다. 설치 중에 파일이 현재 시간이 아닌 다른 타임스탬프로 설치되면 패키지 관리자가 해당 파일을 추적할 수 없습니다. 또한 이 방식은 패키지가 한 번에 하나씩 설치될 때만 사용할 수 있습니다. 두 개의 다른 콘솔에서 두 개의 패키지가 동시에 설치되는 경우 로그는 신뢰할 수 없습니다.

8.2.2.5. 설치 스크립트 추적

이 접근 방식에서는 설치 스크립트가 수행하는 명령이 기록됩니다. 두 가지 기법을 사용할 수 있습니다.

설치 전에 미리 로드할 라이브러리를 가리키도록 LD_PRELOAD 환경 변수를 설정할 수 있습니다. 설치하는 동안 이 라이브러리는 cp, install, mv 등 다양한 실행 파일에 자신을 첨부하고 파일 시스템을 수정하는 시스템 호출을 추적하여 설치 중인 패키지를 추적합니다. 이 접근 방식이 작동하려면 모든 실행 파일이 suid 또는 sgid 비트 없이 동적으로 링크되어야 합니다. 라이브러리를 미리 로드하면 설치 중에 원치 않는 부작용이 발생할 수 있습니다. 따라서 몇 가지 테스트를 수행하여 패키지 관리자가 아무 것도 깨뜨리지 않고 적절한 파일을 모두 로깅하는지 확인하는 것이 좋습니다.

또 다른 방법은 설치 스크립트를 실행하는 동안 이루어진 모든 시스템 호출을 기록하는 스트레이스를 사용하는 것입니다.

8.2.2.6. 패키지 아카이브 만들기

이 방식에서는 앞서 심볼릭 링크 스타일 패키지 관리 섹션에서 설명한 대로 패키지 설치가 별도의 트리로 위조됩니다. 설치가 완료되면 설치된 파일을 사용하여 패키지 아카이브가 생성됩니다. 그런 다음 이 아카이브를 사용하여 로컬 머신 또는 다른 머신에 패키지를 설치합니다.

이 접근 방식은 상용 배포판에 있는 대부분의 패키지 관리자에서 사용됩니다. 이 접근 방식을 따르는 패키지 관리자의 예로는 RPM(리눅스 표준 기본 사양(LSB)에 따라 요구됨), pkg-utils, 데비안의 apt, 젠투의 포티지 시스템 등이 있습니다. LFS 시스템에 이러한 스타일의 패키지 관리 방식을 채택하는 방법을 설명하는 힌트 에서 확인할 수 있습니다.

종속성 정보를 포함하는 패키지 파일을 만드는 것은 복잡하며 LFS의 범위를 벗어납니다.

Slackware는 패키지 아카이브에 tar 기반 시스템을 사용합니다. 이 시스템은 의도적으로 더 복잡한 패키지 관리자처럼 패키지 종속성을 처리하지 않습니다. 자세한 내용은 Slackware 패키지 관리를 참조하세요.

8.2.2.7. 사용자 기반 관리

LFS에 고유한 이 체계는 Matthias Benkmann이 고안한 것으로, Hints 프로젝트에서 사용할 수 있습니다. 이 방식에서는 각 패키지가 표준 위치에 별도의 사용자로 설치됩니다. 패키지에 속한 파일은 사용자 ID를 확인하여 쉽게 식별할 수 있습니다. 이 접근 방식의 특징과 단점은 이 섹션에서 설명하기에는 너무 복잡합니다. 자세한 내용은 힌트를 참조하세요.

8.2.3. 여러 시스템에 LFS 배포하기

LFS 시스템의 장점 중 하나는 디스크 시스템의 파일 위치에 따라 달라지는 파일이 없다는 것입니다. 기본 시스템과 동일한 아키텍처를 가진 다른 컴퓨터에 LFS 빌드를 복제하려면 루트 디렉터리(기본 LFS 빌드의 경우 압축되지 않은 약 900MB)가 포함된 LFS 파티션에 tar를 사용하고 네트워크 전송 또는 CD-ROM/USB 스틱을 통해 해당 파일을 새 시스템으로 복사하고 확장하기만 하면 됩니다. 그 후 몇 가지 구성 파일을 변경해야 합니다. 업데이트해야 할 수 있는 구성 파일은 다음과 같습니다: /etc/hosts, /etc/fstab, /etc/passwd, /etc/group, /etc/shadow 및 /etc/ld.so.conf.

시스템 하드웨어와 원래 커널 구성의 차이에 따라 새 시스템에 사용자 지정 커널이 필요할 수 있습니다.

참고 유사하지만 동일하지 않은 아키텍처 간에 복사할 때 문제가 발생한다는 보고가 있었습니다. 예를 들어, 인텔 시스템의 명령어 세트는 AMD 프로세서의 명령어와 동일하지 않으며, 일부 프로세서의 최신 버전은 이전 버전에서 사용할 수 없는 명령어를 제공할 수 있습니다.

마지막으로 10.4절 "GRUB을 사용하여 부팅 프로세스 설정하기"를 통해 새 시스템을 부팅할 수 있도록 설정해야 합니다.