본문 바로가기
Study

[Linux] 모던 리눅스 교과서 6장 - 애플리케이션, 패키지관리,컨테이너

by wwns 2024. 12. 15.
반응형

이번 장에서는 프로그램, 바이너리, 실행 파일인 애플리케이션, init 시스템, 컨테이너, 패키지관리 매커니즘에 대해 알아본다.

 

다음과 같은 관점에서 공부해보록 한다.

  • 프로세스, 데몬, 서비스의 관점에서 애플리케이션
  • 폐쇄망에서 패키지 관리 (자체적인 패키지 레포지토리 서버 운영)
  • 컨테이너 내부 동작

용어

 

프로그램

  • 일반적으로 리눅스가 메모리에 로드하고 실행할 수 있는 바이너리 파일 혹은 셸 스크립트를 일컫는다.
  • 이 엔티티를 부르는 또 다른 이름은 실행 파일 이다.
  • 실행 파일의 유형에 따라 그 실행을 정확히 무엇이 관리하는지가 결정된다.
  • 예를 들어 셸 스크립트는 셸이 해석하고 실행한다.

프로세스

  • 프로그램 기반의 실행 엔티티이며 슬립 상태가 아닌 한 메인 메모리에 로드되어 CPU나 I/O를 사용한다.

데몬

  • 데몬 프로세스 의 줄임말로 서비스 라고도 하며 다른 프로세스에 특정 기능을 제공하는 백그라운드 프로세스다.
  • 예를 들어 프린터 데몬을 사용하면 인쇄가 가능해진다.
  • 그 밖에도 웹 서비스, 로깅, 시간 등 매일 사용하는 유틸리티를 위한 온갖 데몬이 있다.

애플리케이션

  • 종속성을 포함한 프로그램, 사용자 인터페이스를 포함한 실질적인 프로그램이다.
  • 일반적으로는 검색 부터 업그레이드를 위한 설치, 제거에 이르기까지, 프로그램의 전체 수명주기, 구성, 데이터와 연관해 애플리케이션이라는 용어를 사용한다.

패키지

  • 프로그램과 구성을 포함한 파일을 말한다. 소프트웨어 애플리케이션을 배포하는데 사용된다.

패키지 관리자

  • 패키지 입력 값으로 받아 해당 콘텐츠의 사용자 지침에 따라 리눅스 환경에서 패키지를 설치, 업그레이드, 제거하는 프로그램이다.

공급망

  • 패키지를 기반으로 사용자가 애플리케이션을 찾고 사용할 수 있는 소프트웨어 생산자와 배포자의 모음을 뜻한다.

부팅

  • 리눅스를 사용할 수 있는 상태로 만드는 것을 목표로 하드웨어와 운영체제를 초기화하는 리눅스의 시작 시퀀스를 의미한다.
  • 여기에는 커널 로딩과 서비스(또는 데몬) 프로그램 시작도 포함된다.

리눅스 부트 프로세스

스템 부팅 프로세스의 목적은 커널을 메모리에 올리고 실행하는 것

 

1. 모던 환경에서 통일 확장 펌웨어 인터페이스 사양은 부팅 구성과 부팅 로더를 정의

이전 시스템에서는 이 단계에서 시동 자체 시험가 완료되면 기본 I/O 시스템이 하드웨어를 초기화하고 부트 로더에 제어 권을 넘김.

-> CPU가 펌웨어에 접근하여 UEFI 혹은 BIOS의 Code 영역을 실행하여 하드웨어를 초기화

 

BIOS 방식의 경우 하드웨어를 초기화하고 부팅 디바이스를 찾고 부트로더를 메모리에 올리는 작업이 들어간다.

 

UEFI의 경우

 

EFI 시스템 파티션(ESP)에서 부트로더를 로드함

 

UEFI 방식이 더 현대적인 기술이며, 파티션 테이블의 크기, 처리 방식, 단위 등의 발전으로 더 빠르다.


2. 부트 로더의 목표는 커널을 부트스트랩하는 것 부팅 매체에 따라 세부 사항은 약간 다르며 부트 로더의 옵션은 다양하고 현재 버전과 예전 레거시 모두 사용 가능하다.

