Plan 9에 Tailscale 포팅 작업
(tailscale.com)- 만우절 발표로 시작된 Plan 9 지원은 실제 PR과 커널·Go 수정으로 이어졌고, 2025년 4월 2일 기준 Tailscale이 Plan 9에서 동작하는 상태까지 도달함
- 단순한
GOOS=plan9 GOARCH=386빌드 문제가 아니라, Go의 Plan 9 포트에서 런타임 크래시와 컴파일러 특수 처리 문제가 먼저 드러남 - Russ Cox의 Plan 9 커널 및 Go 런타임·컴파일러 수정으로 SSE, 부동소수점 컨텍스트, 단조 시간, DNS, 개발 환경 문제가 함께 정리됨
- Tailscale은 Plan 9의
/net파일 인터페이스를 활용해 TUN 유사 구현, 라우팅, Tailscale SSH, MagicDNS, 서비스 수집을 붙였지만 일부는 임시 구현이거나 미완성임 - 현재 검증 범위는 주로 9legacy와
GOARCH=386이며, 9front·amd64·exit node·Gonet/netns지원은 추가 검증이나 재설계가 필요함
만우절 농담이 실제 포팅으로 이어짐
- Tailscale은 2025년 4월 1일 Plan 9 지원을 발표했고, 다음 날 이 발표가 실제 동작하는 포팅 작업을 바탕으로 했다는 배경을 공개함
- 작업은 Tailscale PR과 여러 Plan 9·Go 수정으로 이어짐
- 초기 접근은 Tailscale의 Go 바이너리 두 개를
GOOS=plan9 GOARCH=386 go install ./cmd/tailscale{,d}로 빌드하면 될 것이라는 기대에서 출발함 - 2023년 8월 첫 시도에서는 일부 빌드가 진행됐지만 실행 중 비정상 크래시가 발생함
- Go의 Plan 9 포트가 first-class port가 아니어서 회귀가 방치된 상태였음
- Tailscale이 Plan 9에서 기존보다 Go를 더 강하게 밀어붙였을 가능성도 있음
- 포팅은 2024년 내내 멈췄다가, 2025년 3월 만우절 아이디어와 함께 다시 진행됨
SSE와 Go의 Plan 9 지원 정리
- 1999년 Intel Pentium III에 도입된 SSE 명령어가 이번 작업의 주요 출발점 중 하나였음
- Go 컴파일러는 Plan 9 타깃에서 SSE 사용을 피하려고 했음
- Plan 9 커널이 note handler에서 SSE 레지스터를 저장·복원하지 않았기 때문임
- Go 컴파일러는 어떤 코드가 note handler 안에서 실행되는지 알 수 없어 SSE를 전역 비활성화하려 했음
- 이 특수 처리는 자주 깨졌고, 컴파일러 곳곳에
plan9예외가 많아짐
- Russ Cox는 Plan 9 커널이 note handler에서 부동소수점·SIMD 컨텍스트를 다루도록 수정함
- 386 쪽에는 sys/src/9: allow floating point in note handlers 수정이 들어감
- amd64 9k 커널에서는 fork 후 FP 상태 aliasing, note handler의 SIMD,
noted(NCONT)레지스터 손실 등 추가 문제가 발견됨
- Go 쪽에서는 Plan 9 코드 생성 특수 처리 제거가 진행되면서
tailscaled가 더 오래 실행될 수 있게 됨
IPC와 개발 환경
- 이후
tailscaled는 스택 손상 대신 메모리 부족으로 크래시하기 시작함 - 이전 Plan 9 포팅 시도에서 Tailscale의
safesocketIPC 패키지에 무한히 goroutine을 만드는 버그가 있었음 - 우선 localhost TCP를 쓰도록 바꾸면서 문제가 해결됨
- Plan 9의 “모든 것이 파일” 방식에는 덜 어울리지만, 다른 Plan 9 서비스도 localhost TCP를 사용한다고 확인함
- 향후에는 Russ가 Go로 포팅한 srv9p package를 이용해 LocalAPI를 얹는 방식이 더 나을 수 있음
- 현재 구현은 다른 플랫폼처럼 localhost 인증을 붙이지 못하므로, 공유 Plan 9 머신에서는 쓰지 말라고 명시됨
- 초기 개발은 9legacy CD 이미지 기반 VM에서 진행됐고, 바이너리를 HTTP로 내려받아 실행하는 반복 과정이 느렸음
- Russ Cox가 만든 rsc/plan9은 Plan 9 소스, 사전 컴파일 바이너리,
./boot/qemu스크립트를 포함함- qemu VM은 디스크 없이 부팅하고, localhost 9P 서버가 제공하는 Git 저장소를 루트 파일시스템으로 사용함
- 개발 머신과 Plan 9 파일시스템을 공유해 반복 시간이 분 단위에서 초 단위로 줄어듦
- qemu는 virtio도 사용해 더 빨라짐
네트워크 통합: TUN, 라우팅, MagicDNS
- 처음 동작한 Tailscale은 커널 네트워크 스택을 쓰지 않는 사용자 공간 네트워킹 모드였음
- TCP, UDP, ICMP 등은 gVisor의 netstack을 통해 처리됨
- Plan 9 머신에서 tailnet으로 접근하려면
tailscaled의 HTTP/SOCKS5 프록시를 써야 함 - Plan 9 프로그램 중
HTTP_PROXY나ALL_PROXY환경 변수를 인식하는 경우가 거의 없어 이상적이지 않았음
- Plan 9의 TUN 유사 구현은 매우 단순했음
/net/ipifc/clone을 열고 새 인터페이스 번호를 읽음- control fd에
"bind pkt\n"를 쓰면/net/ipifc/2/*같은 새 인터페이스가 생김 /net/ipifc/2/data를 열어 IP 패킷을 그대로 읽고 씀- 별도 ioctl이나 길이 프레이밍이 필요 없음
- 라우팅 테이블 조작도
/net/iproute파일을 통해 이뤄짐"tag tail\n"을 써서 이후 추가되는 route에tail태그를 붙임"add 100.64.0.0 /106 100.102.103.104"같은 메시지로 route를 추가함- Plan 9 내부가 IPv6 중심이고 IPv4를 IPv4-mapped IPv6 주소로 다루기 때문에 CGNAT
100.64.0.0/10이/106처럼 표현됨
- MagicDNS는 Plan 9에서 peer를
foo또는foo.tailnet-name.ts.net같은 이름으로 접근하게 만드는 작업이었음/net/dns나/net/cs질의를 가로채는 방안도 논의됨- 최종적으로 Russ가 특정 DNS suffix에 대해 대체 DNS 서버를 지정할 수 있도록 Plan 9을 수정함
- DNS 질의가 잘못 negative cache되는 문제도 수정됨
Tailscale SSH와 서비스 수집
- Tailscale SSH는
tailscaled내장 SSH 서버로, 패킷과 연결된 WireGuard 키를 통해 알려진 Tailscale identity로 인증함 - 처음에는 Plan 9 셸인
/bin/rc를os/exec.Command로 실행하고 stdin/stdout을 연결함- 셸은 실행됐지만 echo, 내비게이션, 프로세스 interrupt 등이 제대로 동작하지 않았음
- Russ는 netshell example을 9fans/go에 추가함
- 이 예제는 매우 안전하지 않은 telnet 서버에 가까웠지만, Tailscale SSH 뒤에 붙이기에는 충분했음
- 이후 SSH로 Plan 9의
/dev/snarf내용을 가져오거나, 노트북에서 Go 테스트를 cross-compile한 뒤 SSH로 실행하기 쉬워짐
- Tailscale의 선택 기능인 서비스 수집도 Plan 9에 맞춰 검토됨
/proc/NNN/fd를 순회해/net/tcp/clone을 연 프로세스를 찾음- fd의 QID를
/net/tcp/NNN/{status,local}와 맞춰 listening 여부와 포트를 확인함 - QID에서 TCP 번호를 계산하는 방식은 커널 구현 변화에 취약해 아쉬운 부분으로 남음
시간, 웹 데모, v86
- gVisor netstack이 단조 시간(monotonic time)이 뒤로 갔다고 보고하면서
tailscaled가 크래시하는 경우가 있었음- Go의 Plan 9 시간 구현이 단조 시간으로 wall time을 쓰고 있었음
- ntpd가 시계를 뒤로 조정하면 netstack의 단조 시간 가정이 깨짐
- Russ는 Plan 9의
/dev/bintime에 단조 시간을 추가하고, Go가 이를 사용하도록 수정함 - 웹에서 Plan 9을 실행하는 데는 v86이 사용됨
- v86은 WASM으로 32비트 운영체제를 실행하고 여러 네트워크 방식을 제공함
- 이 점도
GOARCH=386에 집중한 이유 중 하나였음
- 처음에는 이더넷 프레임을 websocket relay로 보내기 위해 Tailscale의 네트워크 시뮬레이션 환경에 wsproxy protocol support를 추가함
- ARP, DHCP, DNS, NAT, control plane, DERP 등을 gVisor netstack으로 흉내 내는 통합 테스트 환경에서 동작함
- 하지만 DHCP 왕복 때문에 relay가 멀면 Plan 9 GUI인
rio시작이 느려짐
- 이후 WISP 서버도 구현했지만, 프로덕션화 전에 시간이 부족해 copy.sh/v86의 기본 네트워크 relay 설정으로 출시함
- Tailscale과 Plan 9을 넣은 디스크 이미지는 16MB였고, Tailscale 바이너리는 압축 해제 후 23MB였음
- 부팅 시 “gunzip…” 단계가 보이는 이유가 여기에 있음
- 예제 이미지는
copy.sh/v86의 9legacy 프로필에 포함됨
남은 작업과 실제 성과
- 현재 Tailscale Plan 9 포팅은 9legacy에서만 테스트됨
- Plan 9의 주요 fork에는 최소한의 9legacy와 더 많이 수정된 9front가 있음
- Russ가 9legacy에 작성한 일부 패치는 9front로 포팅이 필요할 수 있음
GOARCH=amd6464비트 지원도 아직 검증이 필요함- exit node 지원과 Go
net/netns패키지 지원은 구현되지 않음- 이를 위해서는 Tailscale이 Plan 9에서 자신을 별도
/net으로 드러내는 방식 같은 재검토가 필요할 수 있음
- 이를 위해서는 Tailscale이 Plan 9에서 자신을 별도
- 이번 작업으로 Go의 Plan 9 지원도 개선됨
- cmd/compile: use FMA on plan9, and drop UseFMA
- runtime: remove nextSampleNoFP from plan9
- cmd/compile, runtime: remove plan9 special case avoiding SSE
- net: fix parsing of interfaces on plan9 without associated devices
- os: guarantee min buffer size for ReadFile reads on /proc-like files
- net: unblock UDP Reads upon Close on plan9, add test
- runtime: fix plan9 monotonic time, crypto randomness
- 특히 Go 컴파일러에서 Plan 9 특수 처리를 제거하면서 컴파일러가 더 단순해지고 수정하기 쉬워짐
- v86 데모 공개 시점에는 v86 작성자의 만우절 장난으로 VGA 텍스트 출력까지 가짜 네덜란드어처럼 표시되는 문제가 있었지만,
&nojokequery argument로 피할 수 있었음
댓글과 토론
Hacker News 의견들
-
궁금한 게 있으면 답할 수 있음
지금 몇 명이 https://meet.google.com/qre-gydb-mkv에서 이 얘기를 나누고 있음
편집: 한 시간이 지나서 모두 나갔음
이전 4월 1일 블로그 글은 https://tailscale.com/blog/tailscale-enterprise-plan-9-suppo...였음- Plan 9 시스템을 한 번도 설정해 본 적이 없는데, 이걸 쓰면 분산 시스템 통신이 내 Tailnet을 통해 지나가게 할 수 있는 건가?
-
Russ Cox가 이 농담을 끝까지 밀어붙인 건 정말 전설적임
- 누가 Russ를 설득해서 Plan 9에 완전한 웹 브라우저를 넣으면 엄청 웃길 거라고 해줬으면 함
-
9fans 목록에는 만우절용으로 이런 게 올라왔음
mips, 386, arm, arm64, amd64 같은 미성숙한 컴퓨터 아키텍처의 유지보수 비용이 너무 커서, 더 성숙하고 안정적인 아키텍처에 집중하기로 했다는 내용임
그 대상은 power64와 itanium이고, 따라서 power64와 itanium을 제외한 모든 아키텍처는 동결·보존되며 수명 종료로 승격된다고 함 -
농담이 아니라 진짜로 Plan 9 엔터프라이즈 버전이 있었으면 좋겠음
요즘 스크립트 대부분을rc로 쓰고 있는데, 우리가 nix를 쓰고 dirnev로 자동으로 끌어올 수 있어서 동료들이 참고 넘어가 주는 중이고, 꽤 좋았음- 다른 사람들이
rc스크립트를 실행할 수 있는지보다, 그걸 읽고 수정할 수 있는지를 더 걱정할 것 같음 rc의 장점 하나는 이거임[1]:
“rc 설계에서 가장 중요한 원칙은 매크로 처리기가 아니라는 점이다. 입력은 어휘·구문 분석 코드에서 절대 두 번 이상 스캔되지 않는다”
예전에 일하던 유닉스 회사에서 실행 중이던 셸 스크립트가 수정되는 바람에 작업 디스크 대부분을 지운 적이 있음. 다행히 일일 백업을 테이프에 보관하고 있었고, 대략 17년 전 일이었음
[1] https://www.scs.stanford.edu/nyu/04fa/sched/readings/rc.pdf- “엔터프라이즈 Plan 9”에서 구체적으로 뭘 기대하는지 더 설명해 줄 수 있나?
- 다른 사람들이
-
첫 글에서 놓쳤고 그냥 직접 시험해 보고 싶다면, 이 v86 이미지에서 동작함:
https://copy.sh/v86/?profile=custom&m=768&vram=16&hda.url=ht...
VM 안에서tailscaled와tailscale을 시작할 수 있음. 프록시 가용성이 제한적이라 온라인 상태가 되기까지 시간이 좀 걸릴 수 있음
편집: alt가 세 번째 버튼 역할을 함. 터미널을 시작하려면 alt를 누른 채 오른쪽 클릭, new 선택, alt에서 손을 떼고 오른쪽 클릭 드래그로 터미널 창 크기를 조절하면 됨 -
웨비나 진행 중임(Google Meet) https://ftp.plan9.ts.net/webinar
- 관심 있었을 사람들을 위해 말하자면, 방금 끝났음
-
농담의 전제는 마음에 들었는데, 설명이 길어질수록 갑자기 우울해졌음
망가진 게 너무 많고 복잡도도 너무 큼. 결국 뭘 위해서인가, 네트워크 터널 하나 만들려고? 이 추가 작업 자체가 농담이었다면 웃겼을 듯함- 새 작업을 하려면 Plan 9 쪽 작업이 조금 필요했지만, 실제 Tailscale 구현은 다른 유닉스들보다 훨씬 작업량이 적었음
- 이 작업 덕분에 Go 컴파일러도 더 좋아진 것처럼 들림. 코드 안의 Plan 9 특수 처리가 줄었으니까
-
rsc, rob pike, bradfitz와는 특히 Plan 9 얘기로 몇 시간이고 붙잡고 이야기할 수 있을 것 같음. 물론 그들의 시간을 완전히 낭비하게 되겠지만
그 운영체제는 정말 매혹적임
커리어 초반에 같이 일하던 전문가가 옆에 앉아서, 내가 충분히 이해할 때까지 참을성 있게 방법을 보여주고 질문을 받아주던 때가 기억남. 깊은 물에 빠져도 헤엄칠 수 있게 만들어 주는 식이었고, 세 시간 만에 특정 지식으로 학사 학위 하나를 받은 듯한, 커리어에서 가장 빠른 성장 중 하나였음
C도 모르고 Plan 9도 생산적으로 쓸 만큼 알지는 못하지만, 지금의 주요 3대 운영체제에는 없는 걸 아쉬워하기 위해서라도 더 알고 배우고 싶은 멋지고 유용한 기능들이 있음
돈이 있다면 Go 지식을 넓히려고 세 사람 모두와 직접 이야기할 시간을 사고, 늘 원했지만 스스로 얻지 못했던 Plan 9 이해를 위해 rsc와 rob pike의 시간도 사고 싶음 -
Plan 9가 정말 좋음. 그 원칙을 많이 가져와서 내 운영체제를 만드는 게 은퇴 프로젝트이자 인생 목표임
편집: 이 프로젝트 이름은 “chaos10”으로 예약해 둠. SerenityOS처럼 계획은 없을 테니까 -
이게 동작하게 하려고 Plan 9 커널까지 패치할 줄은 전혀 예상 못 했음
- 왜 아니겠나? 아무도 이런 걸 진지하게 해본 적이 없었던 게 분명하니, 빠져 있던 작업량은 비교적 적어 보임 :)