Systemd Cgroup 리소스 제한 및 격리 종합 가이드

systemd 및 cgroup으로 Linux 리소스 관리를 마스터하세요. 이 가이드는 systemd 슬라이스, 스코프 및 서비스를 사용하여 정밀한 CPU, 메모리, I/O 제한을 적용하는 방법을 설명합니다. 효과적인 성능 최적화를 위한 실용적인 예시와 모범 사례를 통해 중요 프로세스를 격리하고, 리소스 고갈을 방지하며, 시스템 안정성을 보장하는 방법을 배우세요.

37 조회수

시스템d cgroup를 활용한 리소스 제한 및 격리 종합 가이드

최신 리눅스 초기화 시스템이자 시스템 및 서비스 관리자인 systemd는 시스템 리소스를 관리하는 강력한 도구를 제공합니다. systemd의 가장 중요한 기능 중 하나는 제어 그룹(cgroups)과의 통합입니다. cgroups는 리눅스 커널 기능으로, 프로세스 모음의 리소스 사용량(CPU, 메모리, 디스크 I/O, 네트워크 등)을 제한, 계정 처리 및 격리할 수 있게 해줍니다. 이 가이드에서는 systemd가 슬라이스(slices), 스코프(scopes), 서비스(services)라는 단위 유형을 통해 cgroup을 어떻게 활용하여 정확한 리소스 제한 및 격리를 구현하는지 자세히 살펴봅니다. 이를 통해 중요한 프로세스가 필요한 리소스를 확보하고, 비정상적인 애플리케이션이 시스템 안정성에 영향을 미치는 것을 방지할 수 있습니다.

Systemd의 cgroup 통합을 이해하고 활용하는 것은 시스템 관리자, 개발자, 그리고 리눅스 시스템의 성능 및 안정성을 유지하는 모든 사람에게 매우 중요합니다. 적절한 리소스 제한을 설정하면 리소스 고갈을 방지하고, 애플리케이션 성능 예측 가능성을 높이며, 전반적인 시스템 안정성을 향상시킬 수 있습니다. 이 가이드에서는 이러한 제한을 구성하는 실용적인 접근 방식을 제공하여 복잡한 리소스 관리를 더 쉽고 효과적으로 만들 것입니다.

제어 그룹(cgroups) 이해하기

Systemd의 구현을 자세히 살펴보기 전에 cgroups의 기본 개념을 이해하는 것이 필수적입니다. Cgroups는 리눅스 커널의 계층적 메커니즘으로, 프로세스를 그룹화한 다음 이러한 그룹에 리소스 관리 정책을 할당할 수 있습니다. 이러한 정책에는 다음이 포함될 수 있습니다:

  • CPU: CPU 시간 제한, CPU 액세스 우선순위 지정.
  • 메모리: 메모리 사용량 제한 설정, 메모리 부족(OOM) 조건 방지.
  • I/O: 디스크 읽기/쓰기 작업 스로틀링.
  • 네트워크: 네트워크 대역폭 제한.
  • 장치 액세스: 특정 장치에 대한 액세스 제어.

커널은 일반적으로 /sys/fs/cgroup에 마운트된 가상 파일 시스템을 통해 cgroup 구성을 노출합니다. 각 컨트롤러(예: cpu, memory)는 자체 디렉토리를 가지며, 그 안에서 디렉토리 계층 구조는 그룹과 해당 리소스 제한을 나타냅니다.

Systemd의 Cgroup 관리 아키텍처

