LabHub

블로그

systemd v261 — PID 1에 들어온 단계적 롤아웃, 클라우드 IMDS 흡수, 그리고 dlopen 전환의 완성

한국어English日本語中文

들어가며 — 석 달에 한 번, 이번에는 fleet 차례

systemd는 요즘 거의 분기마다 메이저 릴리스를 냅니다. raw NEWS 파일의 릴리스 푸터를 그대로 옮기면 v258이 2025-09-17, v259가 2025-12-17, v260이 2026-03-17, 그리고 v261이 2026-06-19입니다. 이 글을 쓰는 시점 기준으로 Arch Linux core 저장소는 이미 261.1을 배포 중이고, Ubuntu는 26.10 개발 브랜치(stonking)에 261.1이 들어갔으며, 26.04 LTS는 259.5에 머뭅니다. 즉 롤링 배포판 사용자는 지금 만나고, LTS 사용자는 아마 내년에 만납니다.

버전 번호만 보면 그냥 또 하나의 분기 릴리스지만, NEWS를 처음부터 끝까지 읽어 보면 이번 릴리스에는 뚜렷한 방향이 하나 보입니다. PID 1이 단일 머신의 init을 넘어 fleet 운영의 프리미티브를 흡수하기 시작했다는 것입니다. 유닛을 fleet의 일정 비율에서만 켜는 ConditionFraction=, 머신을 링으로 묶는 태그 시스템, 클라우드 인스턴스 메타데이터(IMDS)를 부팅 과정에 통합하는 새 서브시스템이 전부 이 릴리스 하나에 들어왔습니다. 그 옆에서는 2024년부터 이어진 의존성 격리 작업 — 외부 라이브러리의 dlopen() 전환 — 이 마침내 완결됐고, v258의 cgroup v1 제거에서 시작된 "제거의 아크"도 계속됩니다.

이 글은 세일즈 포인트 순서가 아니라 운영자 관점의 중요도 순서로 갑니다. 각 항목이 무엇인지, 어디에 쓸 만한지, 그리고 무엇이 아닌지를 구분하는 데 초점을 둡니다. systemd 유닛과 조건문 자체가 처음이라면 systemd 서비스 관리와 유닛 파일 트러블슈팅 편을 먼저 읽는 편이 낫습니다.

ConditionFraction= — 유닛 파일에 들어온 단계적 롤아웃

v261의 가장 흥미로운 신기능입니다. v261 태그의 systemd.unit man 소스를 기준으로 정리하면 이렇습니다.

ConditionFraction=은 유닛을 "fleet의 안정적인 의사 난수 부분집합"에서만 활성화합니다. 같은 유닛(또는 드롭인)을 fleet 전체에 배포하되, 설정한 비율의 머신에서만 조건이 참이 되게 하는 것입니다. 판정은 전적으로 로컬에서 이뤄집니다 — 머신 ID를 태그 문자열과 함께 해시해 32비트 정수를 만들고, 그 값이 2^32의 지정 퍼센트보다 작으면 참입니다(NEWS의 설명). 중앙 조정자가 없고, 네트워크 호출도 없고, 같은 머신은 언제나 같은 판정을 받습니다.

문법은 퍼센트 하나, 또는 태그와 퍼센트의 조합입니다. 퍼센트는 소수점 둘째 자리까지 허용됩니다.

# /etc/systemd/system/telemetry-v2.service.d/rollout.conf
# fleet의 약 10%에서만 이 유닛이 켜진다
[Unit]
ConditionFraction=telemetry-v2 10%

느낌표를 앞에 붙이면 여집합이 됩니다 — man 페이지의 예를 그대로 옮기면 !myrollout 30%는 나머지 약 70%의 머신에서 참입니다. 신구 버전을 상호 배타적으로 나눠 돌리는 데 그대로 쓸 수 있습니다.

# 구버전 쪽 유닛: 같은 태그의 여집합에서만 켜진다
[Unit]
ConditionFraction=!telemetry-v2 10%

