4P by GN⁺ | ★ favorite | 댓글 1개
  • Oasis는 BSD에 더 가까운 성격을 가진 작은 Linux 시스템으로, 기본 시스템 전체를 정적 링크하고 단순한 구성과 재현 가능한 빌드를 중시함
  • 기본 시스템의 모든 소프트웨어는 정적 링크되며, display server인 velox와 웹 브라우저 NetSurf까지 자체 포함 바이너리로 만들어 라이브러리 업그레이드 문제를 줄임
  • 패키지는 samurai와 Lua 스크립트로 빌드되며, 초기 패키징 비용은 크지만 빠른 재현 빌드, 낮은 유지보수 비용, 패키지 경계를 넘는 증분 빌드를 목표로 함
  • 패키지 매니저 대신 포함할 파일을 명세(specifications) 로 구성하고, 빌드 시스템이 결과 파일시스템 트리를 git 저장소에 기록해 /에 병합하거나 다른 머신에서 가져올 수 있음
  • 설치 사용자는 직접 커널을 빌드하고 문제가 생길 때 시스템을 손볼 수 있어야 하며, 부족한 소프트웨어는 pkgsrc나 Nix로 보완하는 구조임

Oasis의 목표와 성격

  • Oasis는 작은 Linux 시스템이며, 익숙한 Linux 기반 운영체제와는 상당히 다르고 BSD에 더 가까움
  • 기본 방향은 작고 단순한 구성, 정적 링크, 재현 가능한 빌드, 최소 부트스트랩 의존성에 맞춰져 있음
  • 운영체제 전체를 완전한 시스템으로 제공하려 하지만, 수천 개 패키지 저장소를 새로 만들고 유지하기보다는 기본 시스템을 작고 집중된 상태로 유지함

정적 링크 기반 시스템

  • 기본 시스템의 모든 소프트웨어는 정적 링크
    • display server인 velox 포함
    • 웹 브라우저인 NetSurf 포함
  • 정적 링크는 동적 링크보다 단순한 메커니즘이며, 라이브러리 업그레이드 문제를 제거함
  • 결과 바이너리는 완전히 자체 포함되어 다른 시스템으로 쉽게 복사할 수 있음

빌드 방식과 부트스트랩

  • 모든 패키지는 samurai로 빌드되며, 빌드 매니페스트는 Lua 스크립트로 생성됨
  • 이 방식은 상당한 초기 패키징 비용이 필요하지만, 유지보수 비용은 작게 유지함
  • 빌드 시스템이 제공하는 장점은 다음과 같음
    • 거의 최적에 가까운 빌드 시간
    • 예측 가능하고 재현 가능한 빌드
    • 줄어든 빌드 시점 의존성
    • 패키지 경계를 넘는 증분 빌드
  • 부트스트랩에는 POSIX 시스템, git, lua, curl, sha256 유틸리티, 표준 압축 유틸리티, x86_64-linux-musl 크로스 컴파일러가 필요함
  • 이 조건 때문에 macOS나 OpenBSD 같은 비 Linux 시스템에서도 크로스 컴파일이 쉬움

TLS, 패키지 구성, 시스템 설정

  • Oasis는 시스템 TLS와 암호화 라이브러리로 BearSSL을 사용함
    • BearSSL은 매우 작고 잘 작성되어 있지만 널리 채택되지는 않음
    • BearSSL을 네이티브 지원하는 libcurl과 BearSSL 기반 libtls 대체 구현인 libtls-bearssl을 사용함
    • 일부 선택 패키지만 LibreSSL을 요구함
  • Oasis에는 패키지 매니저가 없음
    • 대신 어떤 패키지의 어떤 파일을 시스템에 포함할지 명세로 구성함
    • 빌드 시스템은 결과 파일시스템 트리를 git 저장소에 기록함
    • 이 트리는 /에 병합하거나 다른 머신에서 가져올 수 있음
  • /etc는 시스템 관리자가 전체를 이해하고 적절히 커스터마이즈할 수 있을 만큼 단순해야 함
    • 기본 설정에서 가장 복잡한 파일은 시스템 초기화 스크립트 /etc/rc.init이며 16줄임