Systemd는 직접적인 cgroup 조작의 복잡성을 추상화하여 구조화된 단위 관리 시스템을 제공합니다. 프로세스를 단위의 계층 구조로 구성하고, 이를 cgroup 계층 구조에 매핑합니다. 리소스 관리와 관련된 주요 단위 유형은 다음과 같습니다:

  • 슬라이스 (Slices): 서비스 단위를 위한 추상 컨테이너입니다. 슬라이스는 계층 구조를 형성하여 리소스 위임을 가능하게 합니다. 예를 들어, 사용자 세션을 위한 슬라이스는 개별 애플리케이션을 위한 슬라이스를 포함할 수 있습니다. Systemd는 시스템 서비스, 사용자 세션, 가상 머신/컨테이너를 위한 슬라이스를 자동으로 생성합니다.
  • 스코프 (Scopes): 일반적으로 전체 서비스 단위로 관리되지 않는 사용자 세션 또는 시스템 서비스와 관련된 임시 또는 동적으로 생성된 프로세스 그룹에 사용됩니다. 스코프는 일시적이며 내부 프로세스가 실행되는 동안 존재합니다.
  • 서비스 (Services): 데몬 및 애플리케이션을 관리하는 기본 단위입니다. 서비스 단위가 시작되면 systemd는 일반적으로 슬라이스 내의 cgroup 계층 구조에 해당 프로세스를 배치합니다. 리소스 제한을 서비스 단위에 직접 적용할 수 있습니다.

Systemd의 기본 계층 구조는 다음과 같은 형태를 띕니다:

-.slice (루트 슬라이스)
  |- system.slice
  |  |- <서비스_이름>.service
  |  |- another-service.service
  |  ...
  |- user.slice
  |  |- user-1000.slice
  |  |  |- session-c1.scope
  |  |  |  |- <애플리케이션>.service (사용자가 시작한 경우)
  |  |  |  ...
  |  |  ...
  |  ... 
  |- machine.slice (VM/컨테이너용)
  ... 

Systemd 단위 파일로 리소스 제한 적용하기

Systemd를 사용하면 .service, .slice, 또는 .scope 단위 파일 내에서 직접 cgroup 리소스 제한을 지정할 수 있습니다. 이러한 지시문은 각각 [Service], [Slice], [Scope] 섹션 아래에 배치됩니다.

CPU 제한

CPU 리소스 제어를 위한 주요 지시문은 다음과 같습니다:

  • CPUQuota=: 단위가 사용할 수 있는 총 CPU 시간을 제한합니다. 이는 백분율(예: CPU 코어의 50%는 50%) 또는 CPU 코어의 분수(예: 0.5)로 지정됩니다. 또한 초당 마이크로초 단위로 값을 지정하는 것도 가능합니다. 기본 기간은 100ms입니다.
  • CPUShares=: CPU 시간에 대한 상대적 가중치를 설정합니다. CPUShares=1024인 단위보다 CPUShares=2048인 단위가 경쟁 시 두 배의 CPU 시간을 얻습니다.
  • CPUWeight=: CPUShares=의 별칭이지만 범위가 다릅니다(1-10000, 기본값 100).
  • CPUQuotaPeriodSec=: CPUQuota의 기간을 설정합니다. 기본값은 100ms입니다.

예시: 웹 서버를 하나의 CPU 코어의 75%로 제한하기:

서비스 파일을 생성하거나 수정합니다. 예를 들어, /etc/systemd/system/mywebapp.service:

[Unit]
Description=My Web Application

[Service]
ExecStart=/usr/bin/mywebapp
User=webappuser
Group=webappgroup

# CPU 코어 1개의 75%로 제한
CPUQuota=75%

[Install]
WantedBy=multi-user.target

서비스 파일을 생성하거나 수정한 후, systemd 데몬을 다시 로드하고 서비스를 다시 시작합니다:

sudo systemctl daemon-reload
sudo systemctl restart mywebapp.service

메모리 제한

메모리 제한은 다음과 같은 지시문으로 제어됩니다:

  • MemoryLimit=: 단위의 프로세스가 소비할 수 있는 RAM 양에 대한 하드 제한을 설정합니다. 바이트 단위 또는 K, M, G, T와 같은 접미사(예: 512M)로 지정할 수 있습니다.
  • MemoryMax=: MemoryLimit과 유사하지만, 메모리 계정 처리 방식과 관련하여 더 현대적이고 유연하다고 여겨집니다. 일반적으로 MemoryLimit보다 권장됩니다.
  • MemoryHigh=: 소프트 제한을 설정합니다. 이 제한에 접근하면 하드 제한이 아직 적용되지 않은 상태에서 메모리 회수(스왑)가 더 적극적으로 트리거됩니다.
  • MemorySwapMax=: 단위가 사용할 수 있는 스왑 공간의 양을 제한합니다.