여기서 태그가 하는 일이 중요합니다. 태그는 해시 유도에 섞이는 임의 문자열(공백 불가)이고, 태그가 다르면 서로 독립인 부분집합이 선택됩니다. 거꾸로 말하면 태그 없는 ConditionFraction= 조건들은 전부 같은 머신 집합을 고릅니다 — 태그 없이 10% 롤아웃을 두 개 돌리면 두 실험이 정확히 같은 머신들에 겹칩니다. 무관한 롤아웃에는 반드시 서로 다른 태그를 쓰고, 여러 유닛을 같은 머신 집합에 묶고 싶을 때만 태그를 공유하라는 것이 man 페이지의 지침입니다. 머신 ID를 읽을 수 없으면 조건은 실패(거짓)합니다.

배포 전에 특정 머신에서 판정을 미리 확인하는 것도 됩니다. systemd-analyze condition 명령은 조건 문자열을 그 자리에서 평가해 줍니다.

systemd-analyze condition 'ConditionFraction=telemetry-v2 10%'

이것은 카나리 배포 시스템이 아니다

이제 정직해질 부분입니다. "PID 1에 카나리 롤아웃이 들어왔다"는 문장은 절반만 맞습니다. ConditionFraction=이 제공하는 것은 모집단 선택뿐이고, 카나리 배포를 카나리 배포로 만들어 주는 나머지 — 관측, 판정, 중단 — 는 전혀 제공하지 않습니다.

그러면 어디에 쓰라고 만든 걸까요. man 페이지가 명시한 용도 그대로, 같은 아티팩트를 fleet 전체에 배포하는 이미지 기반 시스템의 단계적 노출입니다. OSTree나 UKI 이미지처럼 머신별로 다른 파일을 배포할 수 없는(하고 싶지 않은) 환경에서는, "모두에게 배포하되 일부에서만 켠다"가 사실상 유일한 롤아웃 수단입니다. 중앙 조정 없이 결정론적으로 동작한다는 성질이 이 환경에서는 단점이 아니라 요구사항입니다. 반대로 Kubernetes나 구성 관리 도구로 머신별 배포 제어가 이미 되는 환경이라면, 이 기능이 새로 열어 주는 문은 거의 없습니다.

의도적인 링 구분이 필요하면 무작위 비율 대신 새로 들어온 태그 시스템을 씁니다. v261부터 머신은 /etc/machine-infoTAGS= 필드에 태그 목록을 가질 수 있고, hostnamectl man 소스 기준으로 hostnamectl tags로 조회·교체합니다(태그는 1~255자의 ASCII 영숫자, 하이픈, 점). 유닛 쪽에서는 ConditionMachineTag=가 셸 글롭 패턴으로 태그를 검사합니다.

# ring0 태그가 붙은 머신(내부 dogfood 링)에서만 켠다
[Unit]
ConditionMachineTag=ring0

AssertMachineTag=도 함께 추가됐고, NEWS에 따르면 systemd-firstboot이 커맨드라인 인자나 credential로 프로비저닝 시점에 태그를 심을 수 있습니다. 정리하면 이렇게 됩니다 — 명시적 링은 태그로, 링 안의 무작위 비율은 fraction으로. 관측과 판정은 여러분의 모니터링이. 이 분업이 설계 의도에 가장 가깝습니다.

IMDS 서브시스템 — systemd가 cloud-init의 마당에 들어왔다

v261은 클라우드 인스턴스 메타데이터 서비스(IMDS)를 다루는 서브시스템 일체를 새로 실었습니다. 구성 요소가 꽤 많습니다.

운영자가 주의할 부분은 네트워크 잠금입니다. 커널 커맨드라인 systemd.imds.network=는 off, locked, unlocked 세 값을 받는데, locked 모드에서는 IMDS 엔드포인트로 가는 prohibit 라우트가 깔려 비특권 프로세스의 직접 접근이 차단되고 모든 접근이 systemd-imdsd를 거쳐야 합니다. IMDS 자격증명 탈취가 클라우드 침해의 단골 경로였다는 걸 생각하면 방향 자체는 타당합니다. 문제는 NEWS 스스로 적어 둔 문장입니다 — 이 잠금은 보안상 권장되지만, IMDS 직접 접근을 전제로 하는 cloud-init 같은 전통적 클라이언트와 "typically conflicts", 즉 대체로 충돌합니다.

