원격 개발 환경
SSH 최적화, ProxyJump, 포트 포워딩, Mosh, sshfs, tmux, GNU Screen, VS Code Remote Development를 활용한 커널 원격 개발 환경 구축 및 원격 디버깅(Debugging) 가이드입니다.
개요
커널 개발은 고성능 머신이 필요하므로, 원격 서버에서 빌드하고 로컬에서 편집하는 패턴이 흔합니다. 특히 대규모 커널 트리의 make -j$(nproc) 빌드는 수십 분이 소요되기 때문에, 빌드 전용 서버를 활용하면 생산성이 크게 향상됩니다. 일반적인 원격 커널 개발 워크플로는 로컬 편집 → 원격 빌드 → 타겟 배포/테스트의 3단계로 구성됩니다.
이 문서에서 다루는 주제는 다음과 같습니다.
- SSH 설정 최적화: 키 관리, ProxyJump, 포트 포워딩, 보안 강화
- Mosh: 불안정한 네트워크에서의 대안 셸
- sshfs: 원격 파일 시스템 마운트(Mount)를 통한 하이브리드 워크플로
- 터미널 멀티플렉서: tmux, GNU Screen, nohup/disown
- VS Code Remote: 원격 편집, 빌드, 디버깅 통합 환경
- 원격 빌드/배포 워크플로: rsync, NFS, TFTP를 활용한 커널 이미지 배포
SSH 설정 최적화
# ~/.ssh/config
# 커널 빌드 서버
Host kernel-build
HostName 192.168.1.100
User kdev
Port 22
IdentityFile ~/.ssh/id_ed25519
# 연결 유지
ServerAliveInterval 60
ServerAliveCountMax 3
# 멀티플렉싱 (연결 재사용)
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 압축
Compression yes
# GDB 포트 포워딩
LocalForward 1234 localhost:1234
SSH 키 관리
커널 빌드 서버에 접속할 때는 비밀번호 대신 공개키 인증을 사용하는 것이 보안과 편의성 모두에서 유리합니다. Ed25519 알고리즘은 현재 가장 권장되는 키 타입입니다.
# Ed25519 키 생성 (권장)
ssh-keygen -t ed25519 -C "kdev@kernel-build" -f ~/.ssh/id_ed25519
# 공개키를 빌드 서버에 등록
ssh-copy-id -i ~/.ssh/id_ed25519.pub kdev@kernel-build
# ssh-agent 시작 및 키 등록 (~/.bashrc에 추가)
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# 키 등록 확인
ssh-add -l
Agent Forwarding을 활성화하면 빌드 서버에서 다시 다른 서버(예: Git 리포지토리, 타겟 보드)에 접속할 때 로컬 키를 사용할 수 있습니다.
# ~/.ssh/config — Agent Forwarding 설정
Host kernel-build
ForwardAgent yes
# 주의: 신뢰하지 않는 서버에는 ForwardAgent를 사용하지 마십시오.
# 악의적인 서버 관리자가 forwarded agent를 악용할 수 있습니다.
ForwardAgent yes는 원격 서버의 root 사용자가 로컬 SSH 에이전트의 키를 임의로 사용할 수 있게 합니다. 신뢰할 수 있는 서버에만 설정하고, 가능하면 ssh -o ForwardAgent=yes로 필요할 때만 활성화하는 것이 안전합니다.
ProxyJump / 다단계 접속
커널 개발 환경에서 타겟 보드나 빌드 서버가 내부 네트워크에 있어 직접 접속이 불가능한 경우, 점프 호스트(Jump Host)를 경유해야 합니다. OpenSSH 7.3+에서 지원하는 ProxyJump 지시자를 사용하면 간단하게 설정할 수 있습니다.
# ~/.ssh/config — ProxyJump 다단계 접속
# 1단계: 외부에서 접근 가능한 게이트웨이
Host lab-gateway
HostName gateway.company.com
User kdev
Port 22
IdentityFile ~/.ssh/id_ed25519
# 2단계: 게이트웨이를 경유하여 빌드 서버 접속
Host kernel-build
HostName 10.0.1.50
User kdev
ProxyJump lab-gateway
IdentityFile ~/.ssh/id_ed25519
# 3단계: 빌드 서버를 경유하여 타겟 보드 접속
Host target-board
HostName 10.0.1.200
User root
ProxyJump kernel-build
Port 22
# 명령줄에서 직접 점프 호스트 지정
ssh -J lab-gateway kdev@10.0.1.50
# 다단계 점프 (게이트웨이 → 빌드서버 → 타겟)
ssh -J lab-gateway,kdev@10.0.1.50 root@10.0.1.200
# 레거시 방식 (ProxyCommand, OpenSSH 7.3 미만)
ssh -o ProxyCommand="ssh -W %h:%p lab-gateway" kdev@10.0.1.50
SSH 포트 포워딩 전략
원격 커널 개발에서 포트 포워딩은 필수적입니다. GDB 원격 디버깅, QEMU VNC 접속, 시리얼 콘솔 네트워크 접속 등 다양한 상황에서 SSH 터널(Tunnel)을 통해 원격 서비스에 안전하게 접근할 수 있습니다.
# ── 로컬 포워딩 (-L): 로컬 포트로 원격 서비스 접근 ──
# GDB 원격 디버깅: 로컬 1234 → 원격 QEMU의 GDB stub
ssh -L 1234:localhost:1234 kernel-build
# QEMU VNC: 로컬 5900 → 원격 QEMU의 VNC 디스플레이
ssh -L 5900:localhost:5900 kernel-build
# 시리얼 콘솔 네트워크: 로컬 2222 → 원격 ser2net/socat
ssh -L 2222:localhost:2222 kernel-build
# 여러 포트를 한 번에 포워딩
ssh -L 1234:localhost:1234 -L 5900:localhost:5900 -L 2222:localhost:2222 kernel-build
# ── 리모트 포워딩 (-R): 원격에서 로컬 서비스 접근 ──
# 빌드 서버에서 로컬 Git 리포지토리에 접근
ssh -R 3000:localhost:3000 kernel-build
# ── 다이나믹 포워딩 (-D): SOCKS 프록시 ──
# 로컬 SOCKS 프록시로 원격 내부 네트워크 전체 접근
ssh -D 1080 kernel-build
# 브라우저에서 SOCKS5 프록시 localhost:1080 설정 → 내부 웹 UI 접근
# ~/.ssh/config — 포트 포워딩 영구 설정
Host kernel-build
# GDB 디버깅용
LocalForward 1234 localhost:1234
# QEMU VNC용
LocalForward 5900 localhost:5900
# 시리얼 콘솔 네트워크용
LocalForward 2222 localhost:2222
# 다이나믹 포워딩 (SOCKS)
DynamicForward 1080
SSH 보안 강화
커널 빌드 서버는 민감한 코드와 커널 이미지를 다루므로, SSH 서버 설정을 강화하는 것이 좋습니다.
# /etc/ssh/sshd_config — 보안 강화 설정
# 비밀번호 인증 비활성화 (키 전용)
PasswordAuthentication no
ChallengeResponseAuthentication no
# root 직접 로그인 금지
PermitRootLogin no
# 접속 허용 사용자 제한
AllowUsers kdev
# 빈 비밀번호 금지
PermitEmptyPasswords no
# X11 포워딩 비활성화 (불필요 시)
X11Forwarding no
# 최대 인증 시도 횟수
MaxAuthTries 3
# 유휴 세션 타임아웃 (서버 측)
ClientAliveInterval 300
ClientAliveCountMax 2
fail2ban을 설치하고 SSH jail을 활성화하는 것을 권장합니다. sudo apt install fail2ban 후 기본 설정만으로도 5회 실패 시 IP가 10분간 차단됩니다. 커널 빌드 서버처럼 외부에 노출된 경우 특히 중요합니다.
Mosh (모바일 셸)
Mosh(Mobile Shell)는 SSH의 대안으로, UDP 기반 프로토콜을 사용하여 불안정한 네트워크 환경에서도 끊김 없는 원격 세션을 제공합니다. Wi-Fi 전환, 네트워크 로밍, 노트북 절전/복귀 상황에서 SSH 연결이 끊어지는 문제를 해결합니다.
- 로컬 에코(Local Echo): 키 입력을 서버 응답 전에 즉시 표시하여 체감 지연(Latency)을 줄입니다
- UDP 기반: TCP 연결 상태에 의존하지 않아 IP 변경 시에도 세션이 유지됩니다
- 자동 재연결: 네트워크 복구 시 별도 조작 없이 세션이 복원됩니다
# 설치
sudo apt install mosh # Debian/Ubuntu
sudo dnf install mosh # Fedora/RHEL
sudo pacman -S mosh # Arch Linux
# 기본 접속 (SSH와 동일한 문법)
mosh kdev@kernel-build
# SSH config의 Host alias 사용
mosh kernel-build
# SSH 포트가 다른 경우
mosh --ssh="ssh -p 2222" kernel-build
# 서버 측 방화벽(Firewall) 설정 (Mosh는 UDP 60000-61000 사용)
sudo ufw allow 60000:61000/udp
# 또는 iptables
sudo iptables -A INPUT -p udp --dport 60000:61000 -j ACCEPT
| 비교 항목 | SSH | Mosh |
|---|---|---|
| 프로토콜 | TCP | UDP (SSP 프로토콜) |
| 연결 유지 | TCP 상태 의존 (끊김 가능) | IP 변경·절전에도 유지 |
| 입력 지연 | 서버 RTT만큼 지연 | 로컬 에코로 즉시 표시 |
| 포트 포워딩 | 지원 (-L, -R, -D) | 미지원 |
| 출력 히스토리 | 터미널 자체 지원 | 미지원 (tmux와 조합) |
| X11 포워딩 | 지원 | 미지원 |
| 방화벽(Firewall) | TCP 22번 포트 | UDP 60000-61000 추가 필요 |
| 커널 개발 추천 | 안정적 네트워크 환경 | 모바일/불안정 네트워크 |
mosh kernel-build -- tmux attach -t kernel-dev처럼 tmux와 함께 사용하는 것이 일반적입니다. Mosh가 네트워크 안정성을 담당하고, tmux가 세션 관리와 화면 분할을 담당합니다.
sshfs를 활용한 원격 파일 마운트
sshfs(SSH Filesystem)는 FUSE 기반으로 원격 파일 시스템을 로컬에 마운트하는 도구입니다. 로컬 에디터로 원격 서버의 커널 소스를 직접 편집할 수 있어, 로컬 편집 + 원격 빌드 하이브리드 워크플로를 구현할 때 유용합니다.
기본 사용법
# 설치
sudo apt install sshfs # Debian/Ubuntu
sudo dnf install fuse-sshfs # Fedora/RHEL
sudo pacman -S sshfs # Arch Linux
# 원격 커널 소스를 로컬에 마운트
mkdir -p ~/remote-linux
sshfs kdev@kernel-build:/home/kdev/linux ~/remote-linux
# 최적화 옵션 (캐시, 압축, 재연결)
sshfs kdev@kernel-build:/home/kdev/linux ~/remote-linux \
-o cache=yes \
-o compression=yes \
-o reconnect \
-o ServerAliveInterval=15 \
-o kernel_cache \
-o auto_cache
# 마운트 해제
fusermount -u ~/remote-linux
# macOS에서 마운트 해제
umount ~/remote-linux
커널 개발 하이브리드 워크플로
sshfs로 마운트된 디렉토리에서 로컬 에디터를 사용하고, 빌드는 원격 서버의 tmux 세션에서 실행하는 패턴입니다.
# 1. sshfs 마운트
sshfs kdev@kernel-build:/home/kdev/linux ~/remote-linux -o reconnect,cache=yes
# 2. 로컬 에디터로 편집
vim ~/remote-linux/kernel/sched/core.c
# 또는 VS Code
code ~/remote-linux
# 3. 원격 tmux 세션에서 빌드 (별도 터미널)
ssh kernel-build -t "tmux attach -t kernel-dev"
# tmux 내에서: make -j$(nproc)
- sshfs: 별도 에디터 설정 없이 즉시 사용 가능하지만, 대형 커널 트리에서
grep이나find가 느릴 수 있습니다 - VS Code Remote-SSH: 원격 서버에서 직접 clangd가 동작하므로 코드 탐색 성능이 우수하며, 커널 개발에 가장 권장됩니다
- rsync: 양방향 동기화가 필요할 때 사용합니다.
rsync -avz --exclude='.git' ~/linux/ kernel-build:~/linux/로 소스를 동기화하고, 빌드 결과물은rsync -avz kernel-build:~/linux/arch/x86/boot/bzImage ./로 가져옵니다
터미널 멀티플렉서(Terminal Multiplexer) 개요
터미널 멀티플렉서는 하나의 터미널 연결 안에서 여러 가상 터미널을 생성·관리하고, 세션을 분리(detach)/재연결(attach)할 수 있는 도구입니다. 커널 개발에서 특히 중요한 이유는 다음과 같습니다.
- 세션 영속성: SSH 연결이 끊어져도
make -j$(nproc)빌드가 계속 실행됨 - 다중 작업: 편집, 빌드, QEMU, 로그 모니터링을 하나의 세션에서 동시 수행
- 화면 분할: 수평/수직 분할로 여러 작업을 한 화면에서 확인
- 공유 세션: 페어 프로그래밍이나 원격 디버깅 시 동일 세션에 여러 사용자 접속 가능
tmux
tmux(terminal multiplexer)는 BSD 라이선스로 개발된 현대적 터미널 멀티플렉서입니다. 클라이언트-서버 아키텍처로 동작하며, 서버 프로세스(Process)가 모든 세션을 관리하고 클라이언트는 소켓(Socket)을 통해 접속합니다. 커널 개발자들이 가장 널리 사용하는 도구입니다.
tmux 핵심 개념
| 개념 | 설명 | 비유 |
|---|---|---|
| 서버(Server) | 모든 세션을 관리하는 백그라운드 프로세스 | OS 커널 |
| 세션(Session) | 독립된 작업 공간 (여러 윈도우 포함) | 가상 데스크톱 |
| 윈도우(Window) | 세션 내 탭 (전체 화면 차지) | 브라우저 탭 |
| 페인(Pane) | 윈도우 내 분할된 영역 | 화면 분할 |
| 프리픽스 키 | tmux 명령의 시작 키 (기본 Ctrl+b) | Vim의 : 명령 모드 |
tmux 주요 키 바인딩
| 분류 | 키 바인딩 | 동작 |
|---|---|---|
| 세션 | Prefix d | 세션 분리(detach) |
Prefix s | 세션 목록 표시 및 전환 | |
Prefix $ | 세션 이름 변경 | |
Prefix ( / ) | 이전/다음 세션 전환 | |
| 윈도우 | Prefix c | 새 윈도우 생성 |
Prefix n / p | 다음/이전 윈도우 이동 | |
Prefix 0~9 | 번호로 윈도우 이동 | |
Prefix , | 윈도우 이름 변경 | |
Prefix & | 윈도우 종료 (확인 요청) | |
| 페인 | Prefix % | 수직 분할 (좌우) |
Prefix " | 수평 분할 (상하) | |
Prefix 방향키 | 페인 간 이동 | |
Prefix z | 페인 확대/축소(zoom) 토글 | |
Prefix x | 현재 페인 닫기 | |
Prefix Space | 페인 레이아웃 순환 변경 | |
| 복사 모드 | Prefix [ | 복사 모드 진입 (스크롤/검색) |
Space → Enter | 선택 시작 → 복사 | |
Prefix ] | 붙여넣기 | |
/ (복사 모드 중) | 앞으로 검색 |
커널 개발용 tmux 설정
# ~/.tmux.conf — 커널 개발 최적화 설정
# ── 기본 설정 ──
set -g default-terminal "tmux-256color"
set -ag terminal-overrides ",xterm-256color:RGB"
set -g history-limit 100000 # 커널 빌드 로그가 길므로 충분히
set -g mouse on # 마우스 페인 선택/리사이즈
set -g base-index 1 # 윈도우 번호 1부터 시작
setw -g pane-base-index 1 # 페인 번호도 1부터
set -g renumber-windows on # 윈도우 삭제 시 번호 재정렬
set -sg escape-time 0 # Vim ESC 지연 제거
set -g focus-events on # Vim autoread 지원
# ── 프리픽스 키 변경 (Ctrl+a) ──
set -g prefix C-a
unbind C-b
bind C-a send-prefix
# ── 직관적 페인 분할 ──
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"
# ── 페인 이동 (Vim 스타일) ──
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
# ── 페인 리사이즈 (반복 가능) ──
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5
# ── 복사 모드 (vi 키 바인딩) ──
setw -g mode-keys vi
bind -T copy-mode-vi v send -X begin-selection
set -s set-clipboard external # OSC 52 지원 터미널에서 로컬 클립보드 연동
bind -T copy-mode-vi y send -X copy-selection-and-cancel
bind -T copy-mode-vi Escape send -X cancel
# ── 상태바 ──
set -g status-left "[#S@#H] "
set -g status-left-length 30
set -g status-right "#{?client_prefix,#[bg=red] PREFIX ,} %H:%M %d-%b"
set -g status-interval 5
# ── 설정 즉시 리로드 ──
bind r source-file ~/.tmux.conf \; display "Config reloaded!"
tmux 필수 명령어
# 세션 관리
tmux new -s kernel-dev # 세션 생성
tmux ls # 세션 목록
tmux attach -t kernel-dev # 세션 재연결
tmux kill-session -t kernel-dev # 세션 종료
# 분리(detach) 후 재연결 — SSH 끊김 시 빌드 유지의 핵심
# 세션 내에서 Ctrl+a d → SSH 종료 → SSH 재접속 후:
tmux attach -t kernel-dev # 진행 중이던 빌드 화면 그대로
# 세션 외부에서 명령 전송
tmux send-keys -t kernel-dev:build "make -j\$(nproc)" C-m
# 페인 레이아웃 저장/복원 (수동)
tmux list-windows -t kernel-dev -F "#{window_layout}"
# 버퍼 캡처 (빌드 로그 저장)
tmux capture-pane -t kernel-dev:build -p > build.log
# 공유 세션 (페어 디버깅)
tmux new -s shared # 사용자 A: 세션 생성
tmux attach -t shared -r # 사용자 B: 읽기 전용 접속
커널 개발 세션 자동화 스크립트
#!/bin/bash
# kernel-tmux.sh — 커널 개발용 tmux 세션 자동 생성
# 이미 세션이 있으면 재연결, 없으면 새로 생성
SESSION="kernel"
LINUX_DIR="$HOME/linux"
if tmux has-session -t $SESSION 2>/dev/null; then
tmux attach -t $SESSION
exit 0
fi
tmux new-session -d -s $SESSION -c $LINUX_DIR
# 윈도우 1: 에디터
tmux rename-window -t $SESSION:1 "edit"
tmux send-keys -t $SESSION:1 "vim ." C-m
# 윈도우 2: 빌드
tmux new-window -t $SESSION -n "build" -c $LINUX_DIR
# 윈도우 3: QEMU + GDB (수직 분할)
tmux new-window -t $SESSION -n "debug" -c $LINUX_DIR
tmux split-window -h -t $SESSION:3 -c $LINUX_DIR
tmux send-keys -t $SESSION:3.1 "echo '# QEMU: qemu-system-x86_64 -kernel arch/x86/boot/bzImage -s -S -nographic'" C-m
tmux send-keys -t $SESSION:3.2 "echo '# GDB: gdb -ex \"target remote :1234\" vmlinux'" C-m
# 윈도우 4: 로그 모니터링 (수평 3분할)
tmux new-window -t $SESSION -n "logs" -c $LINUX_DIR
tmux split-window -v -t $SESSION:4 -c $LINUX_DIR
tmux split-window -v -t $SESSION:4.2 -c $LINUX_DIR
tmux send-keys -t $SESSION:4.1 "echo '# dmesg -wH'" C-m
tmux send-keys -t $SESSION:4.2 "echo '# tail -f /var/log/kern.log'" C-m
tmux send-keys -t $SESSION:4.3 "echo '# htop'" C-m
tmux select-window -t $SESSION:2
tmux attach -t $SESSION
tmux 유용한 플러그인
| 플러그인 | 기능 | 커널 개발 활용 |
|---|---|---|
tpm | tmux 플러그인 매니저 | 아래 플러그인 설치/관리 |
tmux-resurrect | 세션 저장/복원 | 재부팅 후 빌드 환경 즉시 복구 |
tmux-continuum | 자동 저장 (15분 간격) | resurrect 자동화 |
tmux-yank | 시스템 클립보드 복사 | 빌드 에러 메시지 복사 |
tmux-logging | 페인 출력 자동 로깅 | QEMU 시리얼 콘솔 로그 자동 저장 |
# ~/.tmux.conf — 플러그인 설정 (TPM 사용)
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'tmux-plugins/tmux-continuum'
set -g @plugin 'tmux-plugins/tmux-yank'
# resurrect: Vim 세션도 함께 복원
set -g @resurrect-strategy-vim 'session'
set -g @resurrect-capture-pane-contents 'on'
# continuum: 15분마다 자동 저장, tmux 시작 시 자동 복원
set -g @continuum-save-interval '15'
set -g @continuum-restore 'on'
# TPM 초기화 (반드시 맨 마지막)
run '~/.tmux/plugins/tpm/tpm'
GNU Screen
GNU Screen은 1987년에 처음 공개된 가장 오래된 터미널 멀티플렉서입니다. tmux보다 오래되었지만 여전히 많은 서버에 기본 설치되어 있으며, 특히 레거시(Legacy) 환경이나 임베디드(Embedded) 시스템에서 유용합니다. 커널 개발에서는 시리얼 콘솔(Serial Console) 접속(screen /dev/ttyUSB0)에 특히 많이 쓰입니다.
screen 주요 키 바인딩
| 분류 | 키 바인딩 | 동작 | tmux 대응 |
|---|---|---|---|
| 세션 | Ctrl+a d | 세션 분리(detach) | Prefix d |
Ctrl+a " | 윈도우 목록 | Prefix w | |
Ctrl+a A | 윈도우 이름 변경 | Prefix , | |
| 윈도우 | Ctrl+a c | 새 윈도우 | Prefix c |
Ctrl+a n / p | 다음/이전 윈도우 | Prefix n/p | |
Ctrl+a 0~9 | 번호로 윈도우 이동 | Prefix 0~9 | |
Ctrl+a k | 윈도우 종료 | Prefix & | |
| 분할 | Ctrl+a S | 수평 분할 | Prefix " |
Ctrl+a | | 수직 분할 (v4.1+) | Prefix % | |
Ctrl+a Tab | 분할 영역 이동 | Prefix 방향키 | |
| 복사/스크롤 | Ctrl+a [ | 복사(Copy) 모드 (스크롤) | Prefix [ |
Ctrl+a ] | 붙여넣기 | Prefix ] | |
Ctrl+a H | 화면 로깅 토글 | tmux-logging 플러그인 |
screen 설정과 커널 개발 활용
# ~/.screenrc — GNU Screen 설정
# 시작 메시지 비활성화
startup_message off
# 출력 히스토리 버퍼 확대 (커널 빌드 로그 대비)
defscrollback 50000
# 256색 지원
term "screen-256color"
# vi 키 바인딩 (복사 모드)
markkeys "h=^B:l=^F:$=A"
# 하드스테이터스 (상태바)
hardstatus alwayslastline
hardstatus string '%{= kG}[%{G}%H%{g}] %{= kw}%-w%{= Bw}%n %t%{-}%+w %= %{= kG}%Y-%m-%d %c'
# UTF-8 지원
defutf8 on
# 자동 detach (SSH 끊김 시)
autodetach on
# bell → visual bell
vbell on
# screen 필수 명령어
# 세션 생성 및 관리
screen -S kernel # 이름 있는 세션 생성
screen -ls # 세션 목록
screen -r kernel # 세션 재연결
screen -d -r kernel # 다른 곳에서 detach 후 재연결
screen -x kernel # 공유 세션 접속 (멀티디스플레이)
# 시리얼 콘솔 접속 — 커널 개발의 핵심 용도
screen /dev/ttyUSB0 115200 # 시리얼 디바이스, 보드레이트 지정
screen /dev/ttyACM0 9600 # Arduino/임베디드 보드
# 로깅 — 시리얼 콘솔 출력 자동 저장
screen -L -Logfile serial.log /dev/ttyUSB0 115200
# 원격 명령 전송
screen -S kernel -X stuff "make -j\$(nproc)\n"
screen /dev/ttyUSB0 115200로 접속하면 콘솔 메시지를 실시간(Real-time)으로 확인할 수 있습니다. -L 옵션으로 자동 로깅하면 나중에 분석도 가능합니다. 종료 시 Ctrl+a k를 사용합니다 (Ctrl+c로는 종료되지 않음).
nohup과 disown
멀티플렉서 없이도 SSH 끊김에서 프로세스를 보호하는 간단한 방법이 있습니다. 전체 세션 관리는 불가능하지만, 단일 명령(예: 장시간 커널 빌드)을 보호하기에는 충분합니다.
systemd-run --user --scope는 현재 로그인 세션의 사용자 매니저에 종속됩니다. 마지막 로그인 세션이 종료될 때 lingering이 비활성화되어 있으면 함께 정리될 수 있으므로, SSH 로그아웃 이후까지 확실히 유지해야 하는 빌드는 tmux/screen을 우선 사용하거나 loginctl enable-linger $USER 후 사용자 서비스로 실행하는 편이 안전합니다.
# nohup: SIGHUP 무시 + stdout/stderr를 nohup.out으로 리디렉션
nohup make -j$(nproc) & # 백그라운드 빌드, SSH 끊어져도 유지
nohup make -j$(nproc) > build.log 2&1 & # 출력 파일 지정
# disown: 이미 실행 중인 작업을 쉘 job 목록에서 제거 → SIGHUP 미전송
make -j$(nproc) & # 백그라운드 실행
disown %1 # job 1을 쉘에서 분리
# disown -h: HUP 무시만 설정 (job 목록에서는 유지)
make -j$(nproc) &
disown -h %1 # HUP만 무시, jobs로 여전히 확인 가능
# 실행 중인 포그라운드 프로세스를 백그라운드로 전환 → disown
# 1. Ctrl+z (중지)
# 2. bg (백그라운드 재개)
# 3. disown %1 (쉘에서 분리)
# ── systemd-run: 사용자 서비스 기반 대안 (systemd 기반 시스템) ──
# SSH 로그아웃 뒤에도 유지하려면 lingering 활성화가 필요할 수 있음
loginctl enable-linger $USER
# 분리된 사용자 서비스로 실행 (현재 디렉토리 유지)
systemd-run --user --unit=kernel-build --same-dir --collect make -j$(nproc)
# 현재 로그인 세션 안에서만 묶어둘 일회성 작업
systemd-run --user --scope make olddefconfig
# 상태 확인
systemctl --user status kernel-build.service
# 로그 확인
journalctl --user -u kernel-build.service -f
| 방법 | 용도 | 한계 |
|---|---|---|
nohup cmd & | 새로 실행하는 명령 보호 | 출력 확인 어려움, 상호작용 불가 |
disown | 이미 실행 중인 작업 보호 | 터미널 분리 후 재연결 불가 |
systemd-run --user | lingering이 설정된 사용자 서비스에서 독립 실행 | linger 설정 필요 가능, 로그는 journalctl로 확인 |
tmux / screen | 전체 세션 관리 | 별도 설치 필요할 수 있음 |
터미널 멀티플렉서 비교
| 기능 | tmux | GNU Screen | Zellij |
|---|---|---|---|
| 초기 릴리스 | 2007 | 1987 | 2021 |
| 라이선스 | ISC (BSD 계열) | GPL-3.0 | MIT |
| 언어 | C | C | Rust |
| 아키텍처 | 클라이언트-서버 (소켓) | 단일 프로세스 (SUID) | 클라이언트-서버 (WASM 플러그인) |
| 수직 분할 | 기본 지원 | v4.1+ (Ctrl+a |) | 기본 지원 |
| 수평 분할 | 기본 지원 | 기본 지원 (Ctrl+a S) | 기본 지원 |
| 설정 | ~/.tmux.conf | ~/.screenrc | KDL (config.kdl) |
| 플러그인 | TPM (풍부한 생태계) | 없음 | WASM 플러그인 |
| 스크립팅 | 강력 (명령줄 API) | 제한적 | WASM + CLI |
| 시리얼 접속 | 불가 (별도 도구 필요) | 내장 지원 | 불가 |
| UI 힌트 | 없음 (학습 필요) | 없음 (학습 필요) | 키 바인딩 힌트 표시 |
| 기본 설치 빈도 | 높음 (대부분 배포판) | 매우 높음 (레거시 포함) | 낮음 (별도 설치) |
| Mosh 호환 | 호환 (SSH 레이어 위) | 호환 (SSH 레이어 위) | 호환 (SSH 레이어 위) |
| 커널 개발 추천 | 최우선 권장 | 시리얼 콘솔/레거시 환경 | 대안 (발전 중) |
- 일반 커널 개발: tmux 권장 — 자유로운 화면 분할, 강력한 스크립팅, 플러그인 생태계
- 시리얼 콘솔 디버깅: screen 권장 —
screen /dev/ttyUSB0 115200로 즉시 접속 - 멀티플렉서 처음 사용: Zellij 고려 — UI 힌트가 키 바인딩 학습 부담을 줄여줌
- 멀티플렉서 없는 환경:
nohup/disown으로 최소한의 세션 보호
VS Code Remote Development
VS Code의 Remote-SSH 확장을 사용하면 로컬에서 편집하면서 원격 서버의 clangd, 빌드 시스템(Build System)을 그대로 활용할 수 있습니다. 커널처럼 대규모 C 코드베이스에서는 원격 서버에서 clangd가 직접 동작하므로, sshfs 방식보다 코드 탐색·자동완성 성능이 훨씬 우수합니다.
Remote-SSH 확장 설정
VS Code 마켓플레이스에서 Remote - SSH(ms-vscode-remote.remote-ssh) 확장을 설치한 후, F1 → Remote-SSH: Connect to Host로 ~/.ssh/config에 설정된 호스트에 접속합니다. 첫 접속 시 원격 서버에 VS Code Server가 자동 설치됩니다.
# ~/.ssh/config — VS Code Remote에 최적화된 설정
Host kernel-build
HostName 192.168.1.100
User kdev
IdentityFile ~/.ssh/id_ed25519
# VS Code 재연결 속도 향상
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 연결 유지
ServerAliveInterval 60
ServerAliveCountMax 3
# GDB 포트 포워딩
LocalForward 1234 localhost:1234
원격 서버 설정
기본 VS Code 설정(확장 프로그램, settings.json, tasks.json, launch.json)은 에디터 설정 가이드와 동일합니다. 아래는 원격 환경에서 추가로 필요한 설정만 설명합니다.
// 원격 서버의 .vscode/settings.json — 원격 환경 추가 설정
{
"remote.SSH.remotePlatform": {
"kernel-build": "linux"
},
"clangd.path": "/usr/bin/clangd-18",
"telemetry.telemetryLevel": "off"
}
원격 커널 디버깅 통합
SSH 포트 포워딩(LocalForward 1234 localhost:1234)으로 GDB 포트를 로컬에 노출하면, 에디터 설정 가이드의 launch.json 설정을 그대로 사용하여 원격 QEMU에서 커널을 디버깅할 수 있습니다.
VS Code Remote 최적화
- SSH 멀티플렉싱:
ControlMaster auto로 연결 재사용 → VS Code 재연결 속도 대폭 향상 - 파일 감시 제외:
.git/objects,build/,*.o를 감시 대상에서 제외하여 CPU/메모리 절약 - clangd 메모리 제한: 커널 트리에서 clangd가 메모리를 많이 사용할 수 있습니다.
"clangd.arguments": ["--malloc-trim", "-j=2"]로 제한합니다 - 원격 서버 디스크: VS Code Server는
~/.vscode-server에 약 500MB를 사용합니다. 디스크 여유를 확인합니다 - tmux + VS Code: 장시간 빌드는 VS Code 터미널 대신 tmux 세션에서 실행하면 SSH 끊김에도 빌드가 유지됩니다
원격 커널 빌드 및 배포 워크플로
원격 빌드 서버에서 커널을 빌드한 후, 타겟 보드나 VM에 배포하여 테스트하는 전체 워크플로를 설명합니다.
빌드 서버에서 타겟으로 배포
커널 이미지와 모듈을 빌드 서버에서 타겟 시스템으로 전송하는 방법은 환경에 따라 다릅니다.
update-initramfs와 update-grub 단계는 Debian/Ubuntu 계열 기준입니다. Fedora/RHEL 계열은 보통 dracut, 임베디드/U-Boot 환경은 별도 부트 엔트리 갱신이나 네트워크 부팅 절차가 필요하므로 그대로 복사하지 말고 대상 환경에 맞게 치환해야 합니다.
# ── rsync: 빌드 서버 → 타겟 (SSH 기반, 가장 범용적) ──
# 커널 이미지 전송
rsync -avz arch/x86/boot/bzImage root@target-board:/boot/vmlinuz-custom
# 모듈 전송 (INSTALL_MOD_PATH로 설치 후 전송)
make modules_install INSTALL_MOD_PATH=/tmp/modules
rsync -avz /tmp/modules/lib/modules/ root@target-board:/lib/modules/
# ── 자동 빌드+배포 스크립트 ──
#!/bin/bash
# deploy-kernel.sh — 커널 빌드 후 타겟에 자동 배포
TARGET="root@target-board"
KVER=$(make kernelrelease)
# 빌드
make -j$(nproc) || exit 1
make modules -j$(nproc) || exit 1
# 모듈 설치 (임시 디렉토리)
make modules_install INSTALL_MOD_PATH=/tmp/kmod
# 타겟으로 전송
rsync -avz arch/x86/boot/bzImage ${TARGET}:/boot/vmlinuz-${KVER}
rsync -avz /tmp/kmod/lib/modules/${KVER}/ ${TARGET}:/lib/modules/${KVER}/
# 타겟에서 initramfs 재생성 및 부트로더 업데이트 (Debian/Ubuntu 예시)
ssh ${TARGET} "update-initramfs -c -k ${KVER} && update-grub"
echo "배포 완료: ${KVER}"
# ── NFS root: 임베디드 개발에 편리한 네트워크 부팅 ──
# 빌드 서버에서 NFS 루트 파일시스템 설정 (/etc/exports)
# /srv/nfs/rootfs 10.0.1.200(rw,no_root_squash,no_subtree_check)
# 커널 모듈을 NFS root에 직접 설치 → 타겟 재부팅만으로 반영
make modules_install INSTALL_MOD_PATH=/srv/nfs/rootfs
# ── TFTP: U-Boot 환경에서 커널 이미지 네트워크 부팅 ──
# 빌드 서버 TFTP 디렉토리에 커널 이미지 복사
cp arch/arm64/boot/Image /srv/tftp/
# U-Boot 명령 (타겟 시리얼 콘솔에서)
# => setenv serverip 10.0.1.50
# => tftp ${kernel_addr_r} Image
# => tftp ${fdt_addr_r} board.dtb
# => booti ${kernel_addr_r} - ${fdt_addr_r}
원격 디버깅 워크플로
빌드 서버에서 QEMU를 실행하거나, 원격 타겟 보드의 KGDB stub에 SSH 터널을 통해 GDB를 연결하는 워크플로입니다.
127.0.0.1)에만 바인딩하고 SSH 포트 포워딩으로 접근하십시오. 0.0.0.0에 열면 같은 네트워크의 다른 호스트가 접속할 수 있습니다.
# ── 빌드 서버에서 QEMU + GDB (로컬에서 SSH 터널로 접근) ──
# 1. 빌드 서버의 tmux 세션에서 QEMU 실행
qemu-system-x86_64 \
-kernel arch/x86/boot/bzImage \
-append "console=ttyS0 nokaslr root=/dev/sda rw" \
-drive file=rootfs.img,format=raw \
-nographic \
-enable-kvm -m 2G -smp 4 \
-gdb tcp::1234 -S # GDB stub 활성화, 시작 시 정지
# 2. 로컬에서 SSH 포트 포워딩으로 GDB 연결
ssh -L 1234:localhost:1234 kernel-build
# 별도 터미널에서:
gdb vmlinux -ex "target remote localhost:1234"
# ── 시리얼 콘솔 네트워크 접속 (socat / ser2net) ──
# 빌드 서버의 loopback에만 바인딩하여 시리얼 디바이스를 TCP로 노출 (socat)
socat TCP-LISTEN:2222,bind=127.0.0.1,reuseaddr,fork FILE:/dev/ttyUSB0,b115200,raw,echo=0
# 로컬에서 SSH 터널을 통해 접속
ssh -L 2222:localhost:2222 kernel-build
# 별도 터미널에서 시리얼 콘솔 접속:
socat - TCP:localhost:2222
# 또는 minicom -D tcp:localhost:2222
원격 QEMU 접근
빌드 서버에서 실행 중인 QEMU의 그래픽 콘솔이나 시리얼 콘솔에 로컬에서 접근하는 방법입니다.
# ── VNC: QEMU 그래픽 콘솔 원격 접근 ──
# 빌드 서버에서 QEMU 실행 (VNC 활성화)
qemu-system-x86_64 \
-kernel arch/x86/boot/bzImage \
-vnc :0 # VNC 포트 5900
-enable-kvm -m 2G
# 로컬에서 SSH 터널로 VNC 접속
ssh -L 5900:localhost:5900 kernel-build
# VNC 클라이언트로 localhost:5900 접속
# ── 시리얼 콘솔: QEMU 텍스트 콘솔 원격 접근 ──
# QEMU의 시리얼 콘솔을 loopback TCP로 노출
qemu-system-x86_64 \
-kernel arch/x86/boot/bzImage \
-append "console=ttyS0 nokaslr" \
-serial tcp:127.0.0.1:4444,server=on,wait=off \
-nographic -enable-kvm -m 2G
# 로컬에서 SSH 터널로 시리얼 콘솔 접속
ssh -L 4444:localhost:4444 kernel-build
socat - TCP:localhost:4444
# ── QEMU Monitor: 원격 QEMU 제어 ──
# QEMU Monitor를 loopback TCP로 노출
qemu-system-x86_64 \
-kernel arch/x86/boot/bzImage \
-monitor tcp:127.0.0.1:4445,server=on,wait=off \
-nographic -enable-kvm -m 2G
# 로컬에서 QEMU Monitor 접속 (VM 상태 확인, 스냅샷 등)
ssh -L 4445:localhost:4445 kernel-build
socat - TCP:localhost:4445
# (qemu) info registers
# (qemu) savevm checkpoint1
트러블슈팅
SSH 문제 해결
| 증상 | 원인 | 해결 방법 |
|---|---|---|
Connection refused | SSH 서버 미실행 또는 포트 불일치 | sudo systemctl start sshd, 포트 확인 (ss -tlnp | grep ssh) |
Permission denied (publickey) | 키 미등록 또는 권한 문제 | ssh-copy-id 재실행, ~/.ssh 권한 확인 (700), 키 파일 권한 확인 (600) |
Connection timed out | 방화벽 차단 또는 네트워크 문제 | 방화벽 규칙 확인, telnet host 22로 포트 테스트 |
| ControlMaster 소켓 오류 | 비정상 종료로 소켓 파일 잔류 | rm ~/.ssh/sockets/* 후 재접속 |
| 포트 포워딩 실패 | 원격 포트가 이미 사용 중 | ssh -v로 로그 확인, 원격에서 ss -tlnp으로 포트 점유 확인 |
| SSH 연결 느림 | DNS 역방향 조회 지연 | 서버 sshd_config에 UseDNS no 설정 |
# SSH 연결 문제 디버깅
ssh -vvv kernel-build # 상세 디버그 출력
# ControlMaster 소켓 정리
rm -f ~/.ssh/sockets/*
# 특정 연결의 소켓 상태 확인
ssh -O check kernel-build
tmux 문제 해결
| 증상 | 원인 | 해결 방법 |
|---|---|---|
sessions should be nested 경고 | tmux 안에서 tmux 실행 시도 | TMUX= 환경 변수 해제 후 실행, 또는 tmux attach 사용 |
| 256색이 표시되지 않음 | TERM 환경변수 불일치 | set -g default-terminal "tmux-256color" 설정, SSH 접속 시 TERM 확인 |
| 클립보드 복사 안 됨 | 원격 tmux에서 로컬 클립보드 접근 불가 | tmux-yank 플러그인 + set-clipboard on, 또는 OSC 52 이스케이프 사용 |
| 마우스 스크롤 미동작 | 마우스 모드 미설정 | set -g mouse on 추가 |
| 세션 복원 실패 | tmux-resurrect 저장 파일 손상 | ~/.tmux/resurrect/ 디렉토리에서 이전 저장 파일 복원 |
VS Code Remote 문제 해결
| 증상 | 원인 | 해결 방법 |
|---|---|---|
| 원격 서버 연결 실패 | VS Code Server 설치 실패 | 원격에서 ~/.vscode-server 삭제 후 재시도, 디스크 공간 확인 |
| 높은 CPU 사용률 | 파일 감시자(File Watcher) 과다 | files.watcherExclude에 build/, .git/objects/ 추가 |
| clangd OOM 종료 | 커널 트리가 너무 큼 | -j=2로 인덱싱 스레드(Thread) 제한, --malloc-trim 옵션 추가 |
| 재연결 느림 | SSH 매번 새 연결 생성 | ControlMaster auto + ControlPersist 600 설정 |
| 확장 프로그램 미동작 | 원격 측에 확장 미설치 | 확장 관리 화면에서 "Install in SSH: host" 버튼으로 원격 설치 |