GRUB (GRand Unified Bootloader)

  • 리눅스 시스템에서 가장 널리 사용되는 부트로더.
  • 주요 기능:
    • 커널 이미지를 메모리에 로드
    • 다중 운영체제 선택(듀얼 부팅)
    • 부팅 시 커널 파라미터 전달
  • GRUB의 단계:
    1. Stage 1: 부트로더가 MBR(Master Boot Record) 또는 GPT(Global Partition Table)에서 실행
    2. Stage 1.5(MBR의 경우): MBR과 첫번째 파티션 사이에 있는 블록(a.k.a MBR gap)에 저장된 core.img가 메모리에 로드되고 실행됨. core.img의 configuration 파일과 파일시스템을 위한 드라이버를 로드함
    3. Stage 2: GRUB 설정 파일(/boot/grub2/grub.cfg)을 읽고 커널과 초기 램디스크(initramfs)를 메모리에 로드

 

3. 커널을 일반적으로 압축된 형태로 /boot 디렉토리에 위치해 있음

-> 커널 단계는 커널을 추출해 메인 메모리에 로드하는 것

하위 시스템, 파일 시스템, 드라이버를 초기화하고 나면 커널은 init 시스템에 제어권을 넘기고 이로써 부팅 프로세스는 종료함.

커널 단계 

  • GRUB이 커널 이미지를 로드하면, 제어권이 커널로 넘어감
    • 하드웨어 디텍션 및 초기화:
      • CPU, 메모리, 디스크, 네트워크 인터페이스 등 하드웨어를 초기화.
    • 루트 파일시스템 마운트:
      • initramfs(초기 램 파일시스템)를 임시 루트 파일시스템으로 마운트.
    • 디바이스 드라이버 로드:
      • 하드웨어와 통신하기 위해 필요한 드라이버를 메모리에 로드.
    • 루트 파일시스템 변경:
      • initramfs에서 실제 루트 파일시스템(/)으로 전환.
    • 초기화 프로세스 시작:
      • PID 1 프로세스(init 또는 systemd)를 실행.

4. init 시스템은 시스템 전반에 걸쳐 데몬을 실행할 책임이 있다.

init 프로세스는 프로세스 계층 구조의 루트이며 프로세스 ID 값은 1을 가지며 PID 1 프로세스는 시스템 전원을 끌 때까지 실행된다

다른 데몬 실행을 담당하는 것 외에도 PID 1 프로세스는 전통적으로 고아 프로세스(좀비 -> 고아의 경우도 포함)도 처리한다.

전통적 Init 시스템 (SysVinit)

  • /etc/inittab 설정 파일을 기반으로 실행 레벨(runlevel)을 확인.
  • 해당 실행 레벨에 따라 필요한 서비스를 시작.
    • 예: runlevel 3(멀티유저, CLI), runlevel 5(멀티유저, GUI).

현대적 초기화 시스템 (Systemd)

  • 대부분의 최신 리눅스 배포판에서 사용하는 초기화 시스템.
  • 병렬화된 서비스 실행으로 부팅 속도 향상.
  • 유닛 파일(/etc/systemd/system/)을 기반으로 서비스 관리.
  • 주요 작업:
    1. 기본 대상(default target)을 확인. (예: multi-user.target, graphical.target)
    2. 대상에 필요한 서비스(유닛)를 병렬로 실행.
    3. 로깅 및 상태 관리를 위해 journalctl을 사용.

5. 일반적으로 이 이후에 환경에 따라 또 다른 사용자 공간 수준의 초기화가 진행된다.

  • 일반적으로는 터미널, 환경, 셸 초기화가 일어남 (사용자 공간)
  • 사용자 설정과 구성을 고려해 GUI가 있는 데스크톱 환경용 디스플레이 관리자, 그래픽 서버 등이 시작

systemd

systemd는 처음에 initd를 대체하는 init 시스템이었지만 최근에는 로깅, 네트워크 구성, 네트워크 시간 동기화의 같은 기능을 포함하는 강력한 관리자다.

이는 데몬과 해당 종속성을 정의하는 유연하고 이식 가능한 방법은 물론이고 구성을 제어하는 통일된 인터페이스도 제공한다.

 

systemd는 다음을 통해 이전 init 시스템의 단점을 해결.

  • 여러 배포판에서 리눅스의 시작을 관리할 수 있는 통일된 방법 제공
  • 빠르고 이해하기 쉬운 서비스 구성 구현
  • 모니터링, 리소스 사용 제어(cgroup), 내장된 감사(audit) 기능을 포함한 최신 제품군 제공