기본값이 무엇이냐는 질문에는 문서가 서로 다른 답을 합니다. meson_options.txtimds-network 옵션은 명시적 기본값 없이 choices가 unlocked, locked 순서라 업스트림 소스 기준 기본은 unlocked인데(meson combo 옵션은 첫 항목이 기본), man 페이지의 커널 스위치 설명에는 locked가 기본이라고 적혀 있습니다. 실제 여러분 시스템의 기본값은 배포판이 빌드 시 무엇을 골랐느냐에 달렸으니, cloud-init과 함께 쓰는 이미지라면 배포판 패키지의 빌드 옵션을 확인하는 것이 안전합니다.

한 가지는 분명히 해 둘 필요가 있습니다. 이것은 cloud-init의 대체물이 아닙니다 — 적어도 아직은. systemd-imds가 하는 일은 메타데이터 필드를 credential로 가져오는 데까지고, cloud-config 해석, 패키지 설치, 사용자 생성 같은 프로비저닝 로직은 범위 밖입니다(NEWS도 man 페이지도 그런 주장을 하지 않습니다). 다만 hostname, SSH 키, user-data가 credential 체계로 들어오기 시작했다는 것은, 단순한 프로비저닝 요구라면 systemd 순정 부품만으로 처리되는 미래를 향한 첫 수로 읽는 게 자연스럽습니다. cloud-init 쪽에서 이 잠금 모드와 어떻게 공존할지는 지켜볼 문제입니다.

dlopen 전환 완성 — 그리고 rsyslog가 깨진 방식

2024년 3월의 xz-utils 백도어(CVE-2024-3094)는 배포판들이 sshd에 libsystemd를 링크해 둔 것을 통로로 삼았습니다 — liblzma가 libsystemd의 전이 의존성으로 sshd 프로세스에 실려 들어간 것입니다. 석 달 뒤인 2024년 6월에 나온 v256부터 systemd는 바로 그 liblzma를 포함한 다섯 개 라이브러리(liblz4, libzstd, liblzma, libkmod, libgcrypt)를 일반 링크에서 dlopen() 기반으로 바꾸기 시작했고, 릴리스마다 대상을 넓혀 왔습니다.

v261이 이 작업의 종착점입니다. 이번에 libgnutls, libmicrohttpd, libcurl, libcrypto, libssl, libfdisk, libcryptsetup까지 전환되면서, NEWS는 박스까지 쳐서 선언합니다 — 외부 라이브러리에 대한 직접 링크는 이제 전부 dlopen()으로 대체됐고, 유일한 예외는 libc라고("with the sole exception of libc"). 어떤 dlopen 의존성이 있는지는 각 바이너리에 임베드되는 ELF dlopen metadata 노트로 선언되므로, 패키징 도구가 선택적 의존성을 기계적으로 파악할 수 있습니다.

이 방향의 이득은 분명합니다 — 프로세스에 실리는 코드가 줄고, 최소 이미지에서 뺄 수 있는 라이브러리가 늘고, xz 사건 같은 전이 의존성 공격면이 좁아집니다. 그런데 이 전환은 반대 방향의 함정도 만듭니다. libsystemd가 여태 남들 대신 끌어와 주던 라이브러리들이 사라진다는 것입니다.

v261 NEWS에 실린 실제 사례가 정확히 이것입니다. libsystemd가 libm(수학 라이브러리)에 링크된다는 보장이 사라지자, libm의 심볼을 쓰면서도 정작 libm을 링크하지 않던 libfastjson의 문제가 드러났고, 그 결과 rsyslog가 시작 시점에 크래시합니다. 지금까지는 libsystemd가 libm을 끌어와 준 덕에 가려져 있던 버그입니다. NEWS는 근본 수정(libfastjson이 libm을 직접 링크)이 되기 전의 임시 우회로 링커 플래그를 제시합니다.

