용어 정리
프로그램
- 일반적으로 리눅스가 메모리에 로드하고 실행할 수 있는 바이너리 파일 혹은 셸 스크립트를 일컫는다.
- 프로그램 기반의 실행 엔티티이며ㅑ 슬립 상태가 아닌 한 메인 메모리에 로드되어 CPU나 I/O를 사용한다.
데몬
- 데몬 프로세스의 줄임말로 서비스라고도 하며 다른 프로세스에 특정 기능을 제공하는 백그라운드 프로세스이다.
- 예를 들어 프린터 데몬을 사용하면 인쇄가 가능해진다.
- 그 밖에도 웹 서비스, 로깅, 시간 등 매일 사용하는 유틸리티를 위한 다양한 데몬이 존재한다.
애플리케이션
- 종속성을 포함한 프로그램. 사용자 인터페이스를 포함한 실질적인 프로그램이다.
- 일반적으로 검색부터 업그레이드를 위한 설치, 제거에 이르기까지, 프로그램의 전체 수정주기, 구성, 데이터와 연관하여 애플리케이션이라는 용어를 사용한다.
패키지
- 프로그램과 구성을 포함한 파일을 말한다. 소프트웨어 애플리케이션을 배포하는데 사용된다.
패키지 관리자
- 패키지를 입력값으로 받아 해당 콘텐츠와 사용자 지침에 따라 리눅스 환경에서 패키지를 설치, 업그레이드, 제거하는 프로그램이다.
공급망
- 패키지를 기반으로 사용자가 애플리케이션을 찾고 사용할 수 있는 소프트웨어 생산자와 배포자의 모음을 뜻한다.
부팅
- 리눅스를 사용할 수 있는 상태로 만드는 것을 목표로 하드웨어와 운영체제를 초기화하는 리눅스의 시작 시퀀스를 말한다.
- 여기에는 커널 로딩과 서비스 프로그램 시작도 포함된다.
리눅스 시작 프로세스
- 리눅스 부트 프로세스는 일반적으로 하드웨어와 커널이 함께 작동하는 여러 단계에 걸친 작업이다.
- 모던 환경에서 UEFI 사양은 부팅 구성과 부팅 로더를 정의한다.
- 이전 시스템에서는 이 단계에서 POST가 완료되면 기본 I/O하드웨어를 초기화하고, 부트 로더에 제어권을 넘겨준다.
- 부트 로더의 목표는 커널을 부트스트랩하는 것이다. 부팅 매체에 따라 세부사항은 다를 수 있다.
- 부트 로더의 옵션은 다양하며 현재 버전과 이전 레거시 모두 사용할 수 있다.
- 커널은 일반적으로 압축된 형태로 /boot 디렉토리에 위치해 있다. 따라서 첫 번째 단계는 커널을 추출해 메인 메모리에 로드하는 것이다. 하위 시스템, 파일 시스템, 드라이버를 초기화 하고 나면 커널은 init 시스템에 제어권을 넘기고 이로써 부팅 프로세스는 적절하게 종료된다.
- init 시스템은 시스템 전반에 걸쳐 데몬을 실행할 책임이 있다. 이 init프로세스는 프로세스 계층 구조의 루트이며 프로세스 ID값은 1을 가진다. 즉 PID 1 프로세스는 전통적으로 고아 프로세스도 처리한다.
- 일반적으로 이 이후에 환경에 따라 또 다른 사용자 공간 수준의 초기화가 진행된다.
- 이전에 설명한 것처럼 일반적으로 터미널, 환경, 셸 초기화가 일어난다.
- 사용자 설정과 구성을 고려해 GUI가 있는 데스크톱 환경용 디스플레이 관리자, 그래픽 서버 등이 시작된다.
- 사용 가능한 init 시스템들을 비교한 좋은 젠투(Gentoo)위키 페이지가 있다.
systemd
- systemd는 처음에 initd를 대체하는 init 시스템이었지만 최근에는 로깅, 네트워크 구성, 네트워크 시간 동기화와 같은 기능을 포함하는 강력한 관리자이다.
- 특히 systemd는 다음을 통해 이전 init 시스템의 단점을 해결한다.
- 여러 배포판에서 리눅스의 시작을 관리할 수 있는 통일된 방법 제공
- 빠르고 이해하기 쉬운 서비스 구성 구현
- 모니터링, 리소스 사용 제어, 내장된 감사 기능을 포함한 최신 관리 제품군 제공
- 무엇을 언제 어떻게 실행할지 systemd에 지시하는 방법은 유닛을 통해 이뤄진다.
유닛
- systemd의 유닛은 기능 및 대상 리소스에 따라 의미 체계가 다른 논리 그룹이다.
- 유닛의 종류
- service 유닛 : 서비스나 애플리케이션을 관리하는 방법 설명
- target 유닛 :종속성 캡처
- mount 유닛 : 마운트 지점 정의
- timer 유닛 : 크론 작업 등에 대한 타이머 정의
- 이보다 조금 중요도가 낮은 유닛의 유형으로는 다음과 같은 것이 있다
- socket : 네트워크나 IPC 소켓을 설명
- device : udev나 sysfs 파일시스템 전용
- automount : 자동 마운트 지점 구성
- swap : 스왑 공간 설명
- path : 경로 기반 활성화용
- snapshot : 변경사항이 일어난 후 시스템의 현재 상태를 재구성할 수 있음
- slice : cgroup과 연관됨
- scope : 외부에서 생성된 시스템 프로세스 세트를 관리
- systemd에게 인식되려면 유닛은 한 파일로 직렬화돼야 한다.
- systemd는 여러 위치에서 유닛 파일을 찾는다. 가장 중요한 파일 경로 세가지는 다음과 같다.
- /lib/systemd/system : 패키지가 설치한 유닛
- /etc/systemd/system : 시스템 관리자가 구성한 유닛
- /run/systemd/system : 비지속적 런타임 수정사항
systemctl로 관리하기
- systemd와 상호 작용해 서비스를 관리하기 위해 사용하는 도구가 systemctl이다.
- 자주 사용하는 명령
명렁 | 사용케이스 |
---|---|
systemctl enable XXX.service | 서비스 활성화. 시작할 준비를 갖춤 |
systemctl daemon-reload | 모든 유닛 파일을 다시 로드하고 전체 종속성 트리를 다시 생성함 |
systemctl start XXX.service | 서비스 시작 |
systemctl stop xxx.service | 서비스 정지 |
systemctl restart xxx.service | 서비스 정지 후 재시작 |
systemctl reload xxx.service | 서비스에 reload 명령을 보냄. restart로 돌아감 |
systemctl kill xxx.service | 서비스 실행 중지 |
systemctl status xxx.service | 일부 로그를 포함해서 서비스 상태에 대해 간단 요약 |
- 종속성 관리와 쿼리, 전체 시스템 제어 등 이 표 이외에도 systemctl에는 많은 명령이 있다.
- systemd생태계에는 사용하기 편리하며 최소한 이름 정도는 알아둬야 하는 커맨드라인 도구가 많다.
- 다음은 그 중 일부이다.
- bootctl : 부트로더 상태를 확인하고 사용 가능한 부트로더를 관리할 수 있다.
- timedatectl : 시간과 날짜 관련 정보를 설정하고 볼 수 있다.
- coredumpctl : 저장된 코어 덤프를 처리할 수 있다. 문제 해결 시 이 도구를 사용해보자.
journalctl로 모니터링하기
- journal은 systemd의 구성요소다.
- 기술적으로 이는 systemd-journald 데몬이 관리하는 바이너리 파일로, systemd 구성요소가 기록하는 모든 메시지를 한곳에 모은 것이다.
- 추후에 자세히 언급할 것이다. 우선 지금은 systemd가 관리하는 로그를 볼 수 있는 도구가 journalctl이라는 점만 알아두면 된다.
예제: greeter 스케줄링
- 실제로 동작하는 systemd를 살펴본다.
- 간단한 사용 사례로 아래와 같은 greeter 앱을 매시간 시작한다고 가정해본다. ```bash #!/usr/bin/env bash
set -o errexit set -o errtrace set -o pipefail
name=”${1}”
if [ -z “$name”] then printf “You are awesome\n” else printf “Hello %, you are awesome!\n” ${name} fi
1
2
3
4
5
6
7
8
9
10
- 먼저 service 유형의 systemd 유닛 파일을 정의한다.
- 이 유닛은 systmed에게 greeter 앱을 어떻게 시작해야 하는지 알려준다.
```bash
[Unit]
Description=My Greeting Service
[Service]
Type=oneshot
ExecStart=/Users/inderby/bash-project
- 다음으로 매시간 greeter 서비스를 시작하도록 timer 유닛을 정의해준다. ```bash [Unit] Description=Runs Greeting service at th top of the hour
[Timer] OnCalendar=hourly
1
2
3
4
5
6
7
- 이제 systemd가 인식할 수 있도록 두 유닛 파일을 /run/systemd/system에 복사해준다.
- 해당 디렉터리에 timer 유닛을 복사해두면 systemd는 자동으로 이를 선택하기 대문에 이제 greeter 타이머를 사용할 수 있게 되었다.
- 참고 : 우분투 같은 데비안 기반 시스템은 기본적으로 service 유닛을 활성화하고 시작한다. 하지만 레드햇 계열 시스템은 명시적인 systemctl start greeter.timer 명령 없이는 서비스를 시작하지 않는다.
- greeter 타이머의 상태를 확인하는 명령어는 아래와 같다.
```bash
sudo systemctl status greeter.timer
- 동작 여부를 판별하기 위해서는 아래와 같은 명령어를 통해 로그를 확인할 수 있다.
1
journalctl -f -u greeter.service # journalctl을 이용해 greeter.service 유닛(-u로 선택)의 로그를 보고 따라가기(-f)
리눅스 애플리케이션 공급망
- 공급망(supply chain)이란 소비자에게 제품을 공급하는 조직이나 개인들의 시스템을 뜻한다.
리눅스 애플리케이션 공급의 세 가지 영역은 다음과 같다.
- 소프트웨어 유지 보수 담당자
- 소프트웨어 아티팩트를 생성해(예컨대 저장소에 패키지로) 올리는 개별 개발자, 오픈소스 프로젝트, 독립 소프트웨어 공급업체와 같은 회사가 포함된다.
- 저장소
- 앱의 일부 혹은 전체가 메타데이터와 함께 포함된 패키지가 있다.
- 일반적으로 패키지는 앱의 종속성도 캡처하며, 여기서 종속성이란 앱이 작동하기 위해 필요한 다른 패키지다.
- 이는 라이브러리, 일종의 내보내기나 가져오기, 또는 기타 서비스 프로그램일 수 있다.
- 이런 종속성을 최신 상태로 유지하기란 어렵다
- 도구(패키지 관리자)
- 대상 시스템 쪽에서 패키지 관리자를 사용해 저장소에 위치한 패키지를 조회하고, 설치하고, 업데이트하고, 삭제할 수 있다.
- 앱과 그 종속성을 나타내는 패키지는 하나 이상일 수 있다
전통적인 패키지 관리자, 컨테이너 기반 솔루션 등 패키지와 종속성 관리에 사용할 수 있는 옵션은 매우 다양하다.
- 패키지와 종속성 관리를 위한 세 가지 주요 카테고리 옵션은 아래와 같다.
- 전통적인 패키지 관리자
- 이 카테고리에서는 일반적으로 저수준 도구와 고수준 도구를 구분한다.
- 패키지 관리자가 종속성을 해결 할 수 있고 고수준 인터페이스(설치, 업데이트, 제거)를 제공하는 경우 이를 고수준 패키지 관리자라고 부른다.
- 컨테이너 기반 솔루션
- 이를 사용하기 좋은 곳이 (이것만이 주요 사례는 아니다)바로 애플리케이션 관리다.
- 즉 쉽게 테스트할 수 있고 프로덕션으로 바로 배포 가능한 앱을 간단하게 만들 수 있기 때문에 개발자는 컨테이너를 좋아한다.
- 최신 패키지 관리자
- 이들은 데스크톱 환경에 뿌리를 두고 있으며 여기서 주요 목표는 최종 사용자가 앱을 최대한 쉽게 사용하는 것이다.
패키지와 패키지 관리자
- 이절에서는 오랫동안, 어떤 경우에는 수십 년 동안 사용돼온 패키지 형식과 패키지 관리자에 대해 이야기한다.
- 이들은 보통 두가지 주요 리눅스 배포 제품군인 레드햇 계열(RHEL, 페도라, 센트OS 등)과 데비안 기반 시스템(데비안, 우분투 등)에서 유래된 것이다.
- 아래의 두 가지 개념에 관해 이야기 한다.
- 패키지 그 자체
- 엄밀히 말하면 압축된 파일 하나며, 메타데이터가 포함되어 있다.
- 도구(패키지 관리자라고 불림)
- 대상 시스템에서 해당 패키지를 처리해 앱을 설치하고 유지 관리한다.
- 패키지 관리자는 보통 사용자를 대신해 저장소와 상호 작용하고 패키지의 로컬 캐시를 유지 관리한다.
- 패키지 그 자체
RPM 패키지 관리자
- 원래 레드햇에서 만들었지만 현재는 다양한 배포판에서 널리 사용된다.
- .rpm 파일 형식은 리눅스 표준 베이스에서 사용되며 바이너리나 소스 파일을 포함할 수 있다.
- 패키지는 암호로 검증할 수 있으며 패치 파일을 통한 델타 업데이트를 지원할 수 있다.
- RPM을 사용하는 패키지 관리자는 다음과 같다.
- yum : 아마존 리눅스, 센트OS, 페도라, RHEL에서 사용
- DNF : 센트 OS, 페도라, RHEL에서 사용
- 자이퍼(Zypper) : 오픈수세(openSUSE)와 수세 리눅스 엔터프라이즈에서 사용
- RPM이 실제로 동작하는 모습
- 새로운 개발자 환경이 있고 yum을 사용해 go 프로그래밍 언어 툴 체인을 설치하려 한다고 가정해 보겠다. ```bash sudo yum search golang # 고 패키지를 검색한다.
sudo yum install golang # 고 패키지 설치
sudo yum info golang
1
2
3
4
5
6
7
8
9
10
11
12
### 데비안 deb
- deb패키지와 .deb 파일 형식은 데비안 배포판에서 시작됐으며, deb 패키지는 바이너리나 소스 파일도 포함할 수 있다.
- dpkg처럼 종속성 관리가 없는 저수준 패키지 관리자와 apt-get, apt, aptitude 같은 고수준의 패키지 관리자를 포함해 많은 패키지 관리자가 deb를 사용한다.
- 우분투가 데비안 기반의 배포판이라는 점을 감안할 때 deb 패키지는 데스크톱과 서버 모두에서 널리 사용된다.
- deb 패키지의 실제 동작
- apt로 curl 유틸리티를 설치한다고 가정해보자.
- apt는 HTTP API와 상호 작용하며 다양한 위치에서 파일을 다운로드하는 데 유용한 도구이다.
```bash
apt search curl # curl 패키지 검색
apt install curl # 설치
apt show curl # 검증
프로그래밍 언어별 패키지 관리자
- C/C++ : Conan과 vcpkg를 포함한 다양한 패키지 관리자가 있다.
- Go : 내장된 패키지 관리가 있다(go get, go mod)
- Node.js : npm 등이 있다.
- Java : maven과 nuts 등이 있다.
- Python : pip와 pyPM이 있다
- Ruby : rubygems와 Rails가 있다
- Rust : cargo가 있다.
컨테이너
- 이 책에서는 컨테이너를 리눅스 네임스페이스, cgroup, 때때로 CoW 파일시스템을 사용해 애플리케이션 수준의 종속성을 관리를 제공하는 리눅스 프로세스 그룹으로 이해한다.
- 하지만 컨테이너의 사용 사례는 로컬 테스트와 개발부터 분산 시스템 작업(예 : 쿠버네티스에서의 컨테이너화된 마이크로서비스 작업)에 이르기까지 다양하다.
- 개발자와 시스템 관리자에게는 컨테이너가 매우 유용하지만 최종 사용자에게는 더 고수준의 도구를 사용해 애플리케이션을 관리하는 쪽이 더 편할 것이다.
- 리눅스에서 컨테이너 자체는 새로운 것이 아니다.
- 하지만 도커 덕분에 컨테이너는 메이저 영억으로 편입됐다.
- 그전에는 개발자가 아닌 시스템 관리자를 대상으로 컨테이너를 도입하려는 여러 시도가 있었다.
- 리눅스 V서버, OpenVZ, LXC, lmctfy
- 이 모든 접근 방식의 공통점은 네임 스페이스, cgroup과 같이 리눅스 커널이 제공하는 기본 빌딩 블록을 사용해 사용자가 애플리케이션을 실행할 수 있게 한다는 점이다.
- 도커는 기존 개념을 혁신하고 2가지 획기적인 요소를 도입했다.
- 하나는 컨테이너 이미지를 통해 패키징을 정의하는 표준화된 방법
- 다른 한 가지는 인간 친화적인 사용자 인터페이스(docker run)이다.
- 도커에서 컨테이너 이미지가 정의되고 배포되는 방식과 컨테이너가 실행되는 방식은 현재 OCI 핵심 사양의 기반이 됐다.
- 3가지 핵심 OCI 컨테이너 사양
- 컨테이너와 관련된 또 다른 개념은 불변성이다.
- 한번 구성되면 사용 중에 변경할 수 없다는 의미다.
- 즉 변경하려면 새로운(정적) 구성과 새 리소스(프로세스)를 생성해야 한다.
리눅스 네임 스페이스
- 리눅스는 리소스에 대해 global view를 가지고 있었다.
- 프로세스가 리소스(예 : 파일 시스템, 네트워킹, 사용자)에 대한 local view를 가질 수 있도록 리눅스는 네임스페이스를 도입했다.
- 즉 리눅스 네임스페이스는 모두 리소스 가시성에 관한 것이며 운영체제 리소스의 다양한 측면을 격리하는 데 사용할 수 있다.
- 이 맥락에서 격리란 대부분 프로세스가 무엇을 보는지와 관련이 있으며 보안 관점에서 볼 때 반드시 엄격한 경계는 아니다.
- 네임스페이스를 생성하려면 다음과 같은 3가지 관련 시스템 콜을 원하는 대로 상황에 맞게 사용할 수 있다.
clone
: 실행 컨텍스트의 일부를 부모 프로세스와 공유할 수 있는 자식 프로세스를 만드는데 사용된다.unshare
: 기존 프로세스에서 공유된 실행 컨텍스트를 제거하는 데 사용된다.setns
: 기존 프로세를 기존 네임스페이스에 결함하는 데 사용된다.
- 이 시스템 콜은 다양한 플래스를 매개변수로 사용하므로 새로 생성, 가입, 탈퇴하려는 네임스페이스를 세밀하게 제어할 수 있다.
- CLONE_NEWNS : 파일시스템 마운트 지점에 사용한다. /proc/$PID/mounts를 통해 확인할 수 있다.
- CLONE_NEWUTS : 호스트 명과 도메인 이름 격리를 만들 때 사용한다. unname -n과 hostname -f를 통해 확인할 수 있다.
- CLONE_NEWIPC : 시스테V IPC 객체나 POSIX 메시지 큐 같은 프로세스 간 통신의 리소스 격리를 할 때 사용한다. /proc/sys/fs/mqueue, /proc/sys/kernel, /proc/sysvipc를 통해 확인할 수 있다.
- CLONE_NEWPID : PID 번호 공간 격리(PID 내부/PID 외부 네임스페이스)를 할 때 사용한다. /proc/$PID/status를 통해 세부 정보를 수집할 수 있다.
- CLONE_NEWNET : 네트워크 디바이스, IP 주소, IP 라우팅 테이블, 포트 번호 같은 네트워크 시스템 리소스의 가시성을 제어하는 데 사용한다. ip netns list, /proc/net, /sys/class/net을 통해 확인할 수 있다.
- CLONE_NEWUSER : 네임스페이스 내부/외부에서 UID + GID를 매핑하는 데 사용한다. id명령과 /proc/$PID/uid_map, /proc/$PID/gid_map을 통해 매핑을 조회할 수 있다.
- CLONE_NEWCGROUP : 네임 스페이스에서 cgroup을 관리하는 데 사용한다. /sys/fs/cgroup, /proc/cgroups, /proc/$PID/cgroup을 통해 확인할 수 있다.
리눅스 cgroup
- 네임 스페이스가 가시성에 관한 것이라면 cgroups은 다른 종류의 기능을 제공한다.
- 바로 프로세스 그룹을 구성하는 메커니즘이다.
- cgroup을 계층 구조와 함께 사용하면 시스템 리소스 사용을 제어할 수 있다.
- 또한 리소스 사용 추적을 할 수 있다.
- cgroups은 선언형 유닛으로, 그리고 컨트롤러는 특정 리소스 제한을 적용하거나 사용량을 보고하는 커널 코드로 생각하자.
- 현재 cgroup은 v1과 v2로 나뉘어 있다.
- 결국 v2가 v1을 대체할 것이긴하다.
cgroup v1
- cgroup v1을 이용하면 필요에 따라 새로운 cgroup과 컨트롤러를 추가할 수 있는 애드혹 접근 방식을 사용할 수 있다.
- v1 cgroups과 컨트롤러는 다음과 같다.
- CFS 대역폭 제어 : cpu cgroup을 통해 사용되며 지원된다.
- CPU 사용량 정산 컨트롤러 : cpuacct cgroup을 통해 사용되며 지원된다.
- cpusets cgroup : 작업에 cpu와 메모리를 할당할 수 있다.
- 메모리 리소스 컨트롤러 : 작업의 메모리 동작을 분리할 수 있다.
- 디바이스 화이트리스트 컨트롤러 : 디바이스 파일 사용을 제어할 수 있다.
- freezer cgroup : 배치 작업 관렝 사용된다.
- 네트워크 분류기 : 패킷에 다른 우선 순위를 할당하는데 사용됨
- 블록 IO 컨트롤러 : 블록 I/O를 조절할 수 있다.
- perf_event 명령 : 성능 데이터를 수집할 수 있다.
- 네트워크 우선순위 cgroup : 네트워크 트래픽의 우선 순위를 동적으로 설정할 수 있다.
- HugeTLB 컨트롤러 : HugeTLB 사용을 제한할 수 있다.
- 프로세스 번호 컨트롤러 : 특정 제한에 도달한 후 cgroup 계층이 새로 프로세스 생성을 중지 시킬 때 사용된다.
cgroup v2
- v1에서 배운 교훈을 바탕으로 cgroup 완전히 다시 작성한 것이다.
- cgroup의 일관된 구성과 사용은 물론이고, (중앙 집중식이고 균일한) 문서의 측면에서 봐도 그렇다.
- 프로세스 별로 구분되는 cgroup v1 설계와 달리, cgroup v2는 단일 계층 구조만 가지며 모든 컨트롤러가 동일한 방식으로 관리된다.
- v2 컨트롤러는 다음과 같다.
- CPU 컨트롤러 : CPU 주기의 분산을 제어한다. 다양한 모델(weight, max)을 지원하고 사용량 보고를 포함한다.
- 메모리 컨트롤러 : 사용자 공간 메모리, dentry와 inode같은 커널 데이터 구조, TCP 소켓 버퍼를 지원하는 다양한 제어 매개변수로 메모리 분산을 제어한다.
- I/O 컨트롤러 : 가중치 기반과 절대 대역폭, 혹은 IOPS 제한으로 I/O 리소스의 분산을 제어하고 바이트와 IOPS 읽기/쓰기에 대해 보고한다.
- 프로세스 번호(PID) 컨트롤러 : v1 버전과 유사
- cpuset 컨트롤러 : v1버전과 유사하다.
- device 컨트롤러 : eBPF 위에 구현된 디바이스 파일에 대한 접근을 관리한다.
- rdma 컨트롤러 : RDMA 리소스의 분산과 연산을 제어한다.
- HugeTLB 컨트롤러 : v1버전과 유사하다.
- 또한 v2에는 스칼라 리소스(다른 cgroup 리소스처럼 추상화 할 수 없음)에 대해 리소스 제한과 추적 메커니즘을 허용하는 기타 cgroup이 있다.
1
systemctl status # cgroup을 트리 형식으로 출력
쓰기 시 복사(CoW) 파일시스템
- 컨테이너의 세 번째 빌딩 블록은 쓰기 시 복사 파일시스템으로, 이들은 빌드 시 사용되며, 애플리케이션과 모든 종속성을 배포 가능한 독립 파일 하나로 패키징한다.
- CoW 파일 시스템은 주로 바인드 마운트와 함께 사용되어 종속성이 서로 다른 콘텐츠를 효율적인 방식으로 계층화한다.
도커
- 도커 사에서 개발하고 대중화한 인간 친화적 컨테이너 구현물이다.
- 도커를 사용하면 프로그램과 종속성을 쉽게 패키징하고 이를 데스크톱부터 클라우드까지 다양한 환경에서 실행할 수 있다.
- 도커의 독특한 점은 빌딩 블록(네임스페이스, cgroup, CoW 파일시스템, 바인드 마운트)이 아니다.
- 도커의 특별함은 이런 빌딩 블록을 결함할 때 네임스페이스와 cgroup 같은 저수준 비트 관리의 복잡성을 숨겨서 사요요하기 쉽게 만드는 방식을 썼다는 점이다.
- 도커에는 이미지와 실행 컨테이너라는 두 가지 주요 개념이 존재한다.
- 컨테이너 이미지 : 실질적인 디렉토리인 JSON 파일과 계층의 메타데이터를 포함하는 압축된 아카이브 파일이다.
- 도커 데몬은 필요에 따라 컨테이너 레지스트리에서 컨테이너 이미지를 가져온다.
- 런타임 아티팩트로서의 컨테이너 : 이 컨테이너는 필요에 따라 시작, 중지, 종료, 제거할 수 있다. 도커 데몬과의 상호 작용에는 클라이언트 CLI 도구를 사용하며, 이 CLI 도구는 데몬에 명령을 보내고 데몬은 컨테이너 빌드, 실행과 같은 작업을 각각 차례로 실행한다.
- 컨테이너 이미지 : 실질적인 디렉토리인 JSON 파일과 계층의 메타데이터를 포함하는 압축된 아카이브 파일이다.
- 자주 사용되는 도커 명령어
명령어 | 설명 | 사용 예 |
---|---|---|
run | 컨테이너 실행 | 엔진에스를 데몬으로 실행하고 종료시 컨테이너 제거: docker run -d --rm nginx:1.21 |
ps | 컨테이너 나열 | 모든 컨테이너 나열 : docker ps -a |
inspect | 저수준 정보 출력 | 컨테이너 IP 쿼리용: docker inspect -f '' |
build | 로컬에서 컨테이너 이미지 생성 | 현재 디렉터리와 태그를 기반으로 이미지 빌드: docker build -t some:tag . |
push | 컨테이너 이미지를 레지스트리로 업로드 | AWS 레지스트리로 푸시 : docker push public.ecr.aws/some:tag |
pull | 레지스트리에서 컨테이너 이미지를 다운로드 | AWS 레지스트리에서 풀 : docker pull public.ecr.aws/some:tag |
images | 로컬 컨테이너 이미지 나열 | 특정 레지스트리의 이미지 나열: docker images ubuntu |
image | 컨테이너 이미지 관리 | 사용하지 않는 모든 이미지 삭제: docker image prune -all |
컨테이너 이미지
- 컨테이너 이미지를 빌드하는 방법에 대한 지침을 정의할 때는 Dockerfile이라고 불리는 일반 텍스트 파일 형식을 사용한다.
- 도커 파일에 포함될 수 있는 지시문은 다양하다
- 기본 이미지 : 빌드/실행 단계에 대해 여러 개일 수 있음(
FROM
) - 메타 데이터 : 제작 정보(
LABEL
) - 인자와 환경변수 :
ARGS
,ENV
- 빌드타임 사양 : 계층별로 어떻게 이미지가 생성되었는지를 정의함: COPY, RUN 등
- 런타임 사양 : 컨테이너 실행 방법을 정의함:
CMD
,ENTRYPOINT
- 기본 이미지 : 빌드/실행 단계에 대해 여러 개일 수 있음(
docker build
명령을 사용하면 애플리케이션을 나타내는 파일 컬렉션(소스 또는 바이너리 형식)을 도커 파일과 함께 컨테이너 이미지로 전환한다.- 이 컨테이너 이미지는 직접 실행하거나 레지스트리에 등록함으로써, 달ㄴ 사람들이 내려받아 실행하게 배포할 수 있는 아티팩트다.
실행 컨테이너
- 컨테이너는 대화형 입력이나 데몬으로 실행될 수 있다.
- docker run 명령은 환경변수, 노출할 포트, 마운트할 볼륨 같은 런타임 입력 세트와 컨테이너 이미지를 사용한다.
- 이 정보를 사용해 도커는 필요한 네임스페이스와 cgroup을 생성하고 컨테이너 이미지에 정의된 애플리케이션을 시작한다.
에제; 컨테이너화한 greeter
1
2
3
4
5
6
7
8
9
10
11
12
13
# 베이스 이미지 지정
FROM ubuntu:20.04
# LABEL을 통해 일부 데이터를 할당한다
LABEL org.opencontainers.image.authors="Michael Hausenblas"
# 셸 스크립트를 복사한다. 이는 바이너리, JAR 파일, 파이썬 스크립트일 수 있다.
COPY greeter.sh /app/
# 작업 디렉터리를 지정한다.
WORKDIR /app
# 이 행과 다음 행은 앱을 실행하는 사용자를 정의한다. 이렇게 하지 않으면 불필요하게 root로 실행된다.
RUN chown -R 1001:1 /app
USER 1001
# 무엇을 실행할지 정의한다.
ENTRYPOINT ["/app/greeter.sh"]
다른 컨테이너 도구
- OCI 컨테이너 작업을 위해 도커를 무조건 사용할 필요는 없다.
- 그 대안으로 레드햇이 주도하고 후원하는 포드맨(podman)과 빌다(buildah)를 사용할 수 있다.
- 이렇게 데몬이 없는 도구를 사용해도 OCI 컨테이너 이미지를 빌드하고 실행할 수 있다.
- 또한 다음과 같이 OCI 컨테이너, 네임스페이스, cgroup 작업을 보다 쉽게 해주는 많은 도구가 있다.
- containerd : 이미지 전송과 저장에서부터 컨테이너 런타임 감시까지, OCI 컨테이너 수명주기를 관리하는 데몬
- skopeo : 컨테이너 이미지 조작을 위해 사용(복사, 매니페스트 검사 등)
- systemd-cgtop : cgroup을 인식하는 top의 변종이며 리소스 사용량을 대화식으로 보여준다.
- nsenter : 지정된 기존 네임 스페이스에서 프로그램을 실행할 수 있다.
- unshare : 특정 네임스페이스로 프로그램을 실행할 수 있다.(플래그를 통해 설정(혹은 등록)할 수 있다.)
- lsns : 리눅스 네임스페이스에 대한 정보를 나열한다.
- cinf : 프로세스 ID와 관련된 리눅스 네임스페이스와 cgroup에 대한 정보를 나열한다.
최신 패키지 관리자
- 배포에 특화된 경우가 대부분인 기존 패키지 관리자 외에도 새로운 종류의 패키지 관리자가 있다.
- 이런 최신 솔루션은 대부분 컨테이너를 사용하고 교차 배포나 대상이 특정된 환경을 목표로 한다.
- 예를 들어 리눅스 데스크톱 사용자가 GUI앱을 쉽게 설치할 수 있는 경우다.
- 종류
- Snap : 캐노니컬사가 설계하고 홍보하는 소프트웨어 패키징, 배포 시스템이다. 세련된 샌드박싱 설정과 함께 제공되며 데스크톱, 클라우드, IoT 환경에서 사용할 수 있다.
- Flatpak : cgroup, 네임스페이스, 바인드 마운트, seccomp을 빌딩 블록으로 사용하며 리눅스 데스크탑 환경에 최적화됐다. 처음에는 리눅스 배포판 세계의 레드의 일부로 시작됐지만 이제는 페도라, Mint, 우분투, 데비안, 오픈수세, 크롬을 포함한 수십 가지 배포판에서 사용할 수 있다.
- AppImage : 수년 동안 사용되어 왔으며 하나의 앱은 하나의 파일과 같다는 개념을 추구한다. 즉 대상 리눅스 시스템에 이미 포함된 종속성 외에는 추가 종속성이 필요하지 않다.
- 시간이 지남에 따라 데스크톱에 통합하기 위한 효율적인 업데이트부터 소프트웨어 카탈로그에 이르기까지 AppImage에 흥미로운 기능이 많이 도입됐다.
- Homebrew : 원래는 맥 OS 세계에서 나왔지만 리눅스에서도 사용할 수 있으며 점점 인기를 얻고 있다. 루비로 작성됐으며 강력하면서도 직관적인 사용자 인터페이스가 있다.
정리
- 이 장에서는 리눅스 애플리케이션을 설치, 유지 관리, 사용하는 방법과 관련된 다양한 주제를 다뤘다.
- 먼저 기본적인 애플리케이션 용어를 정의한 다음 리눅스 시작 프로세스를 살펴보고 시작과 구성요소를 관리하는 현재의 표준 방식인 systemd에 대해 설명했다.
- 리눅스는 애플리케이션을 배포하기 위해 패키지와 패키지 관리자를 사용한다.
- 이와 연관된 다양한 관리자에 대해 이야기하고 개발, 테스트, 종속성 관리에 컨테이너를 사용하는 방법에 대해 살펴봤다.
- 도커 컨테이너는 리눅스 기본 요소(cgroup, 네임스페이스, CoW 파일시스템)를 사용해요 애플리케이션 수준의 종속성 관리를 제공한다.
- 마지막으로 Snap 등 앱 관리를 위한 맞춤형 솔루션을 살펴봤다.