또한 init는 초기화 때 순서대로(즉 영숫자 순서로) 서비스를 시작하는 반면 systemd는 어느 서비스는 종속성만 충족되면 시작할 수 있으므로 시작 시간을 단축할 수 있는 잠재력이 있다.

무엇을 언제 어떻게 실행할지 systemd에 지시하는 방법은 유닛을 통해 이뤄진다.

 

유닛

systemd의 유닛은 기능 및 대상 리소스에 따라 의미 체계가 다른 논리 그룹이다. systemd는 대상 리소스에 따라 여러 유닛을 구분한다.

  • service 유닛 - 서비스나 애플리케이션을 관리하는 방법 설명
  • target 유닛 - 종속성 캡쳐
  • mount 유닛 - 마운트 지점 정의
  • timer 유닛 - 크론 작업 등에 대한 타이머 정의

이보다 중요도가 조금 낮은 유닛은 아래와 같다.

  • socket: 네트워크나 IPC 소켓을 설명
  • device: udev나 sysfs 파일 시스템용
  • automount: 자동 마운트 지점 구성
  • swap: 스왑 공간 설명
  • path: 경로 기반 활성화용
  • snapshot: 변경사항이 일어난 후 시스템의 현재 상태를 재구성할 수 있음
  • slice: cgroup과 연관됨
  • scope: 외부에서 생성된 시스템 프로세스 세트를 관리

systemd 유닛  예시

[Unit]
Description=Systemd Test Daemon
After=after-example.service
Requires=after-example.service

[Service]
Type=simple
ExecStart=/usr/local/bin/example-daemon.sh
Restart=on-failure

[Install]
WantedBy=multi-user.target

 

systemd에게 인식되려면 유닛은 한 파일로 직렬화돼야 한다. systemd는 여러 위치에서 유닛 파일을 찾는다. 가장 중요한 파일 경로 세 가지는 다음과 같다.

  • /lib/systemd/system: 패키지가 설치한 유닛
  • /etc/systemd/system: 시스템 관리자가 구성한 유닛
  • /run/systemd/system: 비지속적 런타임 수정사항

systemctl로 관리하기

systemd의 상호 작용해 서비스를 관리하기 위해 사용하는 도구가 systemctl다. 자주 사용하는 systemctl 명령 목록은 아래와 같다.

항목 유형

systemctl enable xxxx.service 서비스 활성화. 시작할 준비를 갖춤
systemctl daemon-reload 모든 유닛 파일을 다시 로드하고 전체 종속성 트리를 다시 생성함
systemctl start xxxx.service 서비스 시작
systemctl stop xxxx.service 서비스 정지
systemctl restart xxxx.service 서비스 정지 후 새로 시작
systemctl reload xxxx.service 서비스에 reload 명령을 보냄. restart로 돌아감
systemctl kill xxxx.service 서비스 실행 중지
systemctl status xxxx.service 일부 로그를 포함해서 서비스 상태에 대해 간단 요약

 

systemd가 관리하는 로그를 볼 수 있는 도구가 journalctl이라고 한다.

journalctl 사용법

1. 마지막 부팅 후 로그 보기
# journalctl -b

2. 오늘 날짜 로그 보기
# journalctl --since=today

3. 특정 기간별 로그 보기
# journalctl --since "2017-05-25 00:00:00" --until "2017-05-30 10:30:00"
# journalctl --since "1 hour ago"
# journalctl --since "2 days ago"

4. 특정 서비스 데몬 로그 보기
# journalctl -u sshd

5. 특정 이벤트 속성 조회
# journalctl -p crit

6. 특정 서비스데몬 및 속성과 날짜 로그 보기
# journalctl -u libvirtd --since=yesterday -p err

7. Error 로그 자세히 보기
# journalctl -p err -o verbose

8. 특정 이벤트 조회
# journalctl /sbin/crond

9. 밑에서부터 로그 보기
# journalctl -f
# journalctl -r -b

패키지 관리자

  • 폐쇄망의 경우 저장소를 담당하는 서버를 하나 만들고 인터넷망에서 사용하는 것과 비슷하게 해당 서버로 요청을 보내 패키지를 다운받는 구성을 한다.
  • 해당 설정(레드햇OS)은 /etc/yum.repos.d/{name}.repo 형식으로 생성하여 요청을 보내도록 설정할 수 있다.

  • 모던 방식의 패키지관리자를 사용하게 되면 의존성 관리, 크로스플랫폼 문제 등을 해결해줄 수 있다