다른 패키지 시스템과 ISO C 지향

  • Oasis는 OS에 독립적인 패키지 시스템과 잘 통합됨
  • 필요한 소프트웨어는 pkgsrc나 Nix로 확장할 수 있으며, 기본 시스템은 작고 집중된 상태로 유지함
  • Oasis의 주요 목표 중 하나는 cproc로 빌드하는 것임
    • cproc는 gcc나 clang보다 ISO C 표준에 훨씬 엄격함
    • cproc는 gcc나 clang보다 훨씬 작음
    • 이 작업은 진행 중이지만, 모든 코어 패키지와 대부분의 다른 패키지는 cproc로 성공적으로 빌드됨

설계 원칙

  • 소프트웨어 복잡도는 모든 전이 의존성을 포함해 측정해야 함
  • 실행 파일은 정적 링크되어야 함
  • 소프트웨어 구성요소는 쉽게 커스터마이즈하거나 수정할 수 있어야 함
  • 패키지 소스는 URL이나 git 서브모듈로 참조해야 하며, 직접 포함하지 않아야 함
  • /etc는 전체를 이해할 수 있을 만큼 단순해야 함
  • 패치는 잘 정리되어야 하고, 좋은 설명을 가져야 하며, 항상 깨끗하게 적용되어야 함

설치와 실행 체험

  • 설치 가이드는 wiki에 있음
  • Oasis는 아직 할 일이 많은 야심찬 프로젝트이므로, 사용자는 직접 커널을 빌드하고 문제가 생길 때 시스템을 손볼 수 있어야 함
  • 직접 설치하지 않고 시험해볼 수 있는 QEMU 이미지가 제공됨
    • 아카이브에는 루트 파일시스템, Linux 커널, QEMU 실행 스크립트가 들어 있음
    • ./run은 그래픽 모드로 실행함
    • ./run -s는 시리얼 모드로 실행함

선택된 기본 소프트웨어

  • Oasis는 가능한 경우 더 작고 단순한 라이브러리와 도구 구현을 사용함
    • musl 대신 glibc가 아니라 musl
    • coreutils 대신 sbase
    • util-linux 대신 ubase
    • gzip 대신 pigz
    • man-db 대신 mandoc
    • openssl 대신 bearssl
    • bash 대신 oksh
    • dhclient나 dhcpcd 대신 sdhcp
    • vim이나 emacs 대신 vis
    • bison 대신 byacc
    • sysvinit이나 systemd 대신 perp와 sinit
    • chromium이나 firefox 대신 netsurf
    • ninja 대신 samurai
    • Xorg 대신 velox
    • ncurses 대신 netbsd-curses
  • 패키지는 논리적 set으로 그룹화되어 있으며, 전체 목록은 pkg 디렉터리에 있음
  • 선호하는 소프트웨어가 없더라도 pkgsrc나 Nix로 설치할 수 있을 가능성이 있음

댓글과 토론

