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를 라이브러리 이름으로 바꾸세요).
grep -l 'libfoo.*deleted' /proc/*/maps | tr -cd 0-9\\n | xargs -r ps u
시스템에 액세스하는 데 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 패키지를 설치한다고 가정해 보겠습니다. 다음 명령어를 사용하면 패키지가 제대로 설치되지 않을 수 있습니다.
./configure --prefix=/usr/pkg/libfoo/1.1
make
make install
설치는 작동하지만 종속 패키지가 예상대로 libfoo에 링크되지 않을 수 있습니다. libfoo에 링크하는 패키지를 컴파일하면 예상대로 /usr/lib/libfoo.so.1이 아닌 /usr/pkg/libfoo/1.1/lib/libfoo.so.1에 링크되어 있는 것을 볼 수 있습니다. 올바른 접근 방식은 DESTDIR 변수를 사용하여 설치를 지시하는 것입니다. 이 방법은 다음과 같이 작동합니다.
./configure --prefix=/usr
make
make DESTDIR=/usr/pkg/libfoo/1.1 install
대부분의 패키지는 이 방법을 지원하지만 일부 패키지는 지원하지 않습니다. 호환되지 않는 패키지의 경우 수동으로 패키지를 설치해야 하거나 문제가 있는 일부 패키지를 /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을 사용하여 부팅 프로세스 설정"을 통해 새 시스템을 부팅 할 수 있도록 설정해야 합니다.