컨테이너

우리는 컨테이너를 리눅스 네임스페이스, cgroup, 때때로 CoW 파일 시스템을 사용해 애플리케이션 수준의 종속성 관리를 제공하는 리눅스 프로세스 그룹으로 이해할 수 있다.

 

컨테이너의 접근 방식의 공통점은 네임스페이스, cgroup 같이 리눅스 커널이 제공하는 기본 빌등 블록을 이용해 사용자가 애플리케이션을 실행할 수 있게 한다는 점이다.

 

리눅스에서 컨테이너 자체는 새로운 것이 아니지만, 도커 덕분에 주류로 편입되었다.

도커는 기존 개념을 혁신하고 2가지 획기적인 요소를 도입했다.

 

하나는 컨테이너 이미지를 통해 패키징을 정의하는 표준화된 방법, 다른 한가지는 인간 친화적인 사용자 인터페이스이다.

도커에서 컨테이너 이미지가 정의되고 배포되는 방식과 컨테이너가 실행되는 방식은 현재 OCI 핵심 사양의 기반이 됐다.

 

3가지 핵심 OCI 컨테이너 사양은 다음과 같다.

  • 런타임 사양
    • 운영과 수명 주기 단계를 포함해 런타임이 지원해야 하는 것을 정의한다.
  • 이미지 형식 사양
    • 메타데이터와 계층을 기반으로 컨테이너 이미지가 구성되는 방식을 정의한다.
  • 배포 사양
    • 컨테이너 이미지가 전달하는 방식, 컨테이너와 관련해 저장소가 효과적으로 동작할 수 있는 방식을 정의한다.

컨테이너와 관련된 또 다른 개념은 불변성이다. 한 번 구성되면 사용 중에 변경할 수 없다는 의미다.

즉 변경하려면 새로운 구성과 새 리소스를 생성해야 한다.


리눅스 네임 스페이스

리눅스는 리소스에 대한 전역 뷰를 가지고 있다.

프로세스가 리소스에 대한 로컬 뷰를 가질 수 있도록 리눅스는 네임스페이스를 도입했다.

즉 리눅스 네임스페이스는 모두 리소스 가시성에 관한 것이며 운영체제 리소스의 다양한 측면을 격리하는 데 사용할수 있다.

 

이 맥락에서 격리란 대부분 프로세스가 무엇을 보는지와 관련이 있으며 보안 관점에서 볼 때 반드시 엄격한 경계는 아니다.

네임 스페이스를 생성하려면 다음과 같은 3가지 관련 시스템 콜을 원하는 대로 상황에 맞게 사용할 수 있다.

  • clone
    • 실행 컨텍스트의 일부를 부모 프로세스와 공유할 수 있는 자식 프로세스를 만드는 데 사용
  • unshare
    • 기존 프로세스에서 공유된 실행 컨텍스트를 제거하는 데 사용된다.
  • setns
    • 기존 프로세스를 기존 네임스페이스에 결합하는데 사용된다.

이 시스템 콜은 다양한 플래그를 매개변수로 사용하므로 새로 생성, 가입, 탈퇴하려는 네임스페이스를 세밀하게 제어할 수 있다.

리눅스 cgroup

네임스페이스가 가시성에 관한거싱라면 cgroup은 프로세스 그룹을 구성하는 메커니즘이다.

cgroup을 계층 구조와 함께 사용하면 시스템 리소스 사용을 제어할 수 있다. 또한 cgroup은 리소스 사용 추적을 할 수 있다.

 

예를 들어 프로세스가 RAM이나 CPU 시간을 얼마나 사용중인지를 보여줄 수 있다.

cgroup은 선언형 유닛으로, 그리고 컨트롤러는 특정 리소스 제한을 적용하거나 사용량을 보고하는 커널 코드로 생각하자.

 

현재는 cgroup v1과 v2가 있다. 현재는 v1이 널리 사용되고 있지만 결국 v2가 이를 대체할 것이다.

쓰기 시 복사(Cow) 파일 시스템

컨테이너의 세 번째 빌딩 블록은 쓰기 시 복사 파일 시스템이다.

이들은 빌드 시 사용되며, 애플리케이션과 모든 종속성을 배포 가능한 독립 파일 하나로 패키징한다.

Cow 파일 시스템은 주로 바인드 마운트와 함께 사용되어 종속성이 서로 다른 콘텐츠를 효율적인 방식으로 계층화한다.

반응형