Hacker News 의견들
  • 시스템 자체는 자세히 말하긴 어렵지만 그냥 잘 동작했고, IRC에서 만난 커뮤니티가 정말 친절했음
    Kubernetes 노드로 돌릴 수 있는 불변 OS 이미지를 위해 Bazel로 oasis를 빌드해보려 했고, 약간의 도움을 받아 성공함

    • 멋진 아이디어임. 오픈소스로 공개하거나 어떤 방식으로든 써볼 수 있게 할 계획이 있는지 궁금함
      Atomic T를 돌리는 용도로 한번 만져보고 싶음
    • BUILD 파일을 업스트림에 공유했는지 궁금함
    • 어떤 접근을 택했는지 정말 궁금함. 생성되는 ninja 파일을 빌드하려고 rules_foreign_cc를 썼는지, BUILD 파일을 직접 생성했는지, 아니면 완전히 다른 방식이었는지 알고 싶음
      정말 멋진 프로젝트처럼 들림
    • Bazel을 써야 했는지 궁금함
  • 모든 것을 정적으로 링크하면 기본 이미지와 실행 시 메모리에 libc와 공통 라이브러리 사본이 많이 들어가서 커지지 않나 싶음
    정적 링크의 단순함은 좋지만, “비대함”을 피하려는 생각과는 좀 반대처럼 보임

    • 링커는 보통 각 바이너리에 필요한 라이브러리 부분만 포함하므로, 정적 링크를 하면 같은 코드가 여러 사본으로 들어가는 부분은 분명 생기지만 전체 라이브러리를 통째로 복사하진 않음
      이건 비대함이라기보다 관심사의 분리가 더 나아진 것으로 봄. 패키징 관점과 실행 시점 양쪽에서 모든 라이브러리 의존성을 추적해야 하는 시스템이 오히려 비대하다고 느껴짐. 출발점에 따라 다르겠지만, 정적 링크가 더 깔끔하고 추가 메모리는 크게 신경 쓰지 않음
    • HN에서 이런 얘기를 전에도 봤는데, 동적 링크된 프로그램과 많은 의존성을 정적 컴파일된 것으로 실제로 바꿔본 적 없는 사람들이 퍼뜨리는 얘기 같음. 추상적으로는 말이 되지만 실제 테스트는 안 해본 이론처럼 보임
      정적 바이너리로 저장 공간을 아끼는 게 목표는 아니고 오히려 공간 손해를 예상했지만, 어떤 경우에는 /usr/lib의 라이브러리를 제거할 수 있어서 저장 공간이 줄었음. 정확한 이유는 모르겠지만, 프로그램이 쓰지 않는 함수가 많이 든 큰 라이브러리를 보관하지 않아도 되기 때문일 수 있음
      내게는 정적 바이너리가 잘 맞음. busybox 같은 멀티콜 바이너리를 쓰면 “공통” 라이브러리도 어떤 경우엔 제거 가능함. 모두에게 맞지는 않겠지만, 컴퓨터 소유자가 선호하는 프로그램 구성, 즉 그 프로그램들이 요구하는 의존성에 크게 달린 듯함
    • Docker와 Kubernetes가 있고, 실행 중인 서비스마다 운영체제 전체 사본이 들어가는 세상에서 이걸 불평하는 건 좀 이상해 보임
    • 의존성 지옥보다는 언제든 비대함을 택하겠음. 요즘은 앱 하나하나가 번들 웹브라우저처럼 느껴짐
    • 정적 링크된 바이너리는 보통 동적 링크 라이브러리와 그 의존성 전체보다 훨씬 작음. 특히 링크 시점 최적화와 인라이닝을 쓰면 더 그렇다
      100개의 도구가 Chromium 전체를 정적으로 링크하게 만들고 싶진 않겠지만, 일반적인 C 라이브러리 크기에서는 비대해지지 않음. Linux 배포판이 동적 라이브러리를 선호하는 건 의존 프로그램을 전부 다시 빌드하지 않고 한 곳에서 패치 업데이트를 배포할 수 있기 때문임
  • 진짜 정적 링크 Linux 시스템에서 핵심 문제는 GPU 지원임. GPU를 실제로 쓰는 앱은 필요한 GPU 드라이버까지 포함하도록 모두 다시 링크해야 함
    사운드는 ALSA라면 재생/캡처 장치용 IPC/공유 메모리 기반 믹싱이 있어서 괜찮음(dmix/dsnoop). 정적 링크가 합리적임. pulseaudio[012] IPC 인터페이스는 비대하고 임시방편적이며 시간이 지나며 안정적이지 않아, 충분히 좋은 작업을 하면서도 실제 프로세스 내부 저지연 하드웨어 접근을 제공하는 ALSA의 강한 안정성과 비교하면 믿기 어려움
    X11과 Wayland는 IPC 기반이라 여기도 문제가 없음
    하지만 GPU에는 Wayland Vulkan 3D에서 영감을 받은 IPC/공유 메모리 인터페이스 집합이 필요해 보임. 3D 가능한 Wayland 컴포지터가 있어야 하고, 계산 작업용 인터페이스는 Wayland 컴포지터와 분리된 공유 dma-buffers가 될 것임
    이 접근의 장점은 시스템 인터페이스를 극도로 복잡한 ELF에서 해방할 수 있다는 점임. 아주 단순한 실행 파일 형식, 즉 현대적인 실행 파일 형식을 고를 수 있지만, 레거시 지원을 돕기 위해 컴파일러와 링커 지원은 필요함
    중간 지점도 있음. 모든 것은 정적으로 링크하되, GPU 드라이버가 필요한 앱만 예외로 두고, 그 드라이버는 여전히 공유 라이브러리로 제공하는 방식임. 그 용도로 ELF는 터무니없이 과함

    • ELF를 “극도로 복잡하다”고 치부하면서 동시에 Vulkan 호출까지 포함해 시스템 전체에 광범위한 IPC를 제안하게 만드는 불편함이 뭔지 궁금함
    • 공정하게 말하면 ELF가 복잡한 주된 이유는 재배치 때문이고, 이건 공유 라이브러리뿐 아니라 요즘은 흔한 PIE도 지원하기 위한 것임
      GPU 드라이버는 좋은 포인트임. 지금도 정적으로 링크할 수 없을 것 같고, 실제로는 런타임에 진짜 드라이버를 찾는 얇은 중간 계층만 정적으로 링크하는 셈일 듯함
  • 흥미롭지만 사용 사례가 무엇인지 궁금함
    예를 들어 TCC 대신 croc C 컴파일러를 쓰는 장점은 뭘까?
    Netsurf(https://www.netsurf-browser.org/);는 몰랐는데 정말 놀라움. 다만 JS 엔진으로 Duktape를 쓰는 것 같아서 성능은 문제가 될 수 있음

    • cproc는 C11을 지원하지만 tcc는 C99까지만 지원함
      또 cproc는 QBE를 쓰고, QBE는 RISC-V 같은 백엔드를 천천히 늘려가는 중인데 tcc는 아마 지원하지 않는 것으로 앎
    • 덧붙이면, 적어준 URL의 끝 “/” 때문에 하이퍼링크에 “);”까지 포함되어 Not Found 오류가 나는 것 같음
      동작하는 링크: https://www.netsurf-browser.org
    • 내가 보기엔 임베디드 기기에 유용할 수 있음
    • https://www.netsurf-browser.org/documentation/
      저 페이지의 링크가 전부 죽어 있음
      https://www.netsurf-browser.org/about/screenshots/
      스크린샷으로 판단하면 BBC, 자기 웹사이트, Wikipedia는 렌더링할 수 있음. 다른 사이트도 가능할 수 있지만 스크린샷만으로는 알 수 없음. 대신 저 세 웹사이트가 다양한 창 장식에서 동작한다는 건 알 수 있음
  • 기본 설치 크기가 얼마나 되는지 아는 사람이 있는지 궁금함. 어디에서도 답을 못 찾았고, QEMU 이미지 링크도 현재 깨진 것 같음
    비슷한 패키지 구성을 갖춘 Alpine과 비교하면 어떨지 궁금함

    • 예전 2020년 .qcow2 파일이 하나 있는데 약 360MB임
  • JavaScript를 꺼두면 GitHub에 접근할 수 없다고 아쉬워하던 죽은 댓글이 있었음. Oasis 저장소는 sourcehut에도 미러링되어 있으니 그쪽이 더 낫다면 여기 있음
    https://git.sr.ht/~mcf/oasis

  • michaelforney는 st의 Wayland 포트도 만든 사람임: https://github.com/michaelforney/st
    oasis의 전신은 https://dl.suckless.org/htmlout/sta.li일 것임

  • 정말 멋짐. 비대함이 없는 성격이 마음에 들고, 특히 velox 창 관리자가 좋음. Samurai 빌드 시스템도 꽤 흥미로워 보임
    Samurai가 정확히 어떻게 동작하는지, 솔직히 ninja와 왜 다른지는 아직 파악하지 못했지만, 이 프로젝트는 배울 게 많아 보이는 딱 그런 종류의 지적 자극임
    Michael Forney에게 큰 박수를 보냄

  • 흥미로운 선택들임. 드디어 또 하나의 Linux 배포판이 아닌 뭔가가 나옴

  • musl을 쓰는 것과 전통적인 glibc를 쓰는 것의 비교가 궁금함
    둘 사이에 성능 차이가 있나?
    요즘 Rust와 Zig 생태계 양쪽에서 musl이 점점 더 많이 쓰이는 걸 보고 있음

    • 일부 빌드를 glibc에서 musl로 옮긴 이유 중 하나는, 여러 배포판의 다양한 컨테이너 환경에서 하나의 바이너리를 돌릴 때 glibc 링크가 깨지기 쉽다고 느꼈기 때문임
      특히 하나의 Linux 바이너리를 RH 계열과 Debian/Ubuntu 계열, 또는 서로 연식이 다른 배포판에서 모두 동작시키고 싶을 때 그렇다
    • 진짜 비교 포인트는 musl이 어떤 libc를 쓰는지 알려주는 전처리기 매크로를 제공하지 않는다는 것임
      그리고 우회해야 하는 이상한 특성이 꽤 많음
      정적 링크는 특히 전역 생성자와 관련해 링크를 더 고통스럽게 만듦. 전역 생성자는 정확성이나 성능 때문에 자주 필요함. 이건 musl만의 문제는 아니지만 둘 다에 관심 있는 사람이 많음
      지원하려는 가장 오래된 시스템에서 빌드하면 동적 링크도 잘 동작함. 설치가 번거로운 비-libc 의존성은 상대 rpath로 처리할 수 있지만, libstdc++는 다시 생각해보는 게 좋음
      MUSL의 큰 장점은 새 OS를 작성할 때 포팅이 훨씬 쉽다는 것임
    • glibc에서 안 생기는 이상한 버그와 실패를 얻게 됨. 예를 들어 특정 조건에서 실패하던 불완전한 DNS 해석 루틴 같은 것들임. 대신 디스크 공간 30~40MB를 아꼈다고 자랑할 수 있음
      이 프로젝트는 더 작은 크기를 위해 전반적인 품질을 타협하는 것처럼 보임
      BearSSL조차 자기 웹사이트에서 베타 품질이라고 함: “Current version is 0.6. It is now considered beta-quality software” (https://bearssl.org/)
    • 직접 많이 실험해본 경험으로는, [0] glibc에는 더 최적화된 루틴이 몇 가지 있지만 musl은 비대함이 훨씬 적음
      libc 함수를 여기저기 무작정 호출하고 코드베이스가 전반적으로 최적화되어 있지 않다면 glibc가 더 나을 수 있음. 하지만 musl의 작은 코드베이스는 더 빠른 시작과 미세 최적화에서 이점이 있고, 링크 시점 최적화를 쓰지 않아도 그렇다. 쓰면 더 이득을 볼 여지가 있음
      [0]: https://neosmart.net/blog/a-high-performance-cross-platform-...
      정정: 올바른 링크는 이것임: https://neosmart.net/blog/using-simd-acceleration-in-rust-to...
    • glibc는 LGPL임. 애플리케이션을 정적으로 링크하면 일정한 의무가 생김
      musl은 MIT라 제약이 더 적음