-Wl,--push-state,--no-as-needed,-lm,--pop-state

교훈은 rsyslog에 국한되지 않습니다. libsystemd에 링크하는 소프트웨어를 빌드하거나 패키징한다면, 지금까지 "어쩌다 링크돼 있던" 전이 의존성에 기대고 있지 않았는지 확인할 시점입니다. 쓰는 심볼은 직접 링크하라는 원칙이 이제 말 그대로 강제되기 시작했습니다.

업그레이드 전 체크리스트 — 조용히 무는 것들

NEWS의 비호환 변경 목록에서 실제 운영에 닿는 것들만 추렸습니다.

맥락을 하나 보태면, 이 목록은 v258에서 시작된 제거 흐름의 연속입니다. v258(2025-09)이 cgroup v1과 System V 상태 제어(runlevel, telinit, init 3)를 제거했고, v260(2026-03)이 System V 서비스 스크립트 지원(systemd-sysv-generator, rc-local.service)을 마저 제거했습니다. 아직 SysV 스크립트로 배포되는 사내 데몬이 있다면 이미 v260 계열 배포판에서 부팅에 올라오지 않습니다.

그 밖에 눈여겨볼 것들

지면상 짧게 줄이지만, 환경에 따라서는 위의 것들보다 중요할 수 있는 항목들입니다.

그래서 누가 언제 신경 써야 하나

정리하면 대상별로 이렇게 됩니다.

지금 당장 — confidential computing에서 RTMR/PCR 기반 attestation을 검증하는 쪽(측정값이 바뀝니다), libsystemd에 링크하는 소프트웨어의 패키저(전이 의존성 정리), rsyslog를 자체 빌드하는 쪽. 롤링 배포판이라면 이미 v261이 도착했습니다.

설계에 반영할 시점 — 이미지 기반 fleet을 굴리는 팀. ConditionFraction=과 머신 태그는 지금까지 외부 도구로 흉내 내던 단계적 노출을 유닛 파일 문법으로 내려 줍니다. 다만 앞서 말한 대로 관측·판정·롤백은 여전히 여러분 몫이라는 전제로 설계하십시오. IMDS 서브시스템은 cloud-init 의존을 줄이고 싶던 미니멀 이미지 진영에 실질적인 선택지가 되기 시작했습니다.

아직 여유 있음 — LTS 배포판에 머무는 대부분의 서버 운영자. Ubuntu 26.04는 v259, 대부분의 엔터프라이즈 배포판은 그보다 뒤에 있습니다. 다만 v258 이후의 제거 흐름(cgroup v1, SysV 스크립트, udev DB v0)은 다음 메이저 업그레이드 때 한꺼번에 도착하니, 레거시 의존이 있다면 목록만이라도 미리 만들어 두는 편이 좋습니다.

마치며

v261을 한 문장으로 줄이면 이렇게 됩니다 — systemd는 이제 머신 하나를 부팅하는 소프트웨어가 아니라 fleet의 최소 단위를 자처하는 소프트웨어가 됐습니다. ConditionFraction=은 롤아웃의 모집단 선택을, 태그는 링 구분을, IMDS 서브시스템은 클라우드 컨텍스트의 반입을 각각 PID 1의 문법으로 만들었습니다. 어느 것도 기존 오케스트레이션을 대체하지는 않지만, 셋 다 "지금까지 반드시 외부 도구가 필요했던 일"의 목록을 조금씩 줄입니다.

동시에 이 릴리스는 systemd가 자기 자신에게도 같은 원칙을 적용하고 있음을 보여 줍니다. dlopen 전환의 완성은 xz 사건이 드러낸 전이 의존성 문제에 대한 2년짜리 대답이고, rsyslog 사례는 그 대답이 생태계에 청구하는 비용입니다. 업그레이드 자체는 여느 분기 릴리스처럼 무난하겠지만, attestation 기대값과 전이 의존성 — 이 두 가지만큼은 릴리스 노트를 읽지 않은 팀을 정확히 무는 종류의 변경입니다. 이 글이 그 목록의 대용이 되었기를 바랍니다.

참고 자료

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다