예시: 데이터베이스를 2GB RAM으로 제한하기:

서비스 파일을 생성하거나 수정합니다. 예를 들어, /etc/systemd/system/mydb.service:

[Unit]
Description=My Database Service

[Service]
ExecStart=/usr/bin/mydb
User=dbuser
Group=dbgroup

# 메모리를 2기가바이트로 제한
MemoryMax=2G

[Install]
WantedBy=multi-user.target

다시 로드하고 다시 시작합니다:

sudo systemctl daemon-reload
sudo systemctl restart mydb.service

I/O 제한

I/O 스로틀링은 다음과 같은 지시문으로 제어할 수 있습니다:

  • IOWeight=: I/O 작업에 대한 상대적 가중치를 설정합니다. 값이 높을수록 I/O 우선순위가 높아집니다. 범위는 1에서 1000(기본값 500)입니다.
  • IOReadBandwidthMax=: 읽기 I/O 대역폭을 제한합니다. [<장치>] <초당 바이트> 형식으로 지정됩니다. 예를 들어, IOReadBandwidthMax=/dev/sda 100M/dev/sda에 대한 읽기 작업을 초당 100MB로 제한합니다.
  • IOWriteBandwidthMax=: 쓰기 I/O 대역폭을 제한합니다. IOReadBandwidthMax와 유사한 형식입니다.

예시: 특정 디스크에 대한 백그라운드 처리 서비스의 속도를 초당 50MB로 제한하기:

서비스 파일을 생성하거나 수정합니다. 예: /etc/systemd/system/batchproc.service:

[Unit]
Description=Batch Processing Service

[Service]
ExecStart=/usr/bin/batchproc
User=batchuser
Group=batchgroup

# /dev/sdb에 대한 쓰기 작업을 초당 50MB로 제한
IOWriteBandwidthMax=/dev/sdb 50M

# 적절한 읽기 우선순위 부여
IOWeight=200

[Install]
WantedBy=multi-user.target

다시 로드하고 다시 시작합니다:

sudo systemctl daemon-reload
sudo systemctl restart batchproc.service

Cgroup 관리 및 모니터링

Systemd는 단위와 관련된 cgroup을 검사하고 관리하는 도구를 제공합니다.

Cgroup 상태 검사

systemctl status 명령은 단위의 cgroup 멤버십 및 리소스 사용량에 대한 정보를 제공합니다.

systemctl status mywebapp.service

cgroup 경로를 나타내는 줄을 찾습니다. 예를 들어:

● mywebapp.service - My Web Application
     Loaded: loaded (/etc/systemd/system/mywebapp.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2023-10-27 10:00:00 UTC; 1 day ago
       Docs: man:mywebapp(8)
   Main PID: 12345 (mywebapp)
      Tasks: 5 (limit: 4915)
     Memory: 15.5M
        CPU: 2h 30m 15s
      CGroup: /system.slice/mywebapp.service
              └─12345 /usr/bin/mywebapp

직접 cgroup 파일 시스템을 검사할 수도 있습니다:

systemd-cgls # systemd가 관리하는 cgroup 계층 구조 표시
systemd-cgtop # top와 유사하지만 cgroup용

서비스의 cgroup에 적용된 특정 제한을 보려면:

# 메모리 제한의 경우
catsysfs /sys/fs/cgroup/memory/system.slice/mywebapp.service/memory.max

# CPU 제한의 경우
catsysfs /sys/fs/cgroup/cpu/system.slice/mywebapp.service/cpu.max

(참고: 정확한 경로 및 파일 이름은 cgroup 버전 및 시스템 구성에 따라 약간 다를 수 있습니다.)

Cgroup 제한 동적 수정

단위 파일에 제한을 설정하는 것이 모범 사례이지만, systemctl set-property를 사용하여 일시적으로 조정할 수 있습니다:

sudo systemctl set-property mywebapp.service CPUQuota=50%

이러한 변경 사항은 재부팅 후에도 유지되지 않습니다. 영구적으로 만들려면 단위 파일을 업데이트하고 systemd 데몬을 다시 로드해야 합니다.

리소스 위임을 위한 슬라이스

슬라이스는 서비스 또는 애플리케이션 그룹을 관리하는 데 유용합니다. 슬라이스에 리소스 제한을 정의하면 해당 슬라이스 내의 모든 서비스 또는 스코프가 해당 제한을 상속하거나 제약받게 됩니다.

예시: 리소스 집약적인 배치 작업을 위한 전용 슬라이스 생성:

슬라이스 파일을 생성합니다. 예: /etc/systemd/system/batch.slice:

[Unit]
Description=Batch Processing Slice

[Slice]
# 이 슬라이스의 모든 작업에 대한 총 CPU를 1코어로 제한
CPUQuota=100%
# 총 메모리를 4GB로 제한
MemoryMax=4G

이제 .service 파일의 Slice= 지시어를 사용하여 이 슬라이스 내에서 실행되도록 서비스를 구성할 수 있습니다:

[Unit]
Description=Specific Batch Job

[Service]
ExecStart=/usr/bin/mybatchjob

# 이 서비스를 batch.slice에 배치
Slice=batch.slice

[Install]
WantedBy=multi-user.target

Systemd를 다시 로드하고, 필요한 경우 슬라이스를 활성화/시작합니다(종종 암시적으로 활성화됨). 그런 다음 서비스를 시작합니다.

sudo systemctl daemon-reload
sudo systemctl start mybatchjob.service

이 접근 방식을 사용하면 관련 프로세스를 그룹화하고 집합적인 리소스 소비를 관리할 수 있습니다.

모범 사례 및 고려 사항

  • 점진적 제한 시작: 제한을 설정할 때는 보수적인 값으로 시작하여 필요에 따라 점진적으로 늘립니다. 과도한 제한은 애플리케이션을 불안정하게 만들 수 있습니다.
  • 모니터링: 시스템의 리소스 사용량과 cgroup 설정의 영향을 정기적으로 모니터링합니다. systemd-cgtop, htop, top, iotop과 같은 도구가 매우 유용합니다.
  • Cgroup v1 vs v2 이해: Systemd는 cgroup v1과 v2를 모두 지원합니다. 많은 지시문이 유사하지만, v2는 통합 계층 구조와 일부 동작 차이를 제공합니다. 복잡한 문제가 발생하는 경우 시스템이 어떤 버전을 사용하고 있는지 인지하고 있어야 합니다.
  • 우선순위 vs 하드 제한: 리소스가 부족할 때는 CPUShares/CPUWeight를 우선순위 지정에 사용하고, 엄격한 하드 제한에는 CPUQuota를 사용합니다. 마찬가지로 MemoryHigh는 소프트 제한에, MemoryMax는 하드 제한에 사용합니다.
  • 서비스 vs 슬라이스: 개별 애플리케이션에는 서비스 단위를 사용하고, 관련 애플리케이션 그룹 또는 리소스 풀을 관리하려면 슬라이스를 사용합니다.
  • 문서화: 특히 프로덕션 환경에서는 중요한 서비스에 적용된 리소스 제한을 명확하게 문서화합니다.
  • OOM Killer: 프로세스가 MemoryMax 제한을 초과하면 cgroup 내에 있더라도 커널의 메모리 부족(OOM) killer가 이를 종료시킬 수 있다는 점에 유의하십시오. Systemd는 OOMPolicy=와 같은 지시문을 사용하여 특정 cgroup에 대한 OOM killer의 동작을 관리할 수 있습니다.

결론

Systemd와 cgroup의 통합은 시스템 리소스를 제어하고 격리하는 강력하고 사용자 친화적인 메커니즘을 제공합니다. 서비스, 스코프, 슬라이스 단위를 효과적으로 사용하는 방법을 익히면 관리자는 CPU, 메모리, I/O 제한을 효율적으로 적용하여 시스템 안정성, 예측 가능한 성능을 보장하고 리소스 부족을 방지할 수 있습니다. 이러한 제어를 구현하는 것은 현대 리눅스 시스템 관리의 기본 측면으로, 애플리케이션 환경과 기본 인프라에 대한 더 나은 제어를 가능하게 합니다.