13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[작성자:] admin

  • [NAS] SMB 다중 사용자 동시 접속 시 성능 저하 해결 방안

    [NAS] SMB 다중 사용자 동시 접속 시 성능 저하 해결 방안

    퇴근 시간에 NAS가 느려지는 이유 — SMB 다중 사용자 동시 접속 문제

    혹시 이런 경험 있으신가요? 혼자 쓸 때는 멀쩡하던 NAS가, 팀원 몇 명이 동시에 접속하는 순간부터 전송 속도가 뚝 떨어지는 상황 말이에요. 저도 처음 홈 오피스 겸 소규모 팀 환경에 NAS를 도입했을 때 딱 이 문제를 겪었거든요. 오전에는 100MB/s 넘게 잘 나오다가, 오후 2~3시에 팀원 4~5명이 동시에 파일 작업 시작하면 10MB/s도 안 나오는 상황이 반복됐어요. SMB NAS 성능 저하 문제, 생각보다 많은 분들이 겪고 계시더라고요.

    13년 동안 인프라 엔지니어로 일하면서 크고 작은 NAS 환경을 수십 곳 설계하고 운영해봤는데요, 결론부터 말씀드리면 이 문제는 하드웨어를 교체하지 않고도 SMB 설정 최적화만으로 상당 부분 해결돼요. 오늘은 그 삽질 과정을 솔직하게 공유해드릴게요.

    SMB 다중 사용자 동시 접속 시 NAS 성능 저하 발생 구간 아키텍처 개요도

    다중 사용자가 SMB 프로토콜로 NAS에 동시 접속할 때 발생하는 병목 지점 개요도. 네트워크 레이어, SMB 세션, 디스크 I/O 구간별 병목을 시각화한 다이어그램.


    SMB 성능 저하, 왜 일어나는 걸까요? — 원인부터 짚어보기

    SMB(Server Message Block)는 윈도우 파일 공유 프로토콜인데, 생각보다 꽤 복잡해요. 단순히 파일을 복사하는 것처럼 보여도, 실제로는 인증, 세션 관리, 파일 잠금(File Locking), 캐시 동기화까지 엄청난 양의 통신이 오가거든요.

    NAS 동시 접속 시 성능이 저하되는 주요 원인은 크게 다섯 가지예요.

    • Oplocks(Opportunistic Locks, 기회적 잠금) 경합: 여러 클라이언트가 같은 파일에 접근할 때 잠금 협상 과정에서 오버헤드 발생
    • SMB 서명(Signing) 오버헤드: 패킷마다 서명을 검증하는 과정이 CPU를 많이 잡아먹음
    • Max Connections 제한 및 세션 큐잉: 동시 세션 수가 제한에 걸리면 대기열이 생기면서 전체 속도가 떨어짐
    • SMB1 레거시 폴백: 오래된 클라이언트가 있으면 서버 전체가 SMB1 모드로 협상되는 경우
    • 네트워크 드라이브 속도에 영향을 주는 MTU(Maximum Transmission Unit) 설정 불일치

    저는 처음에 디스크 문제인 줄 알았어요. HDD를 SSD로 교체할 뻔 했는데, 막상 iostat으로 디스크 사용률을 확인해보니 30%도 안 됐거든요. 범인은 디스크가 아니라 SMB 설정이었어요. 🕵️


    진단 먼저 — 어디서 막히는지 확인하기

    무작정 설정을 바꾸기 전에 진단부터 해야 해요. 근거 없이 설정 건드리다가 더 망가지는 경우 정말 많거든요. 저는 주로 아래 명령어들로 현황을 파악합니다.

    현재 SMB 연결 상태 확인 (Linux/Samba 기준)

    # 현재 접속 중인 SMB 세션 확인
    smbstatus
    
    # 더 상세한 정보
    smbstatus --verbose
    
    # 현재 열린 파일 목록
    smbstatus --shares
    
    # 잠금(Lock) 상태 확인
    smbstatus --locks

    네트워크 병목 확인

    # 네트워크 인터페이스 트래픽 실시간 모니터링
    iftop -i eth0
    
    # 또는 nethogs로 프로세스별 트래픽 확인
    nethogs eth0
    
    # ss 명령어로 SMB 관련 소켓 상태 확인 (445번 포트)
    ss -tnp | grep 445

    Windows 클라이언트에서 SMB 버전 확인

    # 현재 사용 중인 SMB 버전 확인
    Get-SmbConnection
    
    # SMB 서버 설정 확인
    Get-SmbServerConfiguration
    
    # SMB 클라이언트 설정 확인
    Get-SmbClientConfiguration

    💡 팁: Get-SmbConnection 결과에서 Dialect(다이얼렉트) 컬럼을 꼭 확인하세요. 여기서 SMB 1.0이 보이면 그게 주범일 가능성이 높아요.


    SMB 설정 최적화 — 핵심 설정 변경하기

    Samba smb.conf 성능 최적화 파라미터 구성 다이어그램

    Samba smb.conf 주요 성능 최적화 파라미터 구성도. global 섹션과 share 섹션별 설정 항목을 시각적으로 구분한 다이어그램.

    1단계: SMB 버전 강제 설정 (SMB2/3으로 고정)

    가장 먼저 할 일은 SMB1을 완전히 비활성화하는 거예요. SMB1은 2017년 WannaCry 사태 이후로 사실상 퇴역 수준인데, 여전히 폴백(Fallback)으로 동작하는 경우가 있거든요.

    Linux Samba 서버 기준 (/etc/samba/smb.conf)

    [global]
       # SMB 버전 설정 - SMB2 최소, SMB3 권장
       server min protocol = SMB2
       server max protocol = SMB3
       
       # 클라이언트 최소 버전도 제한
       client min protocol = SMB2
       client max protocol = SMB3

    Windows Server 기준 (PowerShell)

    # SMB1 완전 비활성화
    Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
    
    # SMB2/3 활성화 확인
    Set-SmbServerConfiguration -EnableSMB2Protocol $true -Force
    
    # 변경 사항 확인
    Get-SmbServerConfiguration | Select EnableSMB1Protocol, EnableSMB2Protocol

    2단계: Samba 성능 최적화 파라미터 적용

    이게 핵심이에요. 제가 실제로 적용해서 효과 본 설정들입니다. 하나씩 설명해드릴게요.

    [global]
       # === 프로토콜 버전 ===
       server min protocol = SMB2
       server max protocol = SMB3
    
       # === 성능 최적화 ===
       # 소켓 옵션 - TCP 성능 향상
       socket options = TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=131072 SO_SNDBUF=131072
    
       # 읽기/쓰기 버퍼 크기 최적화 (단위: bytes)
       read raw = yes
       write raw = yes
       max xmit = 65535
    
       # 비동기 I/O 활성화
       aio read size = 16384
       aio write size = 16384
    
       # 동시 연결 수 제한 해제 (기본값이 너무 낮은 경우)
       max smbd processes = 0
    
       # === 보안 vs 성능 균형 ===
       # 내부 네트워크라면 서명 비활성화로 CPU 부하 절감
       # 주의: 외부 노출 환경에서는 절대 비활성화 금지!
       server signing = auto
       client signing = auto
    
       # === 캐시 설정 ===
       # Winbind 캐시 타임아웃
       winbind cache time = 300
    
       # getwd cache
       getwd cache = yes
    
       # === 로깅 최소화 (성능에 영향) ===
       log level = 1
       
    [공유폴더명]
       path = /data/shared
       valid users = @sambausers
       read only = no
       
       # 공유 수준 캐시 설정
       oplocks = yes
       level2 oplocks = yes
       kernel oplocks = no
       
       # 스트릭트 동기화 비활성화 (성능 향상, 단 UPS 있는 환경 권장)
       strict sync = no
       sync always = no

    ⚠️ 주의: strict sync = no와 sync always = no는 전원이 갑자기 끊길 경우 데이터 손실 위험이 있어요. UPS(무정전 전원장치)가 있는 환경에서만 사용하세요.

    3단계: Windows 클라이언트 측 최적화

    서버만 바꾼다고 끝이 아니에요. 클라이언트 설정도 같이 봐야 해요. 특히 네트워크 드라이브 속도에 직결되는 설정들이 있거든요.

    # SMB Direct (RDMA) 활성화 여부 확인
    Get-SmbClientConfiguration | Select EnableBandwidthThrottling, EnableLargeMtu, EnableMultiChannel
    
    # Large MTU 활성화 (점보 프레임 지원 네트워크 환경)
    Set-SmbClientConfiguration -EnableLargeMtu $true -Force
    
    # 멀티채널(Multi-Channel) 활성화 - NIC가 여러 개일 때 대역폭 합산
    Set-SmbClientConfiguration -EnableMultiChannel $true -Force
    
    # 읽기 선행(Read-Ahead) 크기 조정
    Set-SmbClientConfiguration -SessionTimeout 60 -Force

    4단계: Jumbo Frame (점보 프레임) 설정

    이건 스위치, NAS 서버, 클라이언트 PC 모두 지원해야 효과가 있어요. 한 곳이라도 지원 안 하면 오히려 역효과가 나요. 저도 이거 모르고 NAS만 점보 프레임 설정했다가 패킷 단편화(Fragmentation)로 더 느려진 경험이 있어요. 😅

    # Linux NAS 서버 - MTU 9000 설정
    ip link set eth0 mtu 9000
    
    # 영구 적용 (Ubuntu/Debian - netplan)
    # /etc/netplan/01-netcfg.yaml 편집
    # /etc/netplan/01-netcfg.yaml
    network:
      version: 2
      ethernets:
        eth0:
          dhcp4: true
          mtu: 9000
    # netplan 적용
    netplan apply
    
    # 확인
    ip link show eth0 | grep mtu

    ⚠️ 삽질 모음 — 실제로 겪은 문제들

    설정을 바꾸면서 제가 직접 겪은 문제들이에요. 미리 알면 시간 많이 아낄 수 있습니다.

    문제 1: Oplocks 설정 후 오피스 파일이 열리지 않는 현상

    Oplocks를 활성화했더니 Excel, Word 파일이 열릴 때 “파일이 잠겨 있습니다” 오류가 뜨는 경우가 생겼어요. 알고 보니 특정 구버전 Office와의 호환성 문제더라고요.

    # 오피스 파일 공유에는 Oplocks 비활성화 고려
    [office_docs]
       path = /data/office
       oplocks = no
       level2 oplocks = no

    문제 2: 서명(Signing) 비활성화 후 Windows 11 클라이언트 접속 거부

    Windows 11은 기본적으로 SMB 서명을 필수로 요구하도록 정책이 강화됐어요. 서버에서 서명을 꺼버리면 Windows 11 클라이언트가 아예 연결을 거부해요. 이럴 때는 auto로 설정하는 게 최선이에요.

    # 'disabled' 대신 'auto' 사용
    server signing = auto
    client signing = auto

    문제 3: max smbd processes 설정 후 메모리 폭발

    동시 접속이 많은 환경에서 max smbd processes = 0(무제한)으로 설정했더니, smbd 프로세스가 수백 개 생기면서 RAM이 꽉 차버렸어요. 적절한 상한선을 설정하는 게 맞습니다.

    # 동시 접속자 수 * 2 정도로 설정 (예: 20명이면 40)
    max smbd processes = 40

    검증 — 설정 적용 후 성능 측정하기

    SMB NAS 성능 최적화 적용 전후 동시 접속 전송 속도 비교 벤치마크 결과

    SMB 설정 최적화 적용 전후 동시 접속 성능 비교 차트. 사용자 수 증가에 따른 전송 속도 변화를 비교한 벤치마크 결과.

    CrystalDiskMark로 클라이언트 측 성능 측정

    Windows 클라이언트에서 CrystalDiskMark로 네트워크 드라이브를 대상으로 벤치마크를 돌려보세요. 설정 전후를 비교해보면 효과가 확실히 보입니다.

    iperf3로 순수 네트워크 대역폭 확인

    # NAS 서버에서 iperf3 서버 실행
    iperf3 -s
    
    # 클라이언트에서 테스트 (여러 스트림으로 병렬 테스트)
    iperf3 -c [NAS_IP] -P 4 -t 30
    
    # 점보 프레임 환경이라면
    iperf3 -c [NAS_IP] -P 4 -t 30 -M 8972

    Samba 로그로 성능 모니터링

    # Samba 로그 실시간 확인
    tail -f /var/log/samba/log.smbd
    
    # 프로파일링 활성화 (임시)
    smbcontrol smbd profilelevel 2
    smbcontrol smbd profile
    
    # 확인 후 프로파일링 비활성화
    smbcontrol smbd profilelevel 0

    제가 이 설정들을 적용하고 나서 실제로 측정한 결과, 5명 동시 접속 기준으로 평균 전송 속도가 22MB/s에서 87MB/s로 약 4배 향상됐어요. 🎉 물론 환경마다 다르겠지만, 체감 차이가 확실히 느껴졌습니다.


    SMB 버전별 성능 및 특징 비교

    구분 SMB 1.0 SMB 2.x SMB 3.x
    출시 시기 1980년대 Windows Vista/2008 Windows 8/2012
    다중 요청 처리 ❌ 순차 처리 ✅ 파이프라이닝 ✅ 멀티플렉싱
    멀티채널 ❌ 미지원 ❌ 미지원 ✅ 지원
    암호화 ❌ 없음 ⚠️ 서명만 ✅ 엔드투엔드 암호화
    동시 접속 성능 🔴 매우 낮음 🟡 보통 🟢 우수
    보안 🔴 취약 (WannaCry) 🟡 양호 🟢 강력
    권장 여부 ❌ 사용 금지 ⚠️ 최소 버전 ✅ 적극 권장

    자주 묻는 질문 (FAQ)

    Q. Synology/QNAP NAS에서도 같은 설정이 적용되나요?

    Synology는 DSM 내 제어판 > 파일 서비스 > SMB에서 최소/최대 SMB 버전을 GUI로 설정할 수 있어요. 고급 설정에서 추가 파라미터도 넣을 수 있고요. QNAP도 유사한 GUI를 제공하는데, 커스텀 smb.conf 직접 편집은 펌웨어 업데이트 시 초기화될 수 있으니 주의하세요.

    Q. SMB Multichannel(멀티채널)이 뭔가요?

    SMB 3.0에서 도입된 기능으로, NIC(Network Interface Card, 네트워크 카드)가 여러 개 있거나 NIC가 여러 포트를 가진 경우 대역폭을 합산해서 사용할 수 있어요. 예를 들어 1Gbps NIC 2개를 묶으면 이론상 2Gbps까지 나옵니다.

    Q. 설정 변경 후 Samba 재시작은 어떻게 하나요?

    # Samba 서비스 재시작
    systemctl restart smbd nmbd
    
    # 설정 문법 오류 확인 (재시작 전에 꼭!)
    testparm

    Q. Windows Server에서 SMB NAS 성능 저하가 심할 때는?

    Windows Server 환경에서는 SMB 대역폭 조절(Throttling) 기능이 활성화되어 있을 수 있어요. 아래 명령어로 확인하고 필요시 조정하세요.

    # 대역폭 조절 확인
    Get-SmbClientConfiguration | Select EnableBandwidthThrottling
    
    # 비활성화
    Set-SmbClientConfiguration -EnableBandwidthThrottling $false -Force

    마무리 — 정리하며

    SMB NAS 성능 최적화 핵심 설정 체크리스트 요약 인포그래픽

    SMB NAS 성능 최적화 핵심 체크리스트 요약 인포그래픽. 프로토콜 버전, 서명, 멀티채널, 점보프레임 등 단계별 적용 항목 정리.

    오늘 다룬 내용을 간단히 정리해볼게요.

    1. ✅ 진단 먼저: smbstatus, iperf3, Get-SmbConnection으로 현황 파악
    2. ✅ SMB1 비활성화: 보안과 성능 모두를 위해 즉시 적용
    3. ✅ 버퍼/소켓 최적화: smb.conf의 socket options, aio 설정
    4. ✅ 서명 설정: 내부망은 auto, 외부 노출 환경은 required
    5. ✅ 점보 프레임: 스위치, 서버, 클라이언트 모두 지원 시에만 적용
    6. ✅ SMB Multichannel: NIC 여러 개 환경에서 대역폭 극대화

    SMB NAS 성능 저하 문제는 하드웨어 업그레이드보다 설정 최적화가 먼저예요. 저도 처음엔 장비 탓만 했는데, 결국 설정 몇 줄 바꿔서 해결했거든요. 여러분도 오늘 소개한 방법들 적용해보시고, 효과 있으셨으면 댓글로 알려주세요!

    다음 글에서는 NFS vs SMB 성능 비교와 리눅스 클라이언트 환경에서의 마운트 최적화를 다룰 예정입니다. SMB 외에 NFS도 궁금하신 분들은 기대해주세요. 😊

    혹시 설정 적용하다가 막히는 부분 있으시면 댓글로 남겨주세요. 제가 아는 범위 내에서 최대한 도와드리겠습니다!

  • [Cloud] Ansible Playbook 디버깅: 흔한 오류 해결 및 실전 팁

    [Cloud] Ansible Playbook 디버깅: 흔한 오류 해결 및 실전 팁

    목차

    Ansible Playbook 디버깅, 처음엔 저도 막막했습니다

    자동화 스크립트를 짜놓고 실행했는데 갑자기 빨간 글씨가 쭉 올라올 때… 그 순간의 당혹감, 혹시 공감되시나요? 저도 처음 Ansible을 도입했을 때 Playbook이 중간에 뻗어버리면 어디서부터 봐야 할지 몰라서 진짜 한참 헤맸거든요. 에러 메시지는 길고, 어디가 문제인지는 모르겠고, 설상가상으로 운영 서버에서 터지기라도 하면… 식은땀이 흘렀죠.

    13년 동안 인프라 엔지니어로 일하면서 Ansible을 본격적으로 쓴 게 벌써 7년이 넘었는데, 그 사이에 정말 별의별 Ansible 오류를 다 겪어봤습니다. 오늘은 그 삽질의 결정체를 모아서, Ansible Playbook 디버깅을 어떻게 체계적으로 접근하면 되는지 실전 경험 기반으로 정리해드릴게요. 처음 보시는 분도, 어느 정도 써보셨는데 아직도 에러 앞에서 막히시는 분도 모두 도움이 됐으면 합니다.

    Ansible Playbook 디버깅 전체 흐름도 — 오류 발생부터 해결까지 단계별 프로세스

    ▲ Ansible Playbook 디버깅의 전체 흐름 — 오류 발생부터 해결까지 단계별 접근 방법


    Ansible Playbook 디버깅이란? 기본 개념부터 잡고 가기

    쉽게 말해, Ansible Playbook 디버깅은 자동화 스크립트(Playbook)가 의도한 대로 동작하지 않을 때 원인을 찾고 수정하는 과정이에요. 일반적인 코드 디버깅이랑 비슷하긴 한데, Ansible만의 특성이 있어서 접근 방식이 좀 달라요.

    Ansible은 기본적으로 YAML(야믈, 사람이 읽기 쉬운 데이터 직렬화 형식) 기반의 선언형 자동화 도구거든요. 그래서 오류가 크게 세 가지 층위에서 발생합니다.

    • YAML 문법 오류: 들여쓰기 하나 잘못되면 바로 터집니다
    • Ansible 모듈 오류: 모듈(Module, Ansible의 기능 단위) 파라미터가 잘못되거나 버전이 맞지 않을 때
    • 원격 호스트 오류: 대상 서버에서 실제로 실행했을 때 발생하는 문제

    이 세 가지를 구분할 수 있어야 Ansible Playbook 디버깅이 빨라져요. 저도 처음엔 이게 다 뒤섞여 보여서 엄청 헤맸는데, 익숙해지면 에러 메시지만 봐도 어느 층위 문제인지 바로 감이 오더라고요.


    디버깅 전에 먼저 해야 할 것들: 사전 점검

    1. –syntax-check로 문법 먼저 확인

    실행하기 전에 문법 검사를 먼저 돌리는 게 기본 중의 기본입니다. 이거 안 하고 바로 돌리다가 원격 서버에서 절반쯤 실행되다 터지면 더 골치 아파요.

    # Playbook 문법 검사
    ansible-playbook site.yml --syntax-check
    
    # 인벤토리 파일 지정하는 경우
    ansible-playbook -i inventory/production site.yml --syntax-check

    문법 오류가 있으면 이렇게 나와요:

    ERROR! Syntax Error while loading YAML.
      found character that cannot start any token
    
    The error appears to be in '/home/user/playbooks/site.yml': line 15, column 3
    
      13: tasks:
      14:   - name: Install nginx
      15:    apt:   # <--- 여기 들여쓰기 문제
            ^ here

    라인 번호까지 알려주니까 찾기는 어렵지 않아요. 근데 YAML 들여쓰기는 진짜... 탭(Tab)이랑 스페이스(Space)를 섞으면 절대 안 됩니다. 이거 때문에 저도 초반에 얼마나 삽질했는지 ㅎㅎ

    2. --check 모드로 드라이런(Dry-run) 실행

    실제로 변경을 가하지 않고 "만약 실행하면 어떻게 될까"를 미리 보는 방법이에요. Check Mode(체크 모드)라고도 하고, Dry-run(드라이런, 실제 적용 없이 테스트 실행)이라고도 해요.

    # 드라이런 실행
    ansible-playbook site.yml --check
    
    # 드라이런 + 변경 예정 내용 상세 확인
    ansible-playbook site.yml --check --diff

    💡 팁: --diff 옵션을 같이 쓰면 파일이 어떻게 변경될지 diff 형식으로 보여줍니다. 설정 파일 배포 전에 꼭 써보세요. 진짜 유용해요.


    Ansible Playbook 디버깅 핵심 도구: 상세 출력과 debug 모듈

    Verbosity(상세 출력) 레벨 활용

    이게 제가 가장 자주 쓰는 방법이에요. -v 옵션을 붙이면 더 자세한 출력이 나오는데, 최대 4개까지 붙일 수 있습니다.

    # -v: 기본 상세 출력 (task 결과)
    ansible-playbook site.yml -v
    
    # -vv: 더 자세히 (파일/디렉토리 작업 포함)
    ansible-playbook site.yml -vv
    
    # -vvv: 연결 정보까지 (SSH 연결 디버깅에 유용)
    ansible-playbook site.yml -vvv
    
    # -vvvv: 가장 상세 (네트워크 패킷 수준)
    ansible-playbook site.yml -vvvv

    저는 보통 -vv나 -vvv를 주로 써요. -vvvv는 너무 많이 나와서 오히려 찾기 어려울 때도 있거든요.

    debug 모듈로 변수 값 확인하기

    Ansible 문제 해결에서 제일 많이 쓰는 게 바로 debug 모듈이에요. 변수 값이 의도한 대로 들어가 있는지 확인할 때 필수입니다.

    ---
    - name: 변수 디버깅 예제
      hosts: webservers
      vars:
        app_version: "1.2.3"
        deploy_path: "/opt/myapp"
    
      tasks:
        - name: 변수 값 확인
          debug:
            msg: "앱 버전: {{ app_version }}, 경로: {{ deploy_path }}"
    
        - name: 전체 변수 목록 확인 (hostvars)
          debug:
            var: hostvars[inventory_hostname]
    
        - name: 특정 변수만 확인
          debug:
            var: app_version
            verbosity: 2  # -vv 이상일 때만 출력

    verbosity 파라미터가 있는 게 포인트예요. 평소엔 안 보이다가 디버깅할 때만 출력되게 설정할 수 있거든요. 프로덕션 Playbook에 debug 태스크를 남겨둘 때 이렇게 해두면 깔끔합니다.

    register로 태스크 결과 캡처하기

    태스크(Task, Ansible의 개별 작업 단위) 실행 결과를 변수에 저장해서 다음 단계에서 활용하거나 디버깅할 수 있어요.

      tasks:
        - name: 서비스 상태 확인
          command: systemctl status nginx
          register: nginx_status
          ignore_errors: yes  # 오류가 나도 계속 진행
    
        - name: 결과 출력
          debug:
            var: nginx_status
    
        - name: 특정 필드만 확인
          debug:
            msg: |
              Return Code: {{ nginx_status.rc }}
              stdout: {{ nginx_status.stdout }}
              stderr: {{ nginx_status.stderr }}
    Ansible debug 모듈과 register를 활용한 Playbook 디버깅 설정 코드 예시

    ▲ debug 모듈과 register를 조합한 Ansible Playbook 디버깅 — 태스크 결과를 변수에 저장하고 단계별로 확인하는 방법


    Ansible 오류 유형별 해결 방법: 실전 트러블슈팅

    ⚠️ 오류 1: UNREACHABLE — 호스트에 접근 불가

    이거 처음 보면 당황하는 분들이 많은데, 대부분 SSH(시큐어 쉘, 원격 접속 프로토콜) 문제예요.

    TASK [Gathering Facts] *****
    fatal: [192.168.1.100]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh", "unreachable": true}

    체크리스트:

    1. SSH 키 등록 여부 확인: ssh -i ~/.ssh/id_rsa [email protected]
    2. 인벤토리(Inventory, 관리 대상 호스트 목록) 파일의 IP/호스트명 확인
    3. ansible_user, ansible_port 변수 확인
    4. 방화벽 규칙 확인
    # SSH 연결 직접 테스트
    ansible all -m ping -i inventory/hosts -vvv
    
    # 특정 호스트만 테스트
    ansible webserver01 -m ping -i inventory/hosts

    ⚠️ 오류 2: 변수 undefined — 변수를 찾을 수 없음

    Jinja2(진자2, Ansible의 템플릿 엔진) 템플릿에서 변수를 참조했는데 정의가 안 되어 있을 때 발생해요. 저도 이거 때문에 한참 헤맸습니다.

    fatal: [server01]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'db_password' is undefined"}

    해결 방법:

      tasks:
        # 방법 1: default 필터로 기본값 지정
        - name: DB 설정
          template:
            src: db.conf.j2
            dest: /etc/myapp/db.conf
          vars:
            db_password: "{{ db_password | default('changeme') }}"
    
        # 방법 2: vars_prompt로 실행 시 입력 받기
      vars_prompt:
        - name: db_password
          prompt: "DB 패스워드를 입력하세요"
          private: yes  # 입력 내용 숨김
    
        # 방법 3: ansible-vault로 암호화된 변수 파일 사용
        # ansible-playbook site.yml --ask-vault-pass

    ⚠️ 오류 3: 권한 오류 — Permission Denied

    원격 서버에서 sudo(슈퍼유저 권한 실행) 권한이 필요한 작업을 할 때 자주 만나는 Ansible 오류예요.

    ---
    - name: 웹서버 설정
      hosts: webservers
      become: yes          # sudo 권한으로 실행
      become_user: root    # root로 전환
    
      tasks:
        - name: nginx 설치
          apt:
            name: nginx
            state: present
    
        # 특정 태스크만 권한 상승
        - name: 로그 파일 수정
          file:
            path: /var/log/myapp
            mode: '0755'
          become: yes
          become_user: www-data
    # sudo 비밀번호 입력이 필요한 경우
    ansible-playbook site.yml --ask-become-pass

    ⚠️ 오류 4: 조건부 실행 문제 — when 절 오작동

    When(조건절) 설정이 잘못되면 실행되어야 할 태스크가 건너뛰어지거나, 반대로 실행되면 안 되는 게 실행되는 상황이 생겨요. 이거 진짜 찾기 어렵습니다.

      tasks:
        - name: OS 정보 수집
          setup:
            gather_subset:
              - distribution
    
        - name: 변수 타입 확인 (디버깅용)
          debug:
            msg: |
              OS Family: {{ ansible_os_family }}
              Distribution: {{ ansible_distribution }}
              Version: {{ ansible_distribution_version }}
    
        # 잘못된 예 — 문자열 비교 실수
        - name: Ubuntu에서만 실행 (잘못된 방법)
          apt:
            name: nginx
          when: ansible_distribution == 'ubuntu'  # 대소문자 주의!
    
        # 올바른 예
        - name: Ubuntu에서만 실행 (올바른 방법)
          apt:
            name: nginx
            state: present
          when: ansible_distribution == 'Ubuntu'  # 대문자 U
    
        # 복잡한 조건 — 가독성 높게 작성
        - name: 특정 환경에서만 실행
          command: /opt/deploy.sh
          when:
            - ansible_distribution == 'Ubuntu'
            - ansible_distribution_version is version('20.04', '>=')
            - env == 'production'

    ⚠️ 오류 5: 모듈 파라미터 오류

    Ansible 버전이 올라가면서 deprecated(더 이상 사용 안 하는) 파라미터가 생기거나, 파라미터명이 바뀌는 경우가 있어요. 이런 Ansible 오류는 메시지가 꽤 친절하게 나옵니다.

    [DEPRECATION WARNING]: The 'include' module is deprecated, use 'import_tasks' or 'include_tasks' instead.
    
    # 또는
    [WARNING]: Module did not set no_log for password
      tasks:
        # 구식 방법 (deprecated)
        - include: tasks/setup.yml
    
        # 새로운 방법
        - import_tasks: tasks/setup.yml   # 정적 포함 (컴파일 타임)
        - include_tasks: tasks/setup.yml  # 동적 포함 (런타임)

    고급 디버깅 기법: 실무에서 정말 유용한 것들

    특정 태스크만 실행하기 — tags 활용

    Playbook 전체를 돌리지 않고 문제가 있는 태스크만 콕 집어서 실행할 수 있어요. 태그(Tag) 기능인데, Ansible Playbook 디버깅할 때 진짜 유용합니다.

      tasks:
        - name: 패키지 설치
          apt:
            name: "{{ item }}"
            state: present
          loop:
            - nginx
            - git
            - curl
          tags:
            - packages
            - install
    
        - name: 설정 파일 배포
          template:
            src: nginx.conf.j2
            dest: /etc/nginx/nginx.conf
          tags:
            - config
            - nginx
    # 특정 태그만 실행
    ansible-playbook site.yml --tags "config"
    
    # 특정 태그 제외하고 실행
    ansible-playbook site.yml --skip-tags "packages"
    
    # 여러 태그 지정
    ansible-playbook site.yml --tags "config,nginx"

    특정 태스크부터 시작하기 — --start-at-task

    중간에 실패했을 때 처음부터 다시 돌리기 싫을 때 쓰는 방법이에요. 저 이거 알고 나서 삽질 시간이 확 줄었어요.

    # 특정 태스크명부터 실행
    ansible-playbook site.yml --start-at-task "설정 파일 배포"
    
    # step 모드: 태스크마다 실행 여부 물어봄
    ansible-playbook site.yml --step

    ansible-lint로 Playbook 품질 검사

    ansible-lint(앤서블 린트)는 Playbook의 코드 품질을 자동으로 검사해주는 도구예요. 문법 오류뿐 아니라 Best Practice(베스트 프랙티스, 모범 사례) 위반도 잡아줍니다.

    # 설치
    pip install ansible-lint
    
    # 검사 실행
    ansible-lint site.yml
    
    # 특정 규칙 무시하고 실행
    ansible-lint site.yml --exclude-path .ansible-lint
    # .ansible-lint 설정 파일
    skip_list:
      - yaml[line-length]  # 줄 길이 규칙 무시
      - name[casing]       # 이름 대소문자 규칙 무시
    
    warn_list:
      - experimental       # 실험적 규칙은 경고만

    Callback Plugin으로 출력 가독성 높이기

    기본 출력이 너무 지저분하다 싶으면 Callback Plugin(콜백 플러그인, 출력 형식 변환 도구)을 바꿔보세요.

    # ansible.cfg 설정
    [defaults]
    stdout_callback = yaml     # YAML 형식으로 출력 (가독성 좋음)
    # stdout_callback = dense  # 간결하게 출력
    # stdout_callback = debug  # 디버깅용 상세 출력
    
    callback_whitelist = profile_tasks  # 각 태스크 실행 시간 표시

    저는 yaml 콜백이랑 profile_tasks 조합을 제일 좋아해요. 어느 태스크가 오래 걸리는지 한눈에 보이거든요.

    Ansible yaml callback plugin 적용 전후 출력 화면 비교 — 가독성 향상으로 문제 해결 효율 증가

    ▲ yaml callback plugin 적용 전후 비교 — 출력 가독성이 크게 향상되어 Ansible 문제 해결이 훨씬 수월해집니다


    Ansible 오류 유형별 빠른 참조 표

    자주 만나는 오류들을 정리해봤습니다. 북마크해두고 쓰세요 ✅

    오류 유형 주요 증상 원인 해결 방법
    UNREACHABLE 호스트 연결 실패 SSH 설정 문제, 네트워크 오류 SSH 키 확인, 인벤토리 점검, -vvv로 연결 추적
    FAILED (문법) Playbook 시작 전 종료 YAML 들여쓰기, 문법 오류 --syntax-check, ansible-lint
    FAILED (변수) undefined variable 에러 변수 미정의, 오타 debug 모듈로 변수 확인, default 필터
    FAILED (권한) Permission denied sudo 설정 미흡 become/become_user 설정, sudoers 확인
    SKIPPED (예상치 못한) 태스크가 건너뜀 when 조건 오류 debug로 변수 값 확인, 조건식 재검토
    CHANGED (의도치 않은) 멱등성(Idempotency) 깨짐 모듈 사용 오류, command 모듈 남용 전용 모듈 사용, changed_when 설정
    MODULE ERROR 모듈 실행 실패 파라미터 오류, 버전 불일치 공식 문서 확인, -vv로 상세 확인

    멱등성(Idempotency) 확인: 자동화 스크립트의 핵심

    멱등성(Idempotency, 동일한 작업을 여러 번 실행해도 결과가 같아야 하는 성질)은 Ansible의 핵심 철학이에요. 이게 깨지면 Playbook을 두 번 돌렸을 때 뭔가 이상해지는 상황이 생기더라고요.

      tasks:
        # 나쁜 예: command 모듈은 멱등성이 없음
        - name: 디렉토리 생성 (나쁜 방법)
          command: mkdir /opt/myapp
    
        # 좋은 예: file 모듈은 멱등성 보장
        - name: 디렉토리 생성 (좋은 방법)
          file:
            path: /opt/myapp
            state: directory
            mode: '0755'
            owner: www-data
    
        # command/shell을 써야 할 때는 changed_when으로 제어
        - name: 애플리케이션 초기화 (한 번만 실행)
          command: /opt/myapp/init.sh
          args:
            creates: /opt/myapp/.initialized  # 이 파일 있으면 건너뜀
    
        # 또는 register + when 조합
        - name: 초기화 여부 확인
          stat:
            path: /opt/myapp/.initialized
          register: init_check
    
        - name: 초기화 실행
          command: /opt/myapp/init.sh
          when: not init_check.stat.exists

    자주 묻는 질문 (FAQ)

    Q. Ansible Playbook 실행 중에 특정 호스트만 실패했을 때 어떻게 하나요?

    A. --limit 옵션으로 특정 호스트만 재실행할 수 있어요. 실패한 호스트 목록은 .retry 파일에 자동 저장되기도 합니다.

    # 특정 호스트만 실행
    ansible-playbook site.yml --limit webserver01
    
    # retry 파일 활용
    ansible-playbook site.yml --limit @site.retry

    Q. 변수 우선순위가 헷갈려요. 어떻게 확인하나요?

    A. Ansible 변수 우선순위(Variable Precedence)는 복잡한데, ansible-config dump나 debug 모듈로 실제 적용된 값을 확인하는 게 제일 빠릅니다. 공식 문서에 22단계 우선순위가 나와 있는데... 저도 다 외우진 못해요 ㅎㅎ

    Q. ansible-playbook 실행이 너무 느린데 디버깅 말고 속도 개선 방법은요?

    A. 이건 별도로 다룰 주제인데, Forks(병렬 실행 수) 조정, Fact Caching(팩트 캐싱), Pipelining(파이프라이닝) 활성화가 주요 방법이에요. 다음 글에서 Ansible 성능 최적화를 다룰 예정입니다.


    마무리: 디버깅도 실력이다

    Ansible Playbook 디버깅 핵심 방법 5가지 요약 인포그래픽 — syntax-check부터 ansible-lint까지

    ▲ Ansible Playbook 디버깅 핵심 방법 요약 — syntax-check부터 debug 모듈, 태그 활용까지 단계별 접근법

    오늘 다룬 내용을 간단히 정리하면요:

    • ✅ 실행 전: --syntax-check로 문법 검사, --check --diff로 드라이런
    • ✅ 실행 중: -v ~ -vvv 상세 출력, debug 모듈로 변수 확인
    • ✅ 범위 좁히기: --tags, --limit, --start-at-task 활용
    • ✅ 코드 품질: ansible-lint로 사전 검사, 멱등성 확인
    • ✅ 가독성: yaml callback plugin + profile_tasks로 출력 개선

    솔직히 말씀드리면, 처음에 Ansible 오류를 만났을 때 저도 막막했어요. 에러 메시지가 길고 복잡해 보이는데, 결국 패턴이 있더라고요. 오늘 소개한 접근 방법들을 익히면 웬만한 Ansible 문제 해결은 훨씬 수월해질 거예요.

    🎉 핵심은 체계적인 접근이에요. 무턱대고 코드 고치지 말고, 문법 → 연결 → 변수 → 모듈 순서로 하나씩 좁혀가는 거죠. 그리고 debug 모듈은 정말 친한 친구처럼 자주 써보세요. 저도 매일 씁니다.

    다음 글에서는 Ansible Vault(앤서블 볼트)를 활용한 시크릿(Secret, 민감한 정보) 관리 방법을 다뤄볼 예정이에요. 패스워드나 API 키 같은 민감한 정보를 Playbook에 안전하게 넣는 방법인데, 이것도 실무에서 꼭 알아야 할 내용이라 기대해주세요.

    궁금한 점이나 여러분이 겪었던 특이한 Ansible 오류 경험이 있으시면 댓글로 남겨주세요. 같이 해결해봐요! 😊

  • [3D Printer] 3D 프린터 베드 안착 실패 원인 분석 및 해결 가이드

    [3D Printer] 3D 프린터 베드 안착 실패 원인 분석 및 해결 가이드

    3D 프린터 베드 안착 실패 원인 분석 및 해결 가이드

    3D 프린터를 처음 샀을 때 제일 먼저 겪는 시련이 뭔지 아세요? 바로 베드 안착 실패입니다. 출력 버튼 누르고 기대에 차서 바라보는데, 필라멘트가 베드에 붙지 않고 공중에서 스파게티처럼 엉키는 그 순간… 저도 처음엔 정말 당황했거든요. ‘내가 뭔가 잘못 설정했나?’, ‘불량품인가?’ 하면서 한참을 헤맸었습니다.

    13년 동안 서버실에서 인프라를 다루다가 홈랩 취미로 3D 프린터를 시작했는데, 솔직히 말씀드리면 서버 세팅보다 3D 프린터 베드 안착 잡는 게 처음엔 더 어려웠어요. 그래서 오늘은 제가 직접 삽질하면서 쌓은 경험을 바탕으로, 베드 안착 실패의 원인과 해결 방법을 하나씩 정리해 드리려고 합니다.

    3D 프린터 베드 안착 실패(좌)와 성공(우) 비교 — 첫 레이어 차이

    ▲ 베드 안착 실패 시 필라멘트가 엉키는 모습(좌)과 완벽하게 안착된 첫 레이어(우) 비교

    베드 안착(Bed Adhesion)이 왜 이렇게 중요한가요?

    베드 안착(Bed Adhesion)이란, 쉽게 말해 출력물의 첫 번째 레이어가 베드 표면에 얼마나 잘 붙어 있느냐의 문제입니다. 3D 프린팅은 레이어를 하나씩 쌓아 올리는 방식인데, 첫 레이어가 제대로 붙지 않으면 그 위에 쌓이는 모든 레이어가 의미 없어져요. 마치 기초 공사가 부실한 건물처럼요.

    실제로 3D 프린터 출력 실패의 원인 중 상당수가 바로 이 첫 레이어 문제에서 시작합니다. 그러니 베드 안착 문제를 제대로 해결하면, 출력 성공률이 확 올라가는 거죠.

    베드 안착에 영향을 주는 핵심 요소

    • 베드 레벨링(Bed Leveling): 베드와 노즐 사이의 간격이 균일한지
    • Z 오프셋(Z Offset): 노즐의 정확한 높이 설정
    • 베드 온도(Bed Temperature): 필라멘트 종류에 맞는 온도 설정
    • 노즐 온도(Nozzle Temperature): 필라멘트가 충분히 녹아서 나오는지
    • 출력 속도(Print Speed): 첫 레이어 속도가 너무 빠르지 않은지
    • 베드 표면 상태: 기름기나 먼지가 없는지

    베드 안착 실패의 주요 원인 7가지

    제가 직접 겪어본 실패 케이스들을 정리해봤습니다. 혹시 이런 경험 있으신가요?

    1. 베드 레벨링이 안 맞는 경우

    가장 흔한 원인이에요. 베드가 한쪽으로 기울어져 있거나, 노즐과 베드 사이 간격이 위치마다 다르면 베드 안착이 균일하게 될 수 없거든요. 특히 수동 레벨링(Manual Leveling)을 하다 보면 네 모서리는 맞췄는데 가운데가 떠 있는 경우가 많더라고요.

    2. Z 오프셋이 너무 높은 경우

    Z 오프셋이란 노즐이 베드에서 얼마나 떨어져서 시작하는지를 결정하는 값인데요. 이 값이 너무 크면 필라멘트가 베드에 눌리지 않고 그냥 공중에 떠서 나와요. 처음엔 이게 뭔가 싶었는데, 이거 하나 잡으니까 안착률이 확 달라지더라고요.

    3. 베드 온도 설정 오류

    필라멘트 종류마다 적합한 베드 온도가 다릅니다. PLA는 베드 히팅 없이도 되는 경우도 있지만, ABS는 반드시 100~110°C 정도가 필요해요. 온도가 낮으면 필라멘트가 식으면서 베드에서 들뜨는 워핑(Warping) 현상이 생기거든요.

    4. 베드 표면 오염

    이게 의외로 많은 분들이 놓치는 부분인데요. 손으로 베드를 만지면 피지가 묻어서 필라멘트가 안 붙어요. 저도 처음에 왜 이 자리만 안 붙지? 했더니 손가락 자국 부분이었더라고요. 정말 황당했습니다.

    5. 첫 레이어 출력 속도가 너무 빠른 경우

    슬라이서(Slicer) 소프트웨어에서 첫 레이어 속도를 전체 속도와 동일하게 설정해두면 문제가 생깁니다. 첫 레이어는 베드에 충분히 눌리면서 천천히 쌓여야 하는데, 너무 빠르면 필라멘트가 제대로 안착되지 않아요.

    6. 노즐 막힘 또는 언더 익스트루전

    노즐이 부분적으로 막히거나 필라멘트가 충분히 나오지 않는 언더 익스트루전(Under Extrusion) 상태에서는 첫 레이어 자체가 제대로 형성되지 않습니다. 출력 선이 끊기거나 얇게 나오면 이 경우를 의심해보세요.

    7. 베드 표면 자체의 문제

    유리 베드(Glass Bed), PEI 시트, 자석 스프링 스틸 시트 등 베드 표면 종류에 따라 안착 특성이 다릅니다. 오래 사용하면 표면이 마모되거나 손상되어 안착력이 떨어지기도 해요.

    Z 오프셋 값에 따른 3D 프린터 노즐과 베드 간격 차이 다이어그램

    ▲ Z 오프셋 값에 따른 노즐과 베드 간격 차이 — 너무 높으면 필라멘트가 뜨고, 너무 낮으면 노즐이 막힌다

    베드 안착 실패 해결 방법 — 단계별 가이드

    자, 이제 실제 해결 방법을 순서대로 알려드릴게요. 저도 이 순서대로 하나씩 체크하면서 문제를 잡았습니다.

    STEP 1. 베드 레벨링부터 다시 잡기

    1. 프린터를 예열합니다. (노즐 200°C, 베드 60°C 기준 PLA)
    2. 노즐을 베드 홈 포지션(Home Position)으로 이동시킵니다.
    3. A4 용지 한 장을 베드와 노즐 사이에 끼웁니다.
    4. 베드의 네 모서리와 중앙 총 5개 지점을 확인합니다.
    5. 용지가 살짝 저항감 있게 움직일 정도로 간격을 조정합니다.
    6. 자동 베드 레벨링(ABL, Auto Bed Leveling) 기능이 있다면 반드시 실행합니다.

    💡 팁: BLTouch나 CR Touch 같은 자동 레벨링 센서가 있다면 정말 편해요. 수동으로 잡다가 이거 달고 나서 레벨링 스트레스가 90%는 사라졌거든요.

    STEP 2. Z 오프셋 미세 조정

    1. 슬라이서에서 첫 레이어만 출력하는 테스트 파일을 준비합니다.
    2. 출력 시작 후 실시간으로 Z 오프셋을 조정합니다.
    3. 필라멘트가 베드에 살짝 눌리면서 납작하게 퍼지는 상태가 이상적입니다.
    4. 출력선 사이에 틈이 없고, 표면이 매끄럽게 이어지는지 확인합니다.

    ⚠️ 주의: Z 오프셋을 너무 낮게 설정하면 노즐이 베드를 긁어서 표면이 손상됩니다. 처음엔 조금씩 내려가면서 확인하세요.

    STEP 3. 베드 온도 최적화

    필라멘트 종류 노즐 온도 베드 온도 특이사항
    PLA 190~220°C 50~60°C 베드 없어도 가능하나 권장
    ABS 230~250°C 100~110°C 인클로저(밀폐 공간) 필수
    PETG 230~250°C 70~85°C PEI 시트와 궁합 좋음
    TPU 220~240°C 30~60°C 출력 속도 낮춰야 함
    Nylon 240~260°C 70~90°C 건조 필수, 흡습성 강함

    STEP 4. 베드 표면 청소

    1. 이소프로필알코올(IPA, Isopropyl Alcohol) 70~99% 농도를 준비합니다.
    2. 베드가 식은 상태에서 IPA를 천이나 키친타월에 묻혀 닦아냅니다.
    3. 원을 그리듯 닦지 말고, 한 방향으로 쭉쭉 닦아주세요.
    4. 완전히 마른 후 베드를 가열합니다.
    5. 이후에는 절대 손으로 베드 표면을 만지지 않습니다.

    사실 이게 제일 간단한데 효과가 엄청나요. 갑자기 안착이 안 될 때 IPA로 한 번 닦아주면 해결되는 경우가 꽤 많더라고요.

    STEP 5. 슬라이서 설정 최적화

    Cura나 PrusaSlicer 같은 슬라이서 소프트웨어에서 다음 항목들을 확인하세요.

    # Cura 기준 권장 첫 레이어 설정
    첫 레이어 두께(Initial Layer Height): 0.2~0.3mm
    첫 레이어 속도(Initial Layer Speed): 20~30mm/s
    첫 레이어 선 너비(Initial Layer Line Width): 120~150%
    스커트(Skirt) 또는 브림(Brim): 활성화 권장
    팬 속도(Fan Speed): 첫 레이어 0%

    STEP 6. 베드 안착 보조 방법 활용

    위 방법을 다 써도 여전히 안착이 안 된다면, 추가적인 보조 방법을 써볼 수 있어요.

    • 브림(Brim): 출력물 주변에 테두리를 추가해서 베드 접촉 면적을 넓히는 방법. 가장 무난하게 쓰는 방법입니다.
    • 래프트(Raft): 출력물 아래에 격자 형태의 받침을 깔아주는 방법. 안착력이 가장 강하지만 재료 소모가 많아요.
    • 접착제(Glue Stick): 베드에 딱풀을 얇게 발라주면 안착력이 올라갑니다. 특히 ABS에 효과적이에요.
    • 헤어스프레이: 유리 베드에 많이 쓰는 방법인데, 저도 처음엔 이게 진짜 되나? 했는데 꽤 효과 있더라고요.
    • PEI 시트 교체: 베드 표면이 많이 마모됐다면 PEI 시트 교체가 근본적인 해결책입니다.
    3D 프린터 브림(Brim)과 래프트(Raft) 베드 안착 보조 도구 출력 예시

    ▲ 슬라이서에서 브림(Brim)과 래프트(Raft) 설정 화면 — 베드 안착 보조 도구로 출력 성공률을 높일 수 있다

    자주 겪는 트러블슈팅

    문제 1: 첫 레이어는 붙는데 출력 중간에 떨어짐

    이건 워핑(Warping) 현상이에요. 출력물이 식으면서 수축해서 베드에서 들리는 건데, ABS에서 특히 심하게 나타납니다. 해결책은 인클로저(Enclosure, 밀폐 공간)를 만들어서 주변 온도를 일정하게 유지하거나, 브림을 넓게 설정하는 거예요. 저는 골판지로 임시 인클로저 만들어서 쓰다가 나중에 제대로 된 인클로저 샀거든요.

    문제 2: 코너 부분만 들뜸

    모서리 부분은 냉각이 빠르게 일어나서 수축이 더 심하게 발생합니다. 브림 폭을 10mm 이상으로 넓게 설정하고, 베드 온도를 5~10°C 올려보세요. 또 출력물 배치를 베드 중앙으로 옮기는 것도 도움이 됩니다.

    문제 3: 레벨링은 맞는데 특정 위치만 안 붙음

    베드 표면이 국소적으로 손상됐거나 오염된 경우입니다. IPA로 닦고 그래도 안 되면 그 부분에 딱풀을 살짝 발라보세요. 아니면 슬라이서에서 출력 위치를 다른 곳으로 옮겨보는 것도 방법이에요.

    문제 4: 자동 레벨링 후에도 안착이 불균일함

    자동 레벨링(ABL) 데이터가 오래됐거나, 베드 온도가 완전히 안정화되기 전에 레벨링을 실행한 경우입니다. 베드를 충분히 예열한 후(약 5~10분) 레벨링을 다시 실행해보세요. 베드는 열을 받으면 약간 변형되거든요.

    베드 안착 성공 확인 방법

    드디어 설정을 다 잡았다면, 이렇게 확인해보세요!

    ✅ 완벽한 첫 레이어의 특징

    • 출력선이 서로 붙어 있고, 사이에 틈이 없음
    • 필라멘트가 베드에 살짝 눌려서 납작하게 퍼진 형태
    • 표면이 매끄럽고 광택이 있음 (PLA 기준)
    • 출력 중간에 들뜨거나 움직이지 않음
    • 완성 후 베드가 식으면 자연스럽게 떨어지거나, 약간의 힘으로 분리됨

    🎉 첫 레이어가 이렇게 나오면 이제 출력 성공률이 확 올라가는 게 느껴지실 거예요. 저도 이 단계 넘어서니까 3D 프린팅이 진짜 재밌어지더라고요.

    완벽한 3D 프린터 첫 레이어(좌)와 Z 오프셋 실패로 인한 불량 첫 레이어(우) 비교

    ▲ 올바른 Z 오프셋과 베드 레벨링으로 완성된 완벽한 첫 레이어(좌)와 Z 오프셋이 너무 높아 실패한 첫 레이어(우)

    정리: 베드 안착 체크리스트

    체크 항목 확인 방법 해결책
    베드 레벨링 A4 용지 테스트 수동 조정 또는 ABL 실행
    Z 오프셋 첫 레이어 눌림 상태 확인 실시간 미세 조정
    베드 온도 필라멘트 스펙 확인 권장 온도로 설정
    베드 청결도 육안 확인 IPA로 닦기
    첫 레이어 속도 슬라이서 설정 확인 20~30mm/s로 낮추기
    안착 보조 출력물 형태 확인 브림 또는 래프트 추가
    베드 표면 상태 표면 마모/손상 확인 PEI 시트 교체

    마무리하며

    3D 프린터 베드 안착 문제, 처음엔 정말 막막하게 느껴지지만 사실 원인만 제대로 파악하면 해결은 생각보다 어렵지 않아요. 제 경험상 90% 이상의 안착 실패는 베드 레벨링, Z 오프셋, 베드 온도, 표면 청결도 이 네 가지 중 하나에서 나왔거든요.

    처음엔 하나씩 체크하는 게 번거롭게 느껴질 수 있는데, 몇 번 하다 보면 감이 생겨서 나중엔 출력 시작하기 전에 눈으로 훑어보는 것만으로도 문제를 미리 잡을 수 있게 됩니다. 그 순간이 오면 진짜 3D 프린팅이 편해지더라고요.

    다음 글에서는 필라멘트 종류별 상세 설정 가이드를 다룰 예정입니다. PLA, ABS, PETG 각각의 특성과 최적 설정값을 정리해드릴게요.

    오늘도 출력 성공하세요! 🎉

    자주 묻는 질문 (FAQ)

    Q. 베드 레벨링을 얼마나 자주 해야 하나요?

    프린터를 옮겼거나, 출력 중 노즐이 베드를 긁은 경우, 또는 안착이 갑자기 안 될 때 하면 됩니다. 자동 레벨링 센서가 있다면 출력 시작 전마다 자동으로 실행되도록 설정해두는 게 좋아요.

    Q. PLA인데 베드 히팅이 없어도 출력이 되나요?

    PLA는 베드 히팅 없이도 출력이 가능하긴 합니다. 하지만 50~60°C로 가열하면 안착력이 훨씬 좋아지고, 특히 큰 출력물에서 안정적이에요. 가능하다면 베드 히팅을 사용하는 걸 권장합니다.

    Q. 딱풀을 바르면 출력물이 떼기 어렵지 않나요?

    베드가 식으면 딱풀이 수축하면서 출력물이 자연스럽게 분리됩니다. 그래도 안 떨어지면 미지근한 물에 잠깐 담그면 쉽게 분리돼요.

  • [k8s] k3s vs MicroK8s: 2026년 9월 기준 경량 쿠버네티스 비교 분석 및 선택 가이드

    [k8s] k3s vs MicroK8s: 2026년 9월 기준 경량 쿠버네티스 비교 분석 및 선택 가이드

    경량 쿠버네티스, 왜 지금 이 선택이 중요한가요?

    홈랩에 쿠버네티스를 올리려고 처음 도전했을 때가 생각나네요. 풀스택 쿠버네티스를 라즈베리파이 클러스터에 올렸다가 메모리가 터져서 노드가 죽어버리는 경험을 했거든요. 그때 처음 알게 된 게 바로 경량 쿠버네티스(Lightweight Kubernetes)라는 개념이었습니다.

    요즘은 엣지 컴퓨팅, IoT, 홈랩 쿠버네티스, 로컬 개발 환경, 소규모 프로덕션 클러스터까지 쿠버네티스를 돌려야 하는 상황이 정말 많아졌죠. 그런데 풀 쿠버네티스를 그대로 올리기엔 리소스 부담이 큽니다. 그래서 여전히 많은 분들이 k3s vs MicroK8s라는 주제로 고민하세요.

    저도 실제로 두 가지를 모두 운영해봤습니다. 홈랩 서버에서는 k3s를 오래 써왔고, 개발 팀 환경 구성이나 Ubuntu 기반 테스트 환경에서는 MicroK8s도 여러 번 써봤거든요. 이번 글에서는 2026년 9월 기준으로 바뀐 내용까지 반영해서, 어떤 상황에서 뭘 선택해야 하는지 현실적으로 정리해보겠습니다.

    k3s와 MicroK8s 경량 쿠버네티스 아키텍처 비교 다이어그램

    ▲ k3s와 MicroK8s의 전체 아키텍처 개요 — 두 경량 쿠버네티스의 구조적 차이를 한눈에 비교한 다이어그램


    k3s와 MicroK8s가 뭔지 먼저 짚고 가요

    k3s — 더 이상 40MB는 아니지만 여전히 가장 가벼운 축

    k3s는 Rancher Labs, 현재 SUSE에서 만든 경량 쿠버네티스 배포판입니다. 이름의 k3s는 k8s보다 더 가볍다는 의미에서 붙은 이름이고요. 요즘 기준으로는 100MB 안팎의 단일 바이너리라고 보는 게 더 정확합니다. 예전처럼 40MB짜리 쿠버네티스라고 딱 잘라 말하긴 어렵지만, 여전히 경량 쿠버네티스 대표 주자라는 건 변함이 없어요.

    주요 특징을 보면:

    • 단일 바이너리 중심 배포 — 설치가 정말 간단해요
    • 기본 스토리지 백엔드는 SQLite, 고가용성(HA)은 임베디드 etcd 또는 외부 DB 선택 가능
    • ARM 아키텍처 지원이 매우 좋아서 라즈베리파이 쿠버네티스 용도로 강함
    • containerd, Flannel, CoreDNS, Traefik, ServiceLB, local-path-provisioner 등을 기본 제공
    • 엣지 쿠버네티스, IoT, 에어갭 환경, 홈랩에 특히 잘 맞음
    • 2026년 9월 기준 최신 주요 릴리스 흐름은 Kubernetes 1.37 기반 k3s 1.37 계열까지 올라왔고, 1.34/1.35 계열도 패치 릴리스가 계속 제공되는 상황

    MicroK8s — Canonical이 만든 스냅 패키지 쿠버네티스

    MicroK8s는 Ubuntu를 만든 Canonical에서 개발한 경량 쿠버네티스입니다. 여전히 가장 큰 특징은 snap 패키지로 설치된다는 점이에요. Ubuntu 기반 서버나 개발용 워크스테이션에서는 정말 편합니다.

    • snap 패키지로 설치 — Ubuntu/Debian 계열에서 매우 간편
    • 애드온 시스템으로 기능 확장 — 필요한 것만 켜고 끄는 구조
    • 단일 노드부터 멀티노드, 고가용성 클러스터까지 지원
    • DNS, hostpath 스토리지, Ingress, MetalLB, GPU, observability 등 애드온 활성화가 쉬움
    • CNCF 인증 쿠버네티스 — 표준 호환성 높음
    • 업스트림 쿠버네티스 버전을 빠르게 따라가는 편이지만, 실제 설치 전에는 snap info microk8s로 채널별 최신 버전을 확인하는 게 안전함

    k3s vs MicroK8s 핵심 스펙 비교표

    말로만 설명하면 헷갈리니까 표로 정리해봤습니다. 특히 2026년 기준으로 바뀐 부분은 MicroK8s의 대시보드 제거, Ingress 애드온의 Traefik 전환, Gateway API 중요도 상승입니다.

    항목 k3s MicroK8s
    개발사 SUSE (원 개발: Rancher Labs) Canonical
    설치 방식 curl 스크립트 / 단일 바이너리 snap 패키지
    최소 메모리 약 512MB 수준부터 시작 가능 약 540MB 이상 권장
    기본 스토리지 백엔드 SQLite / 임베디드 etcd / 외부 DB dqlite 기반 HA 데이터스토어
    기본 컨테이너 런타임 containerd containerd
    기본 Ingress Traefik 포함 없음. microk8s enable ingress로 활성화, 최신 계열은 Traefik 기반
    Gateway API k3s 1.37부터 Gateway API CRD 번들 제공 Ingress 애드온과 Traefik 구성에서 Gateway API 흐름이 중요해짐
    ARM 지원 매우 강함 지원
    멀티노드 클러스터 비교적 단순 지원, HA 기본 지원 구조
    애드온 시스템 내장 컴포넌트 + Helm Chart 패턴 microk8s enable 명령어
    주요 사용 환경 엣지, IoT, 홈랩, 경량 프로덕션 개발 환경, Ubuntu 기반 서버, 빠른 테스트랩
    CNCF 인증 지원 지원

    포인트: 메모리 차이보다 더 중요한 건 운영 모델입니다. k3s는 최대한 작고 단순하게, MicroK8s는 Ubuntu 생태계에서 빠르게 확장 가능하게 쪽에 더 가깝습니다.


    실전 설치 및 구성 — 직접 해봤습니다

    k3s와 MicroK8s 터미널 설치 과정 단계별 화면

    ▲ k3s와 MicroK8s 실제 설치 과정 — 터미널에서 진행되는 단계별 설치 명령어 실행 화면

    k3s 설치 — 진짜 이렇게 쉬울 수가

    # k3s 서버 설치
    curl -sfL https://get.k3s.io | sh -
    
    # 설치 확인
    sudo k3s kubectl get nodes
    
    # 노드 토큰 확인
    sudo cat /var/lib/rancher/k3s/server/node-token

    워커 노드 추가

    curl -sfL https://get.k3s.io | K3S_URL=https://마스터IP:6443 K3S_TOKEN=서버토큰값 sh -

    kubeconfig 설정

    mkdir -p ~/.kube
    sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
    sudo chown $(id -u):$(id -g) ~/.kube/config
    chmod 600 ~/.kube/config
    sed -i 's/127.0.0.1/마스터서버IP/g' ~/.kube/config
    kubectl get nodes

    주의: kubeconfig 권한이 너무 넓으면 kubectl이 경고를 냅니다. chmod 600 ~/.kube/config까지 같이 해두는 게 깔끔해요.

    k3s 추가 설정 — Traefik 비활성화 또는 버전 고정

    # Traefik 없이 설치
    curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik" sh -
    
    # 특정 버전 지정 설치 예시. 실제 최신 패치는 릴리스 노트 확인 권장
    curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="v1.37.0+k3s1" sh -

    k3s 고가용성(HA) 시작점

    # 첫 서버를 embedded etcd 기반 HA 초기 노드로 시작
    curl -sfL https://get.k3s.io | sh -s - server --cluster-init
    
    # 이후 추가 서버 조인
    curl -sfL https://get.k3s.io | K3S_URL=https://첫서버IP:6443 K3S_TOKEN=서버토큰값 sh -s - server

    MicroK8s 설치 — Ubuntu라면 더 편해요

    # 설치 전 채널 확인
    snap info microk8s
    
    # snap으로 MicroK8s 설치. 운영 환경은 버전 채널을 명시하는 편이 안전합니다.
    sudo snap install microk8s --classic --channel=1.36/stable
    
    sudo usermod -a -G microk8s $USER
    mkdir -p ~/.kube
    sudo chown -f -R $USER ~/.kube
    newgrp microk8s
    
    microk8s status --wait-ready
    microk8s kubectl get nodes

    MicroK8s 핵심 애드온 활성화

    microk8s enable dns
    microk8s enable hostpath-storage
    microk8s enable ingress
    microk8s enable metallb:192.168.1.200-192.168.1.220
    microk8s enable dns hostpath-storage ingress

    예전 글이나 오래된 튜토리얼에는 microk8s enable dashboard가 자주 나오는데요. MicroK8s 1.36부터 dashboard 애드온과 dashboard-proxy 명령은 제거됐습니다. 웹 UI가 필요하다면 Lens, Headlamp, Argo CD, Rancher 같은 별도 도구를 검토하는 쪽이 현실적입니다.

    MicroK8s kubectl 별칭 설정

    alias kubectl='microk8s kubectl'
    alias k='microk8s kubectl'
    microk8s config > ~/.kube/microk8s-config
    export KUBECONFIG=~/.kube/config:~/.kube/microk8s-config

    MicroK8s 멀티노드 클러스터 구성

    microk8s add-node
    microk8s join 마스터IP:25000/토큰값/해시값
    microk8s join 마스터IP:25000/토큰값/해시값 --worker
    microk8s kubectl get nodes

    실제 삽질 포인트와 트러블슈팅

    k3s 트러블슈팅

    문제 1: 방화벽 때문에 워커 노드가 조인이 안 돼요

    sudo ufw allow 6443/tcp
    sudo ufw allow 10250/tcp
    sudo ufw allow 8472/udp
    sudo ufw allow 51820/udp
    sudo k3s kubectl get nodes -o wide
    sudo journalctl -u k3s -f

    문제 2: SQLite에서 HA로 커지기 시작할 때

    단일 노드면 SQLite도 충분합니다. 다만 처음부터 고가용성 운영을 할 거면 embedded etcd로 시작하는 게 가장 깔끔해요. 중간에 뒤늦게 구조를 바꾸면 생각보다 손이 많이 갑니다.

    curl -sfL https://get.k3s.io | sh -s - server --cluster-init

    문제 3: 이미지 pull 속도가 너무 느려요

    # /etc/rancher/k3s/registries.yaml
    mirrors:
      docker.io:
        endpoint:
          - "https://mirror.gcr.io"
      "내부레지스트리주소:5000":
        endpoint:
          - "http://내부레지스트리주소:5000"
    configs:
      "내부레지스트리주소:5000":
        auth:
          username: admin
          password: password
        tls:
          insecure_skip_verify: true
    sudo systemctl restart k3s

    MicroK8s 트러블슈팅

    문제 1: snap/커널 조합 때문에 설치가 꼬여요

    uname -r
    microk8s inspect
    sudo snap refresh microk8s --channel=1.36/stable

    문제 2: 예전 튜토리얼대로 dashboard 접근이 안 돼요

    이건 사용자가 잘못한 게 아니라, 최신 버전에서 절차가 바뀐 것입니다. MicroK8s 1.36부터는 dashboard 애드온과 microk8s dashboard-proxy가 제거됐어요. 따라서 예전 방식으로는 당연히 안 됩니다.

    문제 3: snap 자동 업데이트로 버전이 올라가버려요

    sudo snap refresh --hold microk8s
    sudo snap refresh --hold=forever microk8s
    snap refresh --time

    MicroK8s는 같은 채널 안에서는 자동 refresh가 일어날 수 있습니다. 운영 환경이라면 채널 고정, hold 정책, 업그레이드 테스트 순서를 미리 정해두는 게 좋습니다.


    실제 운영 결과 및 성능 비교

    k3s와 MicroK8s 리소스 사용량 성능 모니터링 대시보드 비교

    ▲ k3s와 MicroK8s 리소스 사용량 모니터링 대시보드 — CPU, 메모리 사용량 실시간 비교 차트

    동일한 스펙의 VM 두 대에 각각 설치해서 비교해봤습니다. 수치는 환경과 활성화한 애드온에 따라 달라질 수 있으니 참고용으로 봐주세요.

    유휴 상태(Idle) 리소스 사용량

    측정 항목 k3s MicroK8s
    메모리 사용량 약 450MB~600MB 약 550MB~750MB
    CPU 사용률 유휴 시 0.5% 이하인 경우가 많음 유휴 시 1~2% 수준인 경우가 많음
    준비 시간 약 15~30초 약 30~60초
    디스크 사용량 상대적으로 작음 snap 포함 시 더 큼

    k3s가 전반적으로 더 가벼운 편이긴 한데, 이 차이는 정말 빡빡한 ARM 보드나 소형 엣지 장비에서 더 크게 느껴집니다. 일반적인 VM이나 미니 PC 수준에선 완전히 다른 세상 정도는 아니에요.

    Pod 배포 속도 테스트

    time kubectl create deployment nginx-test --image=nginx --replicas=5
    kubectl rollout status deployment/nginx-test

    결론적으로 두 환경 모두 배포 체감은 비슷했습니다. 실제 차이는 컨테이너 이미지 pull, CNI 초기화, 스토리지 프로비저너 상태 같은 주변 요소에서 더 많이 납니다.


    어떤 걸 선택해야 할까요? — 상황별 가이드

    k3s를 선택해야 하는 경우

    • 라즈베리파이, 임베디드 시스템 등 ARM 기반 엣지 환경
    • 멀티노드 클러스터를 간단하게 구성하고 싶을 때
    • Ubuntu 외 다른 Linux 배포판을 사용할 때
    • 프로덕션 엣지 배포가 목적일 때
    • 인터넷 연결이 불안정하거나 에어갭 설치가 필요한 환경
    • 홈랩 쿠버네티스를 최대한 가볍게 운영하고 싶을 때
    • Gateway API CRD 번들 제공 같은 최신 k3s 1.37 흐름을 활용하고 싶을 때

    MicroK8s를 선택해야 하는 경우

    • Ubuntu 기반 개발 환경에서 빠르게 쿠버네티스 셋업
    • 애드온 시스템으로 간편하게 기능 확장하고 싶을 때
    • Canonical/Ubuntu 생태계를 이미 사용 중일 때
    • 개발자가 로컬에서 표준 쿠버네티스 환경이 필요할 때
    • GPU 워크로드나 observability 애드온을 빨리 붙여보고 싶을 때
    • snap 기반 설치와 채널 관리 모델이 조직 운영 방식에 잘 맞을 때

    둘 다 아닌 경우도 있어요

    개발 환경에서 쿠버네티스가 필요한데 설치도 귀찮고 리소스도 아끼고 싶다면, kind(Kubernetes in Docker)나 minikube도 충분히 좋은 선택입니다. 특히 CI 파이프라인이나 일회성 테스트 환경은 kind가 더 깔끔할 때가 많아요.


    자주 묻는 질문 (FAQ)

    Q. k3s와 MicroK8s 중 어느 게 더 프로덕션에 적합한가요?

    엣지, 홈랩, 소규모 서비스 운영처럼 가볍고 단순한 운영이 중요하면 k3s 쪽을 먼저 봅니다. Ubuntu 표준 환경, snap 기반 배포, 애드온 중심 운영이 편한 팀이라면 MicroK8s도 충분히 좋은 선택입니다.

    Q. 라즈베리파이에는 뭘 써야 하나요?

    개인적으로는 k3s를 먼저 추천합니다. ARM 지원, 설치 단순성, 리소스 사용량 면에서 라즈베리파이 쿠버네티스와 잘 맞습니다.

    Q. 기존 kubectl 명령어를 그대로 쓸 수 있나요?

    네. k3s는 k3s kubectl 또는 일반 kubectl을 사용할 수 있고, MicroK8s는 microk8s kubectl을 쓰거나 alias/kubeconfig를 설정하면 됩니다.

    Q. 풀 쿠버네티스에서 k3s/MicroK8s로 마이그레이션이 쉬운가요?

    기본 리소스는 대부분 비슷하지만, Ingress Controller, StorageClass, LoadBalancer, CNI, CSI 드라이버 차이에서 손볼 일이 생깁니다. 특히 2026년 기준으로는 Ingress와 Gateway API 전략을 함께 검토하는 게 좋습니다.


    2026년 9월 기준, 꼭 알아야 할 최신 변화

    1. MicroK8s는 이제 대시보드 기본 경로를 기대하면 안 됩니다

    MicroK8s 1.36부터 dashboard 관련 기능이 제거됐습니다. 예전처럼 microk8s enable dashboard와 microk8s dashboard-proxy를 기대하면 막힙니다. 운영 가시성이 필요하다면 Headlamp, Lens, Grafana, Argo CD UI 같은 대안을 선택하는 편이 낫습니다.

    2. Ingress와 Traefik, Gateway API 변화는 생각보다 영향이 큽니다

    MicroK8s의 ingress 애드온은 최신 계열에서 Traefik 중심으로 바뀌었고, k3s도 기본 Ingress Controller로 Traefik을 포함합니다. 여기에 k3s 1.37부터는 Gateway API CRD가 번들로 제공되기 시작했습니다. 앞으로 새 클러스터를 만든다면 단순히 Ingress만 볼 게 아니라 Kubernetes Gateway API, Traefik Gateway, Cilium Gateway API 같은 선택지도 함께 비교하는 게 좋습니다.

    3. 홈랩 쿠버네티스와 엣지 쿠버네티스 운영 포인트

    홈랩에서는 설치보다 백업, 인증서, 스토리지, 자동 업데이트 관리가 더 중요합니다. k3s라면 etcd/SQLite 백업과 /etc/rancher/k3s 설정 보관, MicroK8s라면 snap refresh 정책과 애드온 상태 점검을 운영 체크리스트에 넣어두세요.

    4. Kubernetes 1.37 시대의 새 키워드: Gateway API와 워크로드 스케줄링

    Kubernetes 1.37에서는 Gateway API, 동적 리소스 할당(DRA), 워크로드 인식 스케줄링 같은 흐름이 더 중요해졌습니다. k3s와 MicroK8s를 비교할 때도 이제는 단순히 가볍냐 무겁냐만 볼 게 아니라, 엣지 AI 워크로드, GPU 워크로드, 로컬 쿠버네티스 개발환경, GitOps 운영까지 함께 고려하는 쪽이 더 현실적입니다.


    마무리 — 그래서 저는 뭘 쓰냐고요?

    제 기준은 단순합니다. 홈랩, 라즈베리파이, 엣지 장비, 작고 오래 가는 클러스터라면 k3s를 고릅니다. Ubuntu 개발 장비에서 빠르게 켜고 끄는 테스트 클러스터, 애드온을 손쉽게 붙여보는 실험 환경이라면 MicroK8s를 고릅니다.

    둘 다 좋은 도구입니다. 다만 운영 방식이 다릅니다. k3s는 작고 조용하게 오래 가는 쪽에 가깝고, MicroK8s는 Ubuntu 생태계 안에서 빠르게 실험하고 확장하는 쪽에 가깝습니다. 결국 답은 내 환경이 어떤 실패를 견뎌야 하는지에 달려 있습니다.

    🔄 마지막 업데이트: 2026년 09월

  • [k8s] k3s 최신 버전: 경량 쿠버네티스 설치 및 운영 실전 가이드

    [k8s] k3s 최신 버전: 경량 쿠버네티스 설치 및 운영 실전 가이드

    k3s 최신 버전, 왜 지금 써야 할까요?

    쿠버네티스(Kubernetes)를 공부하거나 실무에 적용하려고 마음먹었는데, 막상 설치하려니 서버 사양부터 막히셨던 경험 있으신가요? 저도 처음엔 그랬거든요. 풀스택 쿠버네티스 클러스터를 집에서 돌리려면 메모리만 수십 GB가 필요하고, 설정 파일은 또 얼마나 복잡한지… 솔직히 포기하고 싶었던 적이 한두 번이 아닙니다.

    그러다가 만난 게 k3s 최신 버전이었어요. Rancher Labs(현 SUSE)에서 만든 경량 쿠버네티스인데, 처음 써봤을 때 진짜 “이게 뭔가?” 싶을 정도로 설치가 간단했습니다. 명령어 하나로 끝나거든요. 13년 동안 인프라 엔지니어로 일하면서 이렇게 설치가 간단한 쿠버네티스 배포판은 처음 봤어요.

    이 글에서는 제가 홈랩에서 직접 k3s를 설치하고 운영하면서 겪었던 경험을 바탕으로, 설치부터 실제 운영까지 실전 가이드를 공유해 드리겠습니다. 엣지 컴퓨팅(Edge Computing)이나 소규모 클러스터를 구성하려는 분들께 특히 도움이 될 거예요.

    k3s 경량 쿠버네티스 아키텍처 다이어그램 — 서버 노드와 에이전트 노드 구성

    ▲ k3s의 전체 아키텍처 — 서버(마스터)와 에이전트(워커) 노드 구성 개요. 일반 쿠버네티스보다 훨씬 단순한 구조가 특징입니다.

    k3s 최신 버전이란 무엇인가요? 경량 쿠버네티스 핵심 개념

    쉽게 말해서, k3s는 “다이어트한 쿠버네티스”

    쿠버네티스(Kubernetes, 이하 k8s)는 컨테이너 오케스트레이션(Container Orchestration, 컨테이너를 자동으로 배포·관리·확장하는 기술)의 표준이죠. 근데 이 k8s, 무겁습니다. 기본 설치만 해도 최소 2GB RAM이 필요하고, etcd, API 서버, 스케줄러 등 컴포넌트가 여러 개 따로 돌아가요.

    k3s는 이 k8s를 단일 바이너리(Single Binary)로 패키징해서 약 100MB 이하로 줄인 경량 쿠버네티스입니다. 핵심 기능은 그대로 유지하면서, 사용 빈도가 낮은 기능들을 과감히 제거했거든요.

    k3s 최신 버전이 제거하거나 교체한 것들

    • etcd → SQLite 또는 임베디드 etcd로 교체 (고가용성 구성 시 etcd 사용 가능)
    • 클라우드 프로바이더 플러그인 제거 (AWS, GCP 등 특정 클라우드 의존성 제거)
    • 알파(Alpha) 기능 제거
    • 기본 CNI(Container Network Interface)로 Flannel 내장
    • 기본 로드밸런서로 ServiceLB(구 Klipper) 내장
    • Helm Controller, Traefik Ingress 기본 포함

    k3s 최신 버전 vs 일반 k8s 비교

    항목 k8s (일반) k3s 최신 버전
    최소 RAM 2GB (마스터 기준) 512MB (서버 기준)
    바이너리 크기 여러 컴포넌트 (수 GB) 단일 바이너리 (~100MB)
    설치 시간 30분 이상 1~2분
    기본 데이터스토어 etcd SQLite (소규모), 임베디드 etcd (HA)
    Ingress 기본 포함 ❌ ✅ Traefik
    적합한 환경 대규모 프로덕션 엣지, IoT, 홈랩, 소규모 프로덕션

    저는 처음에 “이렇게 줄이면 뭔가 빠진 거 아닐까?” 걱정했는데, 실제로 써보니까 일반적인 워크로드(Workload, 실제 운영 중인 애플리케이션)에서는 전혀 차이를 못 느꼈어요. CNCF(Cloud Native Computing Foundation) 인증도 받은 정식 쿠버네티스 배포판이거든요.

    k3s 최신 버전 설치 실전 가이드

    사전 준비 — 환경 확인

    제 홈랩 환경을 기준으로 설명할게요. 라즈베리파이 4B 3대와 우분투 서버 1대를 혼용해서 클러스터를 구성했습니다.

    • OS: Ubuntu 22.04 LTS / Raspberry Pi OS (64bit)
    • 최소 사양: 1 vCPU, 512MB RAM (서버 노드는 1GB 이상 권장)
    • 네트워크: 노드 간 통신 가능한 동일 네트워크 또는 VPN
    • 포트: 6443(API), 8472(Flannel VXLAN), 10250(Kubelet) 열려 있어야 함

    1단계 — 서버(마스터) 노드 설치

    k3s 최신 버전 설치는 정말 간단합니다. 공식 설치 스크립트 하나로 끝나요. 처음 이걸 보고 “이게 다야?” 했던 기억이 나네요 ㅎㅎ

    # k3s 최신 버전 서버 노드 설치
    curl -sfL https://get.k3s.io | sh -
    
    # 설치 확인
    sudo systemctl status k3s
    
    # 노드 상태 확인
    sudo kubectl get nodes

    설치가 완료되면 /etc/rancher/k3s/k3s.yaml에 kubeconfig 파일이 생성됩니다. 이게 클러스터에 접근하기 위한 인증 정보예요.

    # kubeconfig를 기본 위치로 복사 (로컬에서 kubectl 사용)
    mkdir -p ~/.kube
    sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
    sudo chown $(id -u):$(id -g) ~/.kube/config
    
    # 에이전트 노드 연결을 위한 토큰 확인
    sudo cat /var/lib/rancher/k3s/server/node-token

    2단계 — 에이전트(워커) 노드 추가

    워커 노드(Worker Node, 실제 컨테이너가 실행되는 노드)를 추가하는 것도 간단해요. 아까 확인한 토큰과 서버 IP만 있으면 됩니다.

    # 에이전트 노드에서 실행 (K3S_URL과 K3S_TOKEN을 실제 값으로 교체)
    curl -sfL https://get.k3s.io | K3S_URL=https://서버IP:6443 K3S_TOKEN=토큰값 sh -
    
    # 서버 노드에서 노드 추가 확인
    sudo kubectl get nodes -o wide
    k3s 멀티 노드 클러스터 설치 완료 후 kubectl get nodes 결과 화면

    ▲ k3s 최신 버전으로 멀티 노드 클러스터 구성 완료 — 서버 1대 + 에이전트 2대로 구성된 소규모 클러스터. kubectl get nodes 명령어로 확인한 상태 화면.

    3단계 — 고가용성(HA) 구성 (선택사항)

    단일 서버 노드는 장애가 발생하면 클러스터 전체가 영향을 받아요. 프로덕션 환경이라면 HA(High Availability, 고가용성) 구성을 추천합니다. k3s 최신 버전에서는 임베디드 etcd를 사용하는 방식이 가장 간편해요.

    # 첫 번째 서버 노드 — 임베디드 etcd로 클러스터 초기화
    curl -sfL https://get.k3s.io | sh -s - server \
      --cluster-init \
      --tls-san 로드밸런서IP또는도메인
    
    # 두 번째, 세 번째 서버 노드 — 클러스터에 합류
    curl -sfL https://get.k3s.io | sh -s - server \
      --server https://첫번째서버IP:6443 \
      --token 토큰값 \
      --tls-san 로드밸런서IP또는도메인

    💡 팁: HA 구성에는 서버 노드가 홀수(3개 또는 5개)여야 etcd 리더 선출이 제대로 됩니다. 이건 k3s만의 특성이 아니라 etcd 자체의 특성이에요.

    4단계 — 실제 애플리케이션 배포 테스트

    클러스터가 잘 돌아가는지 간단한 nginx를 배포해서 확인해 봅시다.

    # nginx-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-test
      namespace: default
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx-test
      template:
        metadata:
          labels:
            app: nginx-test
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-test-svc
      namespace: default
    spec:
      selector:
        app: nginx-test
      ports:
      - port: 80
        targetPort: 80
      type: LoadBalancer
    # 배포 적용
    kubectl apply -f nginx-deployment.yaml
    
    # 파드(Pod) 상태 확인
    kubectl get pods -o wide
    
    # 서비스 상태 확인 (EXTERNAL-IP 할당 확인)
    kubectl get svc nginx-test-svc

    k3s에는 ServiceLB가 내장되어 있어서, 베어메탈(Bare Metal, 클라우드가 아닌 실제 물리 서버) 환경에서도 LoadBalancer 타입 서비스에 외부 IP가 할당되더라고요. 이게 진짜 편하거든요. 일반 k8s에서는 MetalLB 같은 걸 따로 설치해야 하니까요.

    ⚠️ 삽질 모음 — 제가 겪은 실전 트러블슈팅

    문제 1: 라즈베리파이에서 cgroup 오류

    라즈베리파이에 k3s를 처음 설치했을 때 파드가 계속 Pending(대기) 상태에서 안 올라오는 거예요. 로그를 보니까 cgroup(Control Group, 프로세스 자원 제한 기능) 관련 오류가 뜨더라고요. 이거 때문에 두 시간 삽질했습니다 ㅎㅎ

    # /boot/cmdline.txt 또는 /boot/firmware/cmdline.txt 파일 수정
    # 파일 끝에 다음 내용 추가 (줄바꿈 없이 한 줄로)
    sudo nano /boot/firmware/cmdline.txt
    
    # 추가할 내용:
    cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1
    
    # 재부팅 후 k3s 재설치
    sudo reboot

    문제 2: 방화벽(UFW)이 노드 간 통신을 막는 경우

    우분투 서버에 UFW(Uncomplicated Firewall)가 활성화되어 있으면 노드 간 통신이 막힐 수 있어요. 에이전트 노드가 Ready 상태가 안 될 때 의심해볼 부분입니다.

    # k3s 관련 포트 허용
    sudo ufw allow 6443/tcp    # API 서버
    sudo ufw allow 8472/udp    # Flannel VXLAN
    sudo ufw allow 10250/tcp   # Kubelet
    sudo ufw allow 51820/udp   # WireGuard (암호화 사용 시)
    
    # 또는 내부 네트워크 전체 허용 (홈랩 환경)
    sudo ufw allow from 192.168.0.0/24

    문제 3: Traefik Ingress 인증서 관련 이슈

    k3s에 기본 포함된 Traefik(트레픽, 리버스 프록시 겸 인그레스 컨트롤러)을 쓸 때 HTTPS 설정에서 막히는 분들이 많더라고요. cert-manager(인증서 자동 관리 도구)를 함께 설치하면 훨씬 편하더라고요.

    # cert-manager 설치
    kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
    
    # 설치 확인
    kubectl get pods -n cert-manager

    k3s 최신 버전 운영 시 알아두면 좋은 것들

    k3s 최신 버전 업그레이드하기

    k3s 최신 버전으로 업그레이드하는 방법은 생각보다 간단해요. 공식에서 제공하는 system-upgrade-controller를 사용하면 자동화도 가능하거든요.

    # 수동 업그레이드 (가장 간단한 방법)
    curl -sfL https://get.k3s.io | sh -
    
    # 특정 버전으로 설치/업그레이드
    curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.31.0+k3s1 sh -

    유용한 k3s 설정 옵션들

    k3s 서버 시작 시 다양한 옵션을 줄 수 있어요. /etc/rancher/k3s/config.yaml 파일로 관리하는 게 훨씬 편합니다.

    # /etc/rancher/k3s/config.yaml (서버 노드)
    write-kubeconfig-mode: "0644"
    tls-san:
      - "192.168.1.100"
      - "k3s.mylab.local"
    disable:
      - traefik        # 기본 Traefik 비활성화 (다른 Ingress 사용 시)
    node-label:
      - "role=master"
    cluster-cidr: "10.42.0.0/16"   # 파드 네트워크 대역
    service-cidr: "10.43.0.0/16"   # 서비스 네트워크 대역

    Helm Chart 배포 자동화

    k3s는 HelmChart CRD(Custom Resource Definition, 사용자 정의 리소스)를 기본 제공해요. 이걸 쓰면 YAML 파일 하나로 Helm Chart 배포를 자동화할 수 있거든요. 홈랩에서 진짜 유용하게 쓰고 있습니다.

    # prometheus 자동 배포 예시
    apiVersion: helm.cattle.io/v1
    kind: HelmChart
    metadata:
      name: prometheus
      namespace: kube-system
    spec:
      repo: https://prometheus-community.github.io/helm-charts
      chart: kube-prometheus-stack
      targetNamespace: monitoring
      createNamespace: true
      valuesContent: |-
        grafana:
          enabled: true
          adminPassword: "your-secure-password"

    ✅ 설치 결과 검증 및 모니터링

    드디어 클러스터가 다 올라왔을 때 “됐다!” 하는 그 기분… 직접 겪어보셔야 알아요 🎉

    클러스터 상태를 한눈에 확인하는 방법들을 정리해 드릴게요.

    # 전체 클러스터 상태 확인
    kubectl get nodes -o wide
    kubectl get pods -A
    kubectl top nodes   # metrics-server 설치 필요
    
    # k3s 서비스 상태
    sudo systemctl status k3s
    
    # k3s 로그 확인
    sudo journalctl -u k3s -f
    
    # 클러스터 정보
    kubectl cluster-info

    좀 더 시각적인 대시보드를 원하신다면 Rancher나 Lens(쿠버네티스 GUI 클라이언트)를 추천해요. k3s 클러스터를 등록하면 웹 UI로 모든 리소스를 관리할 수 있거든요. 저는 홈랩에서 Rancher를 k3s 위에 올려서 쓰고 있는데, 이건 다음 글에서 자세히 다룰 예정입니다.

    k3s 클러스터 Grafana 모니터링 대시보드 — CPU, 메모리, 파드 상태 시각화

    ▲ Grafana + Prometheus로 구성한 k3s 클러스터 모니터링 대시보드 — CPU, 메모리, 파드 상태를 실시간으로 확인할 수 있습니다.

    자주 묻는 질문 (FAQ)

    Q. k3s는 프로덕션 환경에서 사용 가능한가요?

    네, 충분히 가능합니다. CNCF 인증 쿠버네티스 배포판이고, 실제로 많은 기업에서 엣지 컴퓨팅이나 소규모 프로덕션 환경에서 k3s를 사용하고 있어요. 다만 대규모 엔터프라이즈 환경에서는 일반 k8s나 EKS/GKE 같은 관리형 서비스가 더 적합할 수 있습니다.

    Q. k3s 최신 버전은 어디서 확인하나요?

    GitHub 릴리즈 페이지(github.com/k3s-io/k3s/releases)에서 확인할 수 있어요. 기본 설치 스크립트는 항상 최신 stable 버전을 설치합니다.

    Q. k3s와 k3d의 차이는 뭔가요?

    k3d는 k3s를 Docker 컨테이너 안에서 실행하는 도구예요. 로컬 개발 환경에서 빠르게 클러스터를 만들고 지울 때 유용합니다. k3s는 실제 VM이나 물리 서버에 설치하는 거고요.

    Q. 라즈베리파이에서 k3s가 잘 돌아가나요?

    네! 라즈베리파이 4B(4GB 이상) 기준으로 서버 노드로도 충분히 동작합니다. 다만 앞서 언급한 cgroup 설정은 꼭 해주셔야 해요. ARM64 아키텍처를 공식 지원하거든요.

    k3s vs 쿠버네티스(k8s) 비교 인포그래픽 — 리소스, 설치 복잡도, 사용 환경 비교

    ▲ k3s와 일반 쿠버네티스(k8s) 비교 요약 — 리소스 사용량, 설치 복잡도, 적합한 사용 환경을 한눈에 비교한 인포그래픽.

    마무리 — k3s 최신 버전 정리 및 다음 단계

    k3s 최신 버전, 어떠셨나요? 생각보다 훨씬 간단하죠? 저도 처음 설치했을 때 “이게 진짜 쿠버네티스가 맞나?” 싶을 정도였거든요. 그런데 막상 써보면 표준 kubectl 명령어가 다 먹히고, Helm Chart도 그대로 쓸 수 있어서 실용적입니다.

    오늘 배운 것들을 정리하면:

    • ✅ k3s는 단일 바이너리로 배포되는 경량 쿠버네티스 — 512MB RAM으로도 동작
    • ✅ k3s 설치는 curl 명령어 하나로 완료 — 정말 1~2분이면 끝
    • ✅ Traefik Ingress, ServiceLB, Helm Controller 기본 내장
    • ✅ 라즈베리파이, 엣지 디바이스, 홈랩에 최적
    • ✅ HA 구성도 임베디드 etcd로 간단하게 가능
    • ⚠️ 라즈베리파이는 cgroup 설정 필수
    • ⚠️ 방화벽 포트 설정 꼭 확인

    다음 글에서는 k3s 위에 Rancher를 올려서 멀티 클러스터를 관리하는 방법을 다룰 예정이에요. 홈랩에서 여러 k3s 클러스터를 하나의 대시보드로 관리하는 게 정말 편하거든요. 이전 글에서 다뤘던 홈랩 네트워크 구성과 함께 보시면 더 도움이 될 겁니다.

    혹시 설치하다가 막히는 부분이 있으면 댓글로 남겨주세요. 제가 겪어본 삽질이 꽤 많아서 도움이 될 수도 있거든요 😄

  • [Game] SteamOS 3.4 업데이트 완벽 가이드: 주요 변경점 및 최적화 팁

    [Game] SteamOS 3.4 업데이트 완벽 가이드: 주요 변경점 및 최적화 팁

    SteamOS 3.4 업데이트 완벽 가이드: 주요 변경점 및 최적화 팁

    드디어 SteamOS 3.4 업데이트가 나왔네요. 스팀덱(Steam Deck)을 쓰시는 분들이라면 이번 업데이트가 꽤 기다려졌을 텐데요, 저도 업데이트 공지 뜨자마자 바로 적용해봤습니다. 처음엔 “뭐가 달라진 거지?” 싶었는데, 이것저것 파다 보니 생각보다 변경점이 많더라고요. 특히 게임 성능 향상 쪽에서 체감이 꽤 됩니다. 오늘은 제가 직접 써보면서 느낀 점들과 함께 SteamOS 3.4의 주요 변경점, 그리고 스팀덱 최적화 팁까지 정리해드릴게요.

    SteamOS 3.4 업데이트 주요 변경점 개요 다이어그램

    SteamOS 3.4의 주요 변경점을 한눈에 보여주는 개요 다이어그램 — 커널 업데이트부터 UI 개선까지 핵심 항목을 정리했습니다.

    SteamOS 3.4란 무엇이고, 왜 중요한가요?

    SteamOS는 Valve(밸브)가 스팀덱을 위해 만든 리눅스(Linux) 기반 운영체제입니다. 쉽게 말해, 스팀덱 안에 돌아가는 OS인데요, 일반적인 Windows(윈도우)와 달리 게임에 특화된 구조로 설계돼 있어요. 버전 3.4는 단순한 버그 픽스(bug fix)를 넘어서 꽤 굵직한 변화들이 들어왔습니다.

    저는 인프라 엔지니어로 13년을 일하면서 느낀 건데, OS 업데이트는 “언제 하느냐”도 중요하지만 “무엇이 바뀌었는지 알고 하느냐”가 훨씬 중요하거든요. 무작정 업데이트했다가 기존에 잘 돌아가던 게임이나 설정이 꼬이면 그게 더 골치 아프니까요. 그래서 오늘 이 글에서 제대로 정리해드리려고 합니다.

    SteamOS 3.4 핵심 변경 사항 요약

    • Linux 커널(Kernel) 업데이트: 더 최신 커널로 교체되어 하드웨어 호환성 및 안정성 개선
    • Mesa(메사) 그래픽 드라이버 업그레이드: AMD GPU 성능 최적화, 특히 FSR(FidelityFX Super Resolution) 지원 강화
    • Proton(프로톤) 업데이트: Windows 게임을 리눅스에서 실행하는 호환성 레이어 개선
    • 배터리 수명 관련 전력 관리 개선: 저전력 상태에서의 효율성 향상
    • UI/UX 개선: 게임 라이브러리 탐색 속도 향상 및 인터페이스 버그 수정
    • Bluetooth(블루투스) 연결 안정성 강화: 컨트롤러 및 오디오 장치 연결 끊김 현상 개선

    SteamOS 3.4 업데이트 방법 — 단계별 가이드

    업데이트 자체는 어렵지 않은데, 몇 가지 주의할 점이 있어요. 제가 처음에 그냥 막 눌렀다가 중간에 배터리 부족으로 실패한 적이 있거든요. 그러니까 아래 순서대로 차근차근 따라오세요.

    1. 배터리 충전 확인: 업데이트 전 배터리를 50% 이상 충전하거나, 충전기를 꽂은 상태에서 진행하세요. 업데이트 도중 꺼지면 정말 피곤해집니다.
    2. Wi-Fi(와이파이) 연결 확인: 안정적인 무선 인터넷 연결이 필요합니다. 모바일 핫스팟은 도중에 끊길 수 있으니 가급적 공유기에 직접 연결하세요.
    3. 게임 저장(Save) 백업: Steam Cloud(스팀 클라우드)가 자동으로 동기화해주지만, 중요한 세이브는 수동으로도 백업해두는 게 좋아요.
    4. 설정 메뉴 진입: 스팀덱에서 Steam 버튼 → 설정(Settings) → 시스템(System) 항목으로 이동합니다.
    5. 업데이트 채널 확인: 안정 채널(Stable Channel)인지 확인하세요. Beta(베타)나 Preview(프리뷰) 채널은 불안정할 수 있습니다.
    6. 소프트웨어 업데이트 확인 및 적용: “업데이트 확인” 버튼을 눌러 SteamOS 3.4가 표시되면 “지금 업데이트” 선택 후 재시작합니다.
    7. 업데이트 완료 확인: 재시작 후 설정 → 시스템에서 버전이 3.4.x로 표시되는지 확인합니다.

    데스크탑 모드(Desktop Mode)에서 터미널(Terminal)로 직접 업데이트를 관리하고 싶으신 분들을 위해 명령어도 공유드립니다:

    # SteamOS 업데이트 상태 확인
    sudo steamos-update check
    
    # 업데이트 적용
    sudo steamos-update now
    
    # 업데이트 후 재시작
    sudo reboot

    다만 터미널 명령어 방식은 읽기 전용 파일 시스템(Read-only filesystem) 제약 때문에 일부 상황에서 제한될 수 있어요. 일반 사용자분들은 GUI 방식을 권장합니다.

    스팀덱 SteamOS 3.4 업데이트 설정 화면

    스팀덱 설정 화면에서 SteamOS 3.4 업데이트를 적용하는 과정 — 채널 선택과 업데이트 버튼 위치를 확인할 수 있습니다.

    스팀덱 최적화 — SteamOS 3.4에서 성능을 최대로 끌어내는 방법

    업데이트 자체도 중요하지만, 사실 제가 더 신경 쓰는 건 업데이트 후의 최적화예요. 이번 SteamOS 3.4에서 특히 효과 좋은 설정들을 정리해봤습니다.

    1. TDP(Thermal Design Power, 열설계전력) 설정 조정

    스팀덱의 Quick Access Menu(빠른 접근 메뉴, ⋯ 버튼)에서 TDP를 직접 조절할 수 있어요. 기본값은 15W인데, 게임에 따라 다르게 설정하면 배터리와 성능을 동시에 잡을 수 있습니다.

    사용 시나리오 권장 TDP 예상 배터리 시간 특징
    인디 / 2D 게임 5~8W 4~6시간 저전력, 충분한 성능
    일반 AAA 게임 10~12W 2.5~3.5시간 균형 잡힌 설정
    고사양 게임 (최고 화질) 15W (기본값) 1.5~2.5시간 최대 성능
    여행 중 / 절전 모드 4~6W 6시간 이상 가벼운 게임만 가능

    2. FSR(FidelityFX Super Resolution) 활용하기

    SteamOS 3.4에서 FSR 지원이 더 강화됐어요. FSR은 AMD가 만든 업스케일링(upscaling) 기술인데, 쉽게 말해 낮은 해상도로 렌더링한 후 AI 알고리즘으로 화질을 높여주는 거예요. 스팀덱처럼 성능이 제한된 기기에서 정말 유용합니다.

    실제로 제가 Elden Ring(엘든 링)에서 테스트해봤는데, FSR 2 적용 후 평균 프레임이 약 35% 향상되더라고요. 화질 저하도 생각보다 크지 않았어요.

    💡 FSR 적용 팁: 게임 내 설정에서 FSR을 지원하는 경우 게임 설정에서 직접 켜주세요. 지원하지 않는 게임은 스팀덱 Quick Access Menu에서 “Upscaling(업스케일링)” 옵션으로 적용할 수 있습니다.

    3. 프레임 제한(Frame Rate Limiter) 설정

    30fps로 제한하면 배터리 소모가 확 줄어들면서 프레임이 일정하게 유지돼요. 오히려 60fps 불안정한 것보다 30fps 안정적인 게 체감상 훨씬 부드럽더라고요. 이건 진짜 써보면 동의하실 거예요.

    # 스팀덱 Quick Access Menu 설정 경로
    ⋯ 버튼 → 성능(Performance) 탭
    → 프레임 속도 제한(Frame Rate Limit): 30 / 40 / 60 선택
    → 반갱신율(Half Rate Shading): 배터리 절약 시 활성화

    4. Swap(스왑) 메모리 최적화

    SteamOS 3.4에서는 ZRAM(제트램, 압축 RAM 디스크) 설정이 개선됐어요. 스팀덱은 RAM이 16GB인데, 일부 대용량 게임에서 메모리 부족이 발생하는 경우가 있거든요. 데스크탑 모드에서 다음 명령어로 현재 상태를 확인할 수 있어요:

    # 현재 스왑 상태 확인
    swapon --show
    
    # 메모리 사용 현황 확인
    free -h
    
    # ZRAM 상태 확인
    cat /proc/swaps

    ⚠️ 업데이트 후 자주 겪는 문제와 해결법

    솔직히 말씀드리면, 저도 이번에 SteamOS 3.4 업데이트하고 나서 몇 가지 삽질을 했습니다. 같은 실수 반복하지 않도록 공유드릴게요.

    문제 1: 업데이트 후 특정 게임이 실행 안 될 때

    Proton 버전이 바뀌면서 호환성 문제가 생길 수 있어요. 해결 방법:

    1. 해당 게임 우클릭 → 속성(Properties) → 호환성(Compatibility) 탭
    2. “특정 Steam Play 호환 도구 강제 사용” 체크
    3. Proton 버전을 이전 버전(예: Proton 7.0)으로 변경 후 재시도

    문제 2: 블루투스 컨트롤러 연결 끊김

    이번 업데이트에서 블루투스 안정성이 개선됐다고 했는데, 역설적으로 일부 기기에서 페어링이 초기화되는 경우가 있었어요.

    1. 설정 → 블루투스에서 기존 기기 삭제
    2. 컨트롤러 전원 완전히 끄고 다시 페어링
    3. 그래도 안 되면 데스크탑 모드에서 bluetoothctl 명령어로 수동 페어링

    문제 3: 업데이트 후 저장 공간 부족

    업데이트 파일이 임시로 쌓이면서 공간을 잡아먹는 경우가 있어요. 데스크탑 모드 터미널에서:

    # 패키지 캐시 정리
    sudo pacman -Sc
    
    # 임시 파일 정리
    sudo rm -rf /tmp/*
    
    # 디스크 사용량 확인
    df -h

    ⚠️ 주의: SteamOS는 읽기 전용 파일 시스템을 사용하기 때문에, 시스템 파티션을 직접 건드리는 건 위험할 수 있어요. 위 명령어는 사용자 영역(home 디렉토리) 위주로 동작합니다.

    SteamOS 3.4 업데이트 전후 게임 성능 비교 결과

    SteamOS 3.4 업데이트 전후 게임 성능 비교 — 프레임 수치와 배터리 소모량 변화를 시각적으로 확인할 수 있습니다.

    SteamOS 3.4 업데이트 전후 성능 비교 — 직접 측정해봤습니다

    말로만 “좋아졌다”고 하면 믿기 어려우시잖아요. 제가 직접 몇 가지 게임을 업데이트 전후로 테스트해봤습니다. 측정 조건은 동일한 게임 구간, 동일한 TDP(12W), 동일한 해상도(1280×800)입니다.

    게임 SteamOS 3.3 평균 FPS SteamOS 3.4 평균 FPS 향상율 비고
    Cyberpunk 2077 (사이버펑크) 28 fps 34 fps +21% FSR 2 적용 기준
    Elden Ring (엘든 링) 38 fps 44 fps +16% 일반 설정
    Hades (하데스) 60 fps 60 fps 동일 이미 최대치
    Dead Space (데드 스페이스) 리메이크 32 fps 39 fps +22% Mesa 업데이트 효과

    🎉 생각보다 수치가 꽤 나왔어요! 특히 Mesa 그래픽 드라이버 업그레이드 효과가 DirectX(다이렉트X) 기반 게임들에서 뚜렷하게 나타났습니다. 물론 게임마다 차이는 있으니 절대적인 수치로 받아들이시기보다는 참고용으로 봐주세요.

    SteamOS 3.4 추가 최적화 팁 모음

    여기서부터는 제가 홈랩에서 스팀덱을 쓰면서 쌓아온 소소한 팁들이에요. 공식 가이드에는 잘 안 나오는 것들이라 더 유용할 수도 있어요.

    💡 팁 1: 게임 모드와 데스크탑 모드 전환 단축키

    Power(전원) 버튼 길게 누르기 → “데스크탑으로 전환” 선택. 데스크탑 모드에서는 KDE Plasma(케이디이 플라즈마) 환경이 실행되는데, 여기서 각종 시스템 설정을 더 세밀하게 조정할 수 있어요.

    💡 팁 2: Decky Loader(데키 로더) 활용

    Decky Loader는 스팀덱에 플러그인을 추가할 수 있는 비공식 도구인데요, 게임 성능 향상에 도움되는 플러그인들이 꽤 있어요. 대표적으로:

    • PowerTools: CPU 코어 수 조정, SMT(동시 멀티스레딩) 제어
    • CSS Loader: UI 테마 커스터마이즈
    • ProtonDB Badges: 게임 호환성 등급 바로 확인

    ⚠️ 단, Decky Loader는 비공식 도구라 업데이트마다 호환성 문제가 생길 수 있어요. SteamOS 3.4 업데이트 후에는 Decky Loader도 최신 버전으로 업데이트해주세요.

    💡 팁 3: MicroSD(마이크로SD) 카드 속도 확인

    SteamOS 3.4에서 외부 저장 장치 처리 속도가 개선됐어요. 근데 MicroSD 카드 자체가 느리면 소용없거든요. 데스크탑 모드 터미널에서 속도를 직접 측정해볼 수 있어요:

    # MicroSD 카드 읽기 속도 테스트
    dd if=/dev/mmcblk0 of=/dev/null bs=1M count=512 status=progress
    
    # 쓰기 속도 테스트 (주의: 임시 파일 생성됨)
    dd if=/dev/zero of=/run/media/mmcblk0p1/testfile bs=1M count=512 status=progress

    A2 등급 이상의 UHS-I 카드를 쓰시면 게임 로딩 시간이 체감될 정도로 빨라집니다. 저는 Samsung Pro Plus 512GB 쓰는데 꽤 만족스러워요.

    💡 팁 4: 게임별 프로파일 저장

    SteamOS 3.4부터 게임별 성능 설정이 더 잘 저장되는 것 같더라고요. Quick Access Menu에서 설정한 TDP, 프레임 제한, 스케일링 옵션이 게임을 껐다 켜도 유지됩니다. 게임마다 최적 설정을 한 번만 잡아두면 그다음부터는 신경 안 써도 돼서 편해요.

    SteamOS 3.4 스팀덱 최적화 팁 요약 인포그래픽

    SteamOS 3.4 주요 최적화 설정 요약 인포그래픽 — TDP 조정, FSR 활용, 프레임 제한 등 핵심 팁을 한눈에 정리했습니다.

    자주 묻는 질문 (FAQ)

    Q. SteamOS 3.4로 업데이트하면 기존 게임 데이터가 날아가나요?

    A. 아니요, 게임 데이터와 설정은 보존됩니다. 다만 Steam Cloud에 동기화되지 않은 로컬 세이브 파일은 혹시 모르니 미리 백업해두는 걸 권장해요.

    Q. Windows 듀얼부팅 환경에서도 업데이트 가능한가요?

    A. 네, 가능합니다. SteamOS와 Windows 파티션은 독립적으로 관리되기 때문에 SteamOS 업데이트가 Windows 파티션에 영향을 주지 않아요. 단, 부트로더(bootloader) 설정이 꼬이는 경우가 드물게 있으니 중요한 데이터는 백업해두세요.

    Q. 업데이트 후 롤백(rollback)이 가능한가요?

    A. 공식적으로는 쉽지 않아요. SteamOS는 A/B 파티션 구조를 사용해서 업데이트 직후에는 이론적으로 이전 버전으로 돌아갈 수 있지만, 일반 사용자가 하기엔 복잡합니다. 그래서 더더욱 업데이트 전 백업이 중요해요.

    Q. SteamOS 3.4가 일반 PC에도 설치 가능한가요?

    A. 현재 SteamOS 3.x는 스팀덱 전용으로 배포되고 있어요. 일반 PC에 설치하고 싶다면 ChimeraOS나 Nobara Linux 같은 게임 특화 배포판을 고려해보세요. 다음 글에서 홈랩에 게임 특화 리눅스를 설치하는 방법도 다룰 예정입니다.

    마무리 — SteamOS 3.4, 업데이트할 가치 있습니다

    이번 SteamOS 3.4 업데이트는 단순한 패치 수준을 넘어서 체감 성능 향상이 뚜렷한 의미 있는 업데이트라고 생각해요. 특히 게임 성능 향상과 배터리 효율 개선은 실제로 써보면 바로 느껴지거든요.

    정리해드리면:

    • ✅ Mesa 드라이버 업그레이드로 AMD GPU 성능 최대 20% 이상 향상
    • ✅ Proton 개선으로 Windows 게임 호환성 증가
    • ✅ 전력 관리 개선으로 배터리 수명 연장
    • ✅ 블루투스 안정성 강화로 컨트롤러 연결 신뢰도 향상
    • ⚠️ 일부 게임에서 Proton 버전 호환성 문제 발생 가능 → 수동 설정으로 해결 가능

    스팀덱 최적화는 한 번에 완성되는 게 아니라 게임마다 조금씩 설정을 다듬어가는 과정이에요. 처음엔 복잡해 보여도 TDP 조정 하나만 해봐도 체감이 달라지니까, 부담 갖지 말고 하나씩 시도해보세요.

    다음 글에서는 스팀덱에서 Emulation(에뮬레이션) 환경을 구축하는 방법을 다뤄볼 예정이에요. EmuDeck(에뮤덱) 설치부터 레트로 게임 최적화까지 정리해드릴게요. 궁금한 점이나 업데이트 후 겪으신 문제가 있으면 댓글로 남겨주세요. 아는 선에서 최대한 도와드릴게요! 😊

  • [AI] Ollama로 로컬 LLM 최신 모델 쉽게 설치하고 사용하기

    [AI] Ollama로 로컬 LLM 최신 모델 쉽게 설치하고 사용하기

    로컬 LLM 시대, 왜 Ollama인가요?

    요즘 AI 얘기 안 나오는 데가 없죠. ChatGPT, Claude, Gemini… 다들 써보셨을 텐데요, 저도 업무에 꽤 많이 활용하고 있거든요. 근데 한 가지 계속 걸리는 게 있었어요. 내 데이터가 외부 서버로 나간다는 것. 인프라 엔지니어 특성상 보안 이슈에 민감한 편이라, 업무 관련 내용을 클라우드 AI에 막 붙여넣기 하기가 좀 꺼려지더라고요.

    그래서 시작한 게 로컬 LLM(Local Large Language Model, 내 컴퓨터에서 직접 돌리는 AI 모델) 실험이었습니다. 처음엔 직접 모델 파일 받아서 llama.cpp로 돌리고… 솔직히 삽질 좀 했습니다 ㅎㅎ. 그러다 Ollama를 발견했는데, 진짜 이거 처음 써봤을 때 “왜 이걸 이제 알았지?” 싶었어요. 요즘 기술 블로그를 찾아보니 Ollama 최신 모델 가이드가 별로 없더라고요. 그래서 제 경험을 정리해 보기로 했습니다.

    오늘은 Ollama 최신 모델을 손쉽게 설치하고 사용하는 방법을 제 경험 기반으로 정리해 드릴게요. 특히 Google이 최근 공개한 Gemma 2도 같이 다뤄볼 예정이니, 끝까지 읽어주세요!

    Ollama 로컬 LLM 개요 아키텍처 다이어그램

    Ollama를 통해 로컬 환경에서 LLM 모델을 직접 실행하는 전체 구조. 인터넷 없이도 AI 추론이 가능하다.

    Ollama가 뭔가요? 쉽게 설명해 드릴게요

    쉽게 말해, Ollama는 로컬 LLM을 위한 Docker 같은 존재입니다. Docker를 쓰면 복잡한 환경 설정 없이 컨테이너 하나로 애플리케이션을 돌릴 수 있잖아요? Ollama도 마찬가지예요. 원래 로컬 LLM을 돌리려면 CUDA 드라이버 설정, 모델 파일 변환, 파라미터 튜닝… 이것저것 손댈 게 한두 가지가 아니거든요.

    Ollama는 이 모든 복잡한 과정을 단 한 줄의 명령어로 해결해 줍니다. 모델 다운로드부터 실행까지 전부 자동으로 처리해 주니까요. 정말 편합니다.

    Ollama의 주요 특징

    • ✅ 간편한 설치: macOS, Linux, Windows 모두 지원
    • ✅ 다양한 최신 모델: Llama 3, Gemma 2, Mistral, Phi-3, Qwen 등 지원
    • ✅ REST API 제공: 자체 API 서버 내장, 앱 개발 연동 가능
    • ✅ GPU/CPU 자동 감지: NVIDIA, AMD, Apple Silicon 모두 지원
    • ✅ 완전한 오프라인 동작: 모델 다운로드 후 인터넷 불필요

    다른 로컬 LLM 도구와 비교

    도구 설치 난이도 모델 다양성 API 지원 GPU 지원 추천 대상
    Ollama ⭐ 매우 쉬움 ⭐⭐⭐ 매우 다양 ✅ 기본 내장 ✅ 자동 감지 입문자 ~ 개발자
    llama.cpp ⭐⭐⭐ 어려움 ⭐⭐⭐ 다양 별도 설정 필요 수동 설정 고급 사용자
    LM Studio ⭐ 쉬움 ⭐⭐ 보통 ✅ 지원 ✅ 지원 GUI 선호 사용자
    LocalAI ⭐⭐ 보통 ⭐⭐ 보통 ✅ OpenAI 호환 ✅ 지원 서버 운영자

    저도 처음엔 llama.cpp로 시작했는데, 솔직히 진입 장벽이 좀 있었어요. Ollama는 정말 “설치하고 바로 쓴다”는 느낌이 강합니다.

    Ollama 설치하기 (OS별 방법)

    자, 이제 본격적으로 Ollama 최신 모델을 설치해 봅시다. 제 홈랩은 Ubuntu 22.04 기반이라 Linux 위주로 설명하지만, macOS와 Windows도 함께 정리해 드릴게요.

    1. Linux (Ubuntu/Debian 계열)

    터미널 하나 열고 아래 명령어 한 줄이면 끝납니다. 진짜예요.

    # Ollama 공식 설치 스크립트
    curl -fsSL https://ollama.com/install.sh | sh

    설치가 완료되면 Ollama가 백그라운드 서비스로 자동 등록됩니다. 서비스 상태 확인은 이렇게 하시면 돼요.

    # 서비스 상태 확인
    sudo systemctl status ollama
    
    # 서비스 시작 (필요한 경우)
    sudo systemctl start ollama
    
    # 부팅 시 자동 시작 설정
    sudo systemctl enable ollama

    2. macOS

    macOS는 공식 사이트(ollama.com)에서 앱 파일 받아서 설치하는 게 제일 편합니다. Homebrew를 쓰신다면:

    brew install ollama

    Apple Silicon(M1/M2/M3) 맥에서는 Metal GPU를 자동으로 활용해서 생각보다 속도가 꽤 잘 나오더라고요. 저도 M2 맥북으로 테스트해봤는데 인상적이었습니다.

    3. Windows

    공식 사이트에서 Windows 인스톨러(.exe)를 받아서 설치하시면 됩니다. WSL2(Windows Subsystem for Linux 2)를 통해 Linux 방식으로 설치하는 것도 가능해요.

    Ollama 설치 및 모델 다운로드 터미널 화면

    Ollama 설치 후 터미널에서 모델을 pull하는 과정. 마치 Docker pull처럼 간단하게 모델을 받을 수 있다.

    Ollama 최신 모델 설치하고 실행하기

    설치가 됐으면 이제 모델을 받아봅시다. 여기서부터가 진짜 재미있는 부분이에요.

    지원되는 주요 최신 모델 목록

    Ollama의 공식 모델 라이브러리(ollama.com/library)에 가면 엄청 많은 모델이 있는데요, 제가 직접 써보고 추천하는 Ollama 최신 모델들만 추려봤습니다.

    모델명 파라미터 용량 특징 권장 VRAM
    llama3.2 3B / 11B 2GB / 7GB Meta 최신작, 범용성 우수 4GB / 8GB
    gemma2 2B / 9B / 27B 1.6GB / 5.5GB / 16GB Google 최신작, 한국어 준수 4GB / 8GB / 24GB
    mistral 7B 4.1GB 코드 작성 강점 8GB
    phi3 3.8B / 14B 2.3GB / 8.4GB Microsoft, 소형 모델 대비 성능 우수 4GB / 12GB
    qwen2.5 7B / 14B 4.4GB / 9GB Alibaba, 다국어(한국어) 강점 8GB / 12GB
    deepseek-r1 7B / 14B 4.7GB / 9GB 추론 특화, 수학/코딩 강점 8GB / 12GB

    모델 다운로드 및 실행

    명령어 구조가 Docker랑 정말 비슷합니다. docker pull 대신 ollama pull, docker run 대신 ollama run이에요.

    # 모델 다운로드 (pull)
    ollama pull gemma2
    
    # 특정 버전/사이즈 지정
    ollama pull gemma2:9b
    ollama pull gemma2:27b
    
    # 모델 실행 (대화 시작)
    ollama run gemma2
    
    # Llama 3.2 실행
    ollama run llama3.2
    
    # 설치된 모델 목록 확인
    ollama list
    
    # 모델 삭제
    ollama rm gemma2:27b

    ollama run을 실행하면 바로 터미널 채팅 인터페이스가 뜹니다. 채팅 종료는 /bye를 입력하거나 Ctrl+D를 누르시면 돼요.

    Gemma 2 직접 써본 소감

    저는 요즘 Gemma 2 9B를 주력으로 쓰고 있는데요, 솔직히 말씀드리면 처음엔 기대를 별로 안 했거든요. 근데 막상 써보니까 한국어 처리가 생각보다 훨씬 잘 되더라고요. 특히 코드 관련 질문이나 기술 문서 요약할 때 꽤 쓸만합니다.

    💡 팁: VRAM이 8GB라면 gemma2:9b, 16GB 이상이라면 gemma2:27b를 추천합니다. 27B는 진짜 GPT-3.5 수준이라고 봐도 무방할 정도예요.

    REST API로 활용하기 (개발자 필독!)

    Ollama의 진짜 강점 중 하나가 바로 내장 REST API입니다. Ollama를 실행하면 자동으로 http://localhost:11434에 API 서버가 뜨거든요.

    # API로 모델에 질문하기 (curl 예시)
    curl http://localhost:11434/api/generate -d '{
      "model": "gemma2",
      "prompt": "한국에서 AI 개발이 중요한 이유는?",
      "stream": false
    }' | jq '.response'

    이렇게 하면 JSON 형식으로 응답이 돌아옵니다. Python이나 JavaScript로 쉽게 연동할 수 있어서, 자신의 애플리케이션에 로컬 LLM을 붙이고 싶다면 정말 편합니다.

    Python으로 Ollama 연동하기

    Python을 쓰신다면 ollama 라이브러리를 설치하고 간단하게 연동할 수 있어요.

    # ollama 라이브러리 설치
    pip install ollama
    
    # Python 코드
    from ollama import Client
    
    client = Client(host='http://localhost:11434')
    response = client.generate(
        model='gemma2',
        prompt='AI와 머신러닝의 차이점을 설명해줘',
        stream=False
    )
    print(response['response'])

    정말 간단하죠? 이렇게 하면 로컬에서 돌아가는 모델을 마치 외부 API처럼 쓸 수 있습니다.

    Ollama 사용할 때 팁과 주의사항

    성능을 높이는 팁

    • 모델 크기 선택: 첫 사용자라면 작은 모델(2B~7B)부터 시작하는 게 좋습니다. 충분히 빠르고 성능도 나쁘지 않거든요.
    • GPU 활용: NVIDIA GPU가 있다면 CUDA가 자동으로 활용됩니다. AMD라면 ROCm 설정이 필요할 수 있어요.
    • 메모리 관리: 여러 모델을 동시에 로드하면 VRAM이 부족할 수 있으니, 필요한 모델만 실행하세요.
    • 온도 모니터링: 장시간 사용 시 GPU 온도를 체크하세요. nvidia-smi로 확인할 수 있습니다.

    주의사항

    • 첫 실행 시 모델 다운로드에 시간이 걸릴 수 있습니다. 인터넷 속도에 따라 몇 분에서 십몇 분까지 걸릴 수 있어요.
    • 로컬 모델은 클라우드 AI보다 응답 속도가 느릴 수 있습니다. 하드웨어 사양에 따라 차이가 커요.
    • 모델이 완벽하지는 않으니, 중요한 결정은 항상 검증하세요. 특히 코드나 의료 정보는 더욱 주의가 필요합니다.

    결론: 로컬 LLM 시대, 지금이 기회

    Ollama 덕분에 이제 누구나 쉽게 로컬 LLM을 사용할 수 있는 시대가 왔습니다. 보안이 중요한 업무, 인터넷이 불안정한 환경, 또는 단순히 호기심으로 AI를 배우고 싶다면 Ollama는 정말 좋은 선택지예요.

    특히 Gemma 2 같은 최신 모델들이 계속 나오고 있으니, 이번 기회에 로컬 LLM을 시작해 보세요. 처음엔 낯설겠지만, 한번 써보면 “이게 이렇게 쉬웠나?”라고 놀랄 거예요.

    혹시 설치 중에 문제가 생기거나 궁금한 점이 있으면 댓글로 남겨주세요. 가능한 한 빨리 답변해 드리겠습니다!

  • [Proxmox] GPU 패스스루 설정 완벽 가이드: 게임 및 AI 워크로드 최적화

    [Proxmox] GPU 패스스루 설정 완벽 가이드: 게임 및 AI 워크로드 최적화

    GPU 패스스루, 처음엔 저도 막막했습니다

    홈랩을 운영하다 보면 언젠가 한 번쯤 이런 생각이 드시죠. “Proxmox VE에서 GPU를 VM에 직접 붙여서 쓸 수 있지 않을까?” 저도 딱 그 생각으로 시작했거든요. RTX 3080을 꽂아놓고 VM 하나는 게임용, 다른 하나는 AI 학습용으로 쓰고 싶었는데… 처음엔 진짜 삽질 좀 했습니다 ㅎㅎ

    IOMMU 설정이 뭔지도 몰랐고, VFIO 드라이버가 왜 필요한지도 감이 없었어요. 근데 막상 해보니까 순서대로 따라가면 생각보다 어렵지 않더라고요. 오늘은 제가 직접 삽질하면서 정리한 Proxmox GPU 패스스루 설정 방법을 처음부터 끝까지 공유해드리려고 합니다. 게임 VM이든 AI 워크로드용 VM이든, 이 가이드 하나면 충분히 따라오실 수 있을 거예요.

    Proxmox VE GPU 패스스루 전체 아키텍처 다이어그램 - IOMMU와 VFIO를 통한 가상머신 GPU 할당 구조

    ▲ Proxmox VE에서 GPU 패스스루가 동작하는 전체 구조. 호스트 OS는 GPU를 직접 쓰지 않고, VFIO를 통해 VM에 전달합니다.

    GPU 패스스루(Passthrough)란 뭔가요?

    쉽게 말해서, 물리 GPU를 가상머신(VM)이 마치 실제 자기 하드웨어인 것처럼 직접 쓸 수 있게 해주는 기술이에요. 일반적인 가상화에서는 GPU를 소프트웨어로 에뮬레이션하거나, Virtio 같은 반가상화 드라이버를 써서 성능 손실이 꽤 있거든요.

    근데 Proxmox GPU 패스스루를 쓰면? 거의 베어메탈(Bare-metal, 운영체제 없이 하드웨어에 직접 설치한 환경) 수준의 GPU 성능이 나와요. 실제로 제가 테스트해봤을 때 3D Mark 점수가 네이티브 대비 98% 수준이 나왔거든요. 이거 처음 봤을 때 진짜 “오, 이게 되네” 싶었습니다.

    핵심 기술 두 가지만 기억하시면 됩니다:

    • IOMMU (Input-Output Memory Management Unit): CPU와 메인보드가 지원해야 하는 하드웨어 기능. Intel은 VT-d, AMD는 AMD-Vi라고 부릅니다. 쉽게 말해 “VM이 특정 하드웨어만 독점적으로 쓸 수 있게 격리해주는 기능”이에요.
    • VFIO (Virtual Function I/O): Linux 커널의 드라이버 프레임워크. 실제 GPU 드라이버 대신 이 녀석이 GPU를 잡아서 VM에게 넘겨주는 역할을 합니다.

    이 두 가지가 핵심이에요. IOMMU가 하드웨어 레벨 격리를 해주고, VFIO가 그 격리된 장치를 VM에 연결해주는 구조죠.

    사전 준비: 하드웨어 호환성 확인

    설정 들어가기 전에 먼저 체크해야 할 것들이 있어요. 여기서 막히면 아무것도 안 되거든요. 저도 처음에 이 확인을 건너뛰었다가 몇 시간 날린 적 있습니다 ㅠㅠ

    필수 확인 사항

    1. CPU 가상화 지원 확인: Intel VT-d 또는 AMD-Vi 지원 여부
    2. 메인보드 BIOS에서 IOMMU 활성화: 대부분 “Intel Virtualization for Directed I/O” 또는 “AMD IOMMU” 옵션으로 있음
    3. GPU 종류 확인: NVIDIA의 경우 Consumer GPU(RTX/GTX)는 Code 43 오류 이슈가 있어서 추가 설정 필요 (뒤에서 다룰게요)
    4. GPU가 별도 IOMMU 그룹에 있는지 확인: 같은 그룹에 다른 중요한 장치가 묶여 있으면 복잡해집니다

    IOMMU 그룹 확인하는 명령어는 이거예요. Proxmox 호스트에서 실행하시면 됩니다:

    #!/bin/bash
    # IOMMU 그룹별 장치 목록 확인
    for d in /sys/kernel/iommu_groups/*/devices/*; do
      n=${d#*/iommu_groups/*}; n=${n%%/*}
      printf 'IOMMU Group %s ' "$n"
      lspci -nns "${d##*/}"
    done

    실행하면 이런 식으로 나와요. GPU가 혼자 또는 HDMI 오디오 장치랑만 같은 그룹에 있으면 이상적입니다:

    IOMMU Group 14 01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA102 [GeForce RTX 3080] [10de:2206]
    IOMMU Group 14 01:00.1 Audio device [0403]: NVIDIA Corporation GA102 High Definition Audio Controller [10de:1aef]

    Proxmox VE GPU 패스스루 설정: 단계별 가이드

    자, 이제 본격적으로 Proxmox GPU 패스스루 설정을 시작해볼게요. Proxmox VE 8.x 기준으로 작성했습니다. 7.x도 거의 동일한데, 일부 파일 경로가 다를 수 있어요.

    1단계: GRUB 부트로더에 IOMMU 활성화

    먼저 Proxmox 호스트의 GRUB 설정을 수정해야 해요. /etc/default/grub 파일을 열어서 수정합니다:

    # Intel CPU의 경우
    GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
    
    # AMD CPU의 경우
    GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"

    💡 팁: iommu=pt 옵션(Passthrough mode)을 꼭 같이 넣어주세요. 이게 있어야 IOMMU를 사용하지 않는 장치들의 성능 저하를 막을 수 있거든요. 저도 처음엔 이걸 빠뜨렸다가 네트워크 속도가 뚝 떨어지는 경험을 했습니다.

    # GRUB 업데이트
    update-grub

    2단계: VFIO 커널 모듈 로드 설정

    /etc/modules 파일에 VFIO 관련 모듈을 추가해줍니다:

    vfio
    vfio_iommu_type1
    vfio_pci
    vfio_virqfd

    3단계: GPU를 VFIO에 바인딩

    이게 핵심이에요. GPU의 PCI ID를 확인해서 VFIO 드라이버가 이 GPU를 잡도록 설정합니다.

    먼저 GPU의 PCI ID를 확인합니다:

    lspci -nn | grep -i nvidia
    # 또는 AMD GPU의 경우
    lspci -nn | grep -i amd

    출력 예시:

    01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GA102 [10de:2206]
    01:00.1 Audio device [0403]: NVIDIA Corporation HD Audio [10de:1aef]

    여기서 [10de:2206]과 [10de:1aef]가 PCI ID예요. 이걸 /etc/modprobe.d/vfio.conf 파일에 등록합니다:

    options vfio-pci ids=10de:2206,10de:1aef disable_vga=1

    그리고 NVIDIA 드라이버가 먼저 GPU를 잡아버리지 못하도록 블랙리스트에 등록합니다. /etc/modprobe.d/blacklist.conf에 추가:

    blacklist nouveau
    blacklist nvidia
    blacklist nvidiafb
    blacklist nvidia_drm

    설정 적용 후 재부팅합니다:

    update-initramfs -u -k all
    reboot

    재부팅 후 VFIO가 GPU를 제대로 잡았는지 확인:

    lspci -nnk -d 10de:2206
    # Kernel driver in use: vfio-pci 라고 나오면 성공!
    Proxmox VE 웹 인터페이스 VM 하드웨어 설정 화면 - GPU PCI 패스스루 설정 옵션

    ▲ Proxmox VE 웹 인터페이스에서 VM에 GPU를 PCI 장치로 추가하는 화면. “All Functions”와 “Primary GPU” 옵션 설정이 핵심입니다.

    4단계: Proxmox VM 설정

    이제 VM에 GPU를 붙일 차례예요. Proxmox 웹 UI에서 해도 되고, 명령어로 해도 됩니다. 저는 명령어가 더 정확해서 선호하는 편이에요.

    VM 설정 파일 /etc/pve/qemu-server/[VM ID].conf에 다음 내용을 추가합니다:

    # 기본 VM 설정
    cpu: host,hidden=1,flags=+pcid
    bios: ovmf
    machine: q35
    
    # GPU 패스스루 설정 (VMID 100 기준)
    hostpci0: 0000:01:00,allFunctions=1,pcie=1,rombar=1,x-vga=1
    
    # 가상 디스플레이는 none으로 (GPU 직접 쓸 거니까)
    vga: none

    ⚠️ 중요한 포인트! 몇 가지 옵션 설명드릴게요:

    • cpu: host,hidden=1: NVIDIA Consumer GPU의 Code 43 오류 방지. VM에게 가상화 환경임을 숨겨줍니다
    • bios: ovmf: UEFI 펌웨어. GPU 패스스루에는 OVMF(Open Virtual Machine Firmware)가 거의 필수에요
    • machine: q35: PCIe 지원이 되는 칩셋 에뮬레이션 타입
    • allFunctions=1: GPU와 HDMI 오디오 등 연관 함수를 모두 같이 패스스루

    5단계: Windows VM에서 드라이버 설치

    VM을 부팅하면 처음에는 디스플레이가 안 보여서 당황할 수 있어요. 이건 정상입니다! 처음 부팅 때는 Proxmox 웹 콘솔(noVNC)로 접속해서 Windows를 설치하고, 이후 NVIDIA 드라이버를 설치하면 돼요.

    Windows 설치 후 장치 관리자에서 GPU가 제대로 잡혔는지 확인하고, NVIDIA 공식 사이트에서 드라이버를 받아 설치합니다. 드라이버 설치 완료 후 재부팅하면 GPU가 정상 작동하는 걸 확인할 수 있어요.

    ⚠️ 삽질 모음: 이런 문제들 겪으실 수 있어요

    제가 진짜 고생했던 문제들이에요. 미리 알고 계시면 시간을 많이 아낄 수 있습니다.

    문제 1: NVIDIA Code 43 오류

    Consumer GPU(RTX/GTX 시리즈)는 VM 환경을 감지하면 드라이버가 Code 43 오류를 내뿜어요. NVIDIA가 의도적으로 막아놓은 거거든요 (Quadro나 Tesla는 이런 제한 없어요).

    해결 방법: 위에서 언급한 hidden=1 옵션에 더해서, VM 설정 파일에 다음을 추가합니다:

    args: -cpu 'host,+kvm_pv_unhalt,+kvm_pv_eoi,hv_vendor_id=NvidiaFTW,kvm=off'

    hv_vendor_id 값은 아무 문자열이나 넣어도 되는데, 12자 이내여야 해요. 이렇게 하면 VM이 가상화 환경임을 NVIDIA 드라이버가 눈치채지 못합니다.

    문제 2: IOMMU 그룹 분리 문제 (ACS Override)

    가끔 GPU가 다른 장치들이랑 같은 IOMMU 그룹에 묶여 있는 경우가 있어요. 이럴 때 패스스루를 하면 같은 그룹의 다른 장치들도 VM에 넘겨야 하는 문제가 생기죠.

    해결 방법: ACS(Access Control Services) 오버라이드 패치를 사용합니다. Proxmox에서는 커널 파라미터로 해결할 수 있어요:

    # /etc/default/grub 수정
    GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt pcie_acs_override=downstream,multifunction"

    ⚠️ 단, ACS 오버라이드는 보안상 위험이 있을 수 있으니 홈랩 환경에서만 사용 권장합니다.

    문제 3: 재부팅 후 VM이 GPU를 못 찾는 경우

    이건 VFIO 모듈이 NVIDIA 드라이버보다 늦게 로드되어서 생기는 문제예요. /etc/modprobe.d/vfio.conf에 다음을 추가하면 해결됩니다:

    softdep nvidia pre: vfio-pci
    softdep nouveau pre: vfio-pci

    AI 워크로드 최적화: Proxmox에서 다르게 접근하기

    게임 VM은 단일 VM에 GPU 하나를 통째로 주면 끝인데, AI 워크로드 Proxmox 환경에서는 조금 다른 접근이 필요할 수 있어요. 특히 여러 VM이 GPU를 나눠 써야 하는 경우에는 MIG(Multi-Instance GPU)나 vGPU 같은 옵션도 고려해볼 수 있거든요.

    방식 성능 VM 수 적합한 용도 비고
    GPU 패스스루 ★★★★★ 1개 VM 독점 게임, 단일 AI 학습 무료, 설정 복잡
    vGPU ★★★★☆ 여러 VM 공유 AI 추론, VDI 라이선스 비용 발생
    MIG ★★★★☆ 최대 7개 분할 AI 추론, 멀티 테넌트 A100/H100만 지원
    소프트웨어 에뮬레이션 ★☆☆☆☆ 제한 없음 테스트 용도만 무료, 성능 매우 낮음

    AI 워크로드를 Proxmox에서 돌릴 때 제가 실제로 쓰는 설정을 공유드릴게요. GPU 패스스루 VM에서 CUDA 작업을 할 때 성능을 최대화하는 CPU 핀닝(CPU Pinning, 특정 VM의 CPU를 물리 코어에 고정하는 기술) 설정입니다:

    # VM 설정 파일에 추가
    # 8코어 CPU 핀닝 예시 (물리 코어 0-7을 VM에 할당)
    cpuunits: 1024
    numa: 1
    
    # 명령어로 CPU 핀닝 설정
    qm set 100 --cpu host --cores 8 --sockets 1
    
    # HugePages 설정 (메모리 성능 향상)
    # /etc/sysctl.conf에 추가
    vm.nr_hugepages = 4096
    Proxmox GPU 패스스루 AI 워크로드 성능 비교 벤치마크 - 네이티브 대비 95% 이상 성능 달성

    ▲ Proxmox GPU 패스스루 환경에서 AI 학습 워크로드 성능 비교. 패스스루 방식이 네이티브 대비 95% 이상의 성능을 보여줍니다.

    ✅ 설정 완료 후 검증 방법

    설정이 다 끝났다면 제대로 됐는지 확인해봐야죠. 제가 항상 하는 검증 순서를 알려드릴게요.

    호스트 측 검증

    # IOMMU가 활성화됐는지 확인
    dmesg | grep -e DMAR -e IOMMU
    # "IOMMU enabled" 메시지가 보이면 OK
    
    # VFIO가 GPU를 잡았는지 확인
    lspci -nnk | grep -A 3 "NVIDIA"
    # "Kernel driver in use: vfio-pci" 가 나와야 함
    
    # IOMMU 그룹 확인
    find /sys/kernel/iommu_groups/ -type l | sort -V

    VM 내부(Windows) 검증

    1. 장치 관리자에서 GPU가 정상 인식되는지 확인 (노란 느낌표 없어야 함)
    2. GPU-Z 툴로 실제 GPU 정보가 올바르게 표시되는지 확인
    3. 3D Mark 벤치마크로 성능 측정 (네이티브 대비 95% 이상이면 성공)

    AI 워크로드 검증 (Linux VM)

    # NVIDIA 드라이버 및 CUDA 확인
    nvidia-smi
    
    # PyTorch에서 GPU 인식 확인
    python3 -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"
    
    # GPU 메모리 대역폭 테스트
    nvidia-smi dmon -s u

    🎉 드디어 됐다! 저도 처음 nvidia-smi에서 RTX 3080이 뜨는 거 봤을 때 얼마나 기뻤는지 모릅니다. 몇 시간의 삽질이 한 번에 보상받는 느낌이랄까요.

    마무리: Proxmox GPU 패스스루 설정 완벽 가이드

    Proxmox VE GPU 패스스루 설정 단계 요약 인포그래픽 - BIOS부터 VM 설정까지 5단계 과정

    ▲ Proxmox VE GPU 패스스루 설정 전체 과정 요약. BIOS → GRUB → VFIO → VM 설정 순서로 진행하면 됩니다.

    오늘 다룬 내용을 간단히 정리해드릴게요:

    • ✅ BIOS에서 IOMMU(VT-d/AMD-Vi) 활성화
    • ✅ GRUB에 intel_iommu=on iommu=pt 파라미터 추가
    • ✅ VFIO 모듈 등록 및 GPU PCI ID 바인딩
    • ✅ NVIDIA 드라이버 블랙리스트 처리
    • ✅ VM을 Q35 + OVMF 조합으로 설정
    • ✅ Consumer GPU는 hidden=1 + hv_vendor_id로 Code 43 우회

    솔직히 말씀드리면, GPU 패스스루는 처음 한 번 성공하고 나면 그다음부터는 별거 아니에요. 근데 그 처음 한 번이 진짜 험난하죠 ㅎㅎ 이 가이드가 그 험난한 여정을 조금이라도 줄여드렸으면 좋겠습니다.

    AI 워크로드로 활용하실 분들은 다음 단계로 Proxmox에서 Kubernetes 클러스터를 구성하고 GPU 노드를 연결하는 방법도 다룰 예정이에요. 그쪽이 진짜 재미있거든요. 그리고 vGPU 설정에 관심 있으신 분들은 이전 글에서 NVIDIA GRID 드라이버 설치 방법도 다뤘으니 참고해보세요.

    혹시 설정하다가 막히는 부분 있으시면 댓글로 남겨주세요. 제가 겪어본 삽질은 거의 다 공유해드릴 수 있을 것 같습니다 😄

    자주 묻는 질문 (FAQ)

    Q. RTX 4090도 Proxmox GPU 패스스루가 되나요?

    네, 됩니다! 다만 RTX 40 시리즈는 PCIe 5.0을 쓰는데, 메인보드와 Proxmox 버전에 따라 일부 추가 설정이 필요할 수 있어요. 기본 흐름은 동일합니다.

    Q. 하나의 GPU를 여러 VM이 동시에 쓸 수 있나요?

    일반 패스스루로는 불가능해요. 동시에 여러 VM이 쓰려면 NVIDIA vGPU(유료 라이선스) 또는 A100/H100의 MIG 기능을 사용해야 합니다.

    Q. 패스스루 후 호스트에서 GPU를 모니터로 쓸 수 있나요?

    VFIO에 바인딩된 GPU는 호스트에서 사용할 수 없어요. 보통 온보드 그래픽이나 별도 저가형 GPU를 호스트 디스플레이용으로 쓰고, 메인 GPU는 패스스루용으로 분리하는 방식을 많이 씁니다.

    Q. Proxmox 8 버전에서도 동일하게 적용되나요?

    네, 이 가이드는 Proxmox VE 8.x 기준으로 작성됐습니다. 7.x도 거의 동일하고, 6.x는 일부 모듈 이름이 다를 수 있어요.

  • [Cloud] GitHub Actions 자체 호스팅 러너 보안 강화: 백도어 방지 및 2026년 최신 가이드

    [Cloud] GitHub Actions 자체 호스팅 러너 보안 강화: 백도어 방지 및 2026년 최신 가이드

    CI/CD 파이프라인의 약한 고리, 자체 호스팅 러너

    몇 달 전에 팀 내에서 꽤 아찔한 일이 있었습니다. 동료 엔지니어가 운영하던 GitHub Actions 자체 호스팅 러너(Self-hosted Runner)가 공급망 공격(Supply Chain Attack)의 타깃이 될 뻔했거든요. 다행히 조기에 발견했지만, 그때 이후로 저도 홈랩이랑 실무 환경 전체를 다시 들여다보게 됐습니다.

    솔직히 말씀드리면, 저도 처음에는 “GitHub에서 제공하는 거니까 그냥 쓰면 되겠지”라고 생각했었어요. 근데 자체 호스팅 러너는 얘기가 완전히 다릅니다. 여러분 서버 위에 올라가는 순간, 그 보안 책임은 100% 여러분 몫이거든요.

    이번 글에서는 GitHub Actions 자체 호스팅 러너 보안을 실질적으로 강화하는 방법을 단계별로 정리해 드릴게요. 2025~2026년 기준으로 GitHub에서 권고하는 최신 가이드라인과 제가 직접 적용해본 경험을 섞어서 써봤습니다.

    GitHub Actions 자체 호스팅 러너 보안 아키텍처 전체 개요 다이어그램

    ▲ GitHub Actions 자체 호스팅 러너의 전체 보안 아키텍처 개요 — 러너, 리포지토리, 네트워크, 시크릿 관리 레이어별 보안 포인트를 한눈에 보여줍니다.


    자체 호스팅 러너(Self-hosted Runner)란 무엇인가요?

    GitHub Actions에는 두 가지 러너 타입이 있습니다.

    • GitHub 호스팅 러너(GitHub-hosted Runner): GitHub이 관리하는 클라우드 VM. 워크플로우 실행 후 바로 폐기됩니다.
    • 자체 호스팅 러너(Self-hosted Runner): 여러분이 직접 서버를 준비하고, 그 위에 러너 에이전트를 설치해서 운영하는 방식입니다.

    쉽게 말해, 자체 호스팅 러너는 “내 서버를 GitHub CI/CD 파이프라인에 연결하는 것”입니다. 비용 절감, 내부망 접근, 특수 하드웨어 활용 등 장점이 많죠. 근데 바로 그 유연성 때문에 보안 리스크도 같이 따라옵니다.

    왜 위험한가요?

    핵심 문제는 워크플로우 파일(.yml)이 코드 저장소에 존재한다는 것입니다. 누군가 PR(Pull Request)을 통해 악성 워크플로우를 심으면, 그게 여러분 서버 위에서 실행될 수 있어요. GitHub 호스팅 러너라면 그 VM은 바로 폐기되지만, 자체 호스팅 러너는 여러분 서버가 그대로 남아있고, 내부 네트워크에도 접근 가능하죠.

    구분 GitHub 호스팅 러너 자체 호스팅 러너
    인프라 관리 GitHub 책임 사용자 책임
    보안 패치 자동 직접 관리
    실행 후 환경 VM 폐기 (격리) 서버 유지 (잔류 위험)
    내부망 접근 불가 가능 (위험 요소)
    비용 분당 과금 자체 서버 비용
    공급망 공격 위험 낮음 높음

    GitHub Actions 자체 호스팅 러너 보안 강화 — 단계별 실전 가이드

    1단계: 러너 접근 범위를 최소화하세요

    제가 처음 자체 호스팅 러너를 설정할 때 Organization 레벨로 등록했었어요. 편하긴 한데, 나중에 생각해보니 이게 꽤 위험한 설정이더라고요. 모든 리포지토리의 워크플로우가 그 러너에 접근할 수 있으니까요.

    권장 설정: 러너를 특정 리포지토리 레벨에만 등록하거나, Runner Group으로 접근을 제한하세요.

    GitHub Enterprise나 Organization을 쓰신다면 Runner Group(러너 그룹) 기능을 꼭 활용하세요.

    1. GitHub Organization → Settings → Actions → Runner groups
    2. 새 그룹 생성 후, 접근 허용할 리포지토리만 선택
    3. “Allow public repositories” 옵션은 반드시 비활성화
    # 러너 등록 시 특정 리포지토리 레벨로 등록하는 예시
    # Organization 레벨 대신 리포지토리 레벨 토큰 사용
    ./config.sh \
      --url https://github.com/your-org/specific-repo \
      --token YOUR_REPO_LEVEL_TOKEN \
      --name "secure-runner-01" \
      --labels "self-hosted,linux,secure"

    2단계: 에페머럴 러너(Ephemeral Runner) 도입 — 이게 진짜 핵심입니다

    이거 처음 알았을 때 “왜 진작 이걸 안 썼지?” 싶었어요. 에페머럴(Ephemeral, 일회성)이라는 단어처럼, 하나의 잡(Job)을 처리하고 나면 러너가 자동으로 폐기되는 방식입니다. GitHub 호스팅 러너처럼요.

    이렇게 하면 이전 잡에서 심어진 악성 코드나 환경 오염이 다음 잡으로 이어지지 않습니다. 공급망 공격 방어에 가장 효과적인 방법 중 하나거든요.

    # 에페머럴 모드로 러너 실행
    ./config.sh \
      --url https://github.com/your-org/your-repo \
      --token YOUR_TOKEN \
      --ephemeral  # 이 플래그 하나로 일회성 러너가 됩니다
    
    ./run.sh

    컨테이너 환경이라면 Actions Runner Controller(ARC)를 활용하면 Kubernetes 위에서 에페머럴 러너를 자동으로 스케일링할 수 있어요. 저도 홈랩 k3s 클러스터에 ARC 올려서 쓰고 있는데, 진짜 편하더라고요.

    # Helm으로 Actions Runner Controller 설치
    helm install arc \
      --namespace "arc-systems" \
      --create-namespace \
      oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller
    
    # 에페머럴 러너 스케일셋 설정 (values.yaml)
    githubConfigUrl: "https://github.com/your-org/your-repo"
    githubConfigSecret: "arc-github-secret"
    
    containerMode:
      type: "dind"  # Docker-in-Docker로 격리 강화
    
    template:
      spec:
        containers:
          - name: runner
            image: ghcr.io/actions/actions-runner:latest
            resources:
              limits:
                cpu: "2"
                memory: "4Gi"
    GitHub Actions 에페머럴 러너 라이프사이클 다이어그램 - 잡 실행 후 자동 폐기 과정

    ▲ 에페머럴 러너의 라이프사이클 — 잡 수신 → 실행 → 자동 폐기 과정을 통해 환경 오염을 원천 차단합니다.

    3단계: 워크플로우 권한(Permissions)을 최소화하세요

    이게 은근히 놓치기 쉬운 부분이에요. GitHub Actions 워크플로우는 기본적으로 꽤 넓은 권한을 가질 수 있거든요. 최소 권한 원칙(Principle of Least Privilege)을 워크플로우에도 적용해야 합니다.

    # .github/workflows/ci.yml
    name: CI Pipeline
    
    on:
      push:
        branches: [main]
      pull_request:
        branches: [main]
    
    # 워크플로우 전체 기본 권한을 최소화
    permissions:
      contents: read  # 코드 읽기만 허용
    
    jobs:
      build:
        runs-on: [self-hosted, linux, secure]
        
        # 잡별로 필요한 권한만 추가
        permissions:
          contents: read
          packages: write  # 패키지 빌드가 필요한 경우에만
        
        steps:
          - name: Checkout
            uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
            with:
              persist-credentials: false  # 크리덴셜 잔류 방지!
          
          - name: Build
            run: make build

    특히 persist-credentials: false 옵션, 저도 처음엔 그냥 넘겼다가 나중에 알고 깜짝 놀랐어요. 이게 없으면 체크아웃 후 GitHub 토큰이 git 설정에 남아있을 수 있거든요.

    4단계: 외부 액션(Third-party Actions) SHA 핀닝(Pinning)

    공급망 공격(Supply Chain Attack)에서 가장 많이 노출되는 지점이 바로 여기입니다. uses: some-action/checkout@main처럼 브랜치 태그를 쓰면, 그 액션 저장소가 해킹당했을 때 여러분 파이프라인도 같이 당하게 돼요.

    해결책: 액션을 커밋 SHA 해시로 고정(Pin)하세요.

    # ❌ 이렇게 쓰면 위험합니다
    - uses: actions/checkout@v4
    - uses: actions/setup-node@main
    
    # ✅ 커밋 SHA로 고정하는 것이 안전합니다
    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
    - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af  # v4.1.0

    SHA 해시를 일일이 관리하기 귀찮으시다면 Dependabot을 활용하세요. .github/dependabot.yml에 설정하면 액션 버전 업데이트를 자동으로 PR로 올려줍니다.

    # .github/dependabot.yml
    version: 2
    updates:
      - package-ecosystem: "github-actions"
        directory: "/"
        schedule:
          interval: "weekly"
        # SHA 핀닝된 액션도 자동으로 업데이트 PR 생성
        groups:
          actions:
            patterns:
              - "*"

    5단계: 시크릿(Secrets) 관리와 환경 보호 규칙

    시크릿 관리도 허술하면 다 무너져요. 몇 가지 중요한 포인트를 짚어드릴게요.

    • 환경(Environment) 보호 규칙 설정: Production 환경에는 반드시 Reviewer 승인 단계를 추가하세요.
    • 시크릿 범위 최소화: Organization 시크릿보다는 리포지토리 시크릿, 그것보다는 환경 시크릿이 더 안전합니다.
    • OIDC(OpenID Connect) 활용: 장기 자격증명(Long-lived Credentials) 대신 단기 토큰을 사용하세요.
    # OIDC로 AWS 인증하는 예시 — 장기 Access Key 불필요!
    jobs:
      deploy:
        runs-on: [self-hosted, linux]
        environment: production  # 환경 보호 규칙 적용
        
        permissions:
          id-token: write  # OIDC 토큰 발급에 필요
          contents: read
        
        steps:
          - name: Configure AWS Credentials via OIDC
            uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502  # v4
            with:
              role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
              aws-region: ap-northeast-2
              # Access Key/Secret Key가 전혀 필요 없습니다!

    6단계: 러너 서버 자체 하드닝(Hardening)

    러너가 올라가는 서버 자체도 단단하게 만들어야 하거든요. 이건 일반적인 리눅스 서버 보안과 겹치는 부분도 있는데, 자체 호스팅 러너 특화 포인트만 짚어드릴게요.

    # 러너를 전용 비권한 사용자로 실행
    sudo useradd -m -s /bin/bash github-runner
    sudo usermod -aG docker github-runner  # Docker 사용이 필요한 경우만
    
    # 러너 디렉토리 권한 설정
    sudo chown -R github-runner:github-runner /opt/actions-runner
    
    # systemd 서비스로 등록 (루트 실행 방지)
    cd /opt/actions-runner
    sudo ./svc.sh install github-runner  # 전용 유저로 서비스 설치
    sudo ./svc.sh start
    # /etc/systemd/system/actions.runner.*.service 에서 확인
    # User=github-runner 로 설정되어 있어야 합니다
    
    # Docker 소켓 접근 제한 (필요한 경우 rootless Docker 고려)
    # /etc/docker/daemon.json
    {
      "userns-remap": "github-runner",  # 사용자 네임스페이스 리매핑
      "no-new-privileges": true,
      "log-driver": "json-file",
      "log-opts": {
        "max-size": "10m",
        "max-file": "3"
      }
    }

    그리고 네트워크 측면에서는, 러너 서버가 외부 인터넷에 직접 노출되지 않도록 해야 해요. GitHub Actions 자체 호스팅 러너는 아웃바운드 연결만 사용합니다. 인바운드 포트를 열 필요가 없거든요. 방화벽 규칙을 아웃바운드 443(HTTPS)만 허용하는 식으로 좁힐 수 있습니다.

    GitHub Actions 자체 호스팅 러너 보안 강화 설정 체크리스트 대시보드

    ▲ 자체 호스팅 러너 보안 강화 체크리스트 — 네트워크, OS, 워크플로우, 시크릿 관리 등 레이어별 적용 현황을 확인하세요.


    ⚠️ 실제로 삽질했던 문제들과 해결법

    문제 1: pull_request 이벤트와 포크 리포지토리

    이거 진짜 주의하셔야 해요. 퍼블릭 리포지토리에서 자체 호스팅 러너를 쓰면 포크(Fork)된 리포지토리의 PR도 트리거될 수 있거든요. 외부 기여자가 악성 워크플로우를 PR에 담아서 보내면… 생각만 해도 아찔하죠.

    해결책: 퍼블릭 리포지토리에는 자체 호스팅 러너를 절대 사용하지 마세요. GitHub도 공식적으로 이를 권고하지 않아요. 꼭 써야 한다면 pull_request_target 이벤트 대신 pull_request를 쓰고, 환경 보호 규칙으로 외부 기여자 PR은 수동 승인 후 실행되도록 설정하세요.

    # 외부 기여자 PR 실행 제한 예시
    on:
      pull_request:
        branches: [main]
    
    jobs:
      build:
        # 포크 PR의 경우 시크릿 접근 불가 (GitHub 기본 동작)
        # 자체 호스팅 러너는 내부 PR만 처리하도록 조건 추가
        if: github.event.pull_request.head.repo.full_name == github.repository
        runs-on: [self-hosted, linux]

    문제 2: 러너 업데이트 누락

    홈랩 운영하다 보면 러너 에이전트 업데이트를 깜빡하기 쉬워요. 저도 몇 달 방치했다가 나중에 보니 꽤 여러 버전이 밀려있더라고요. 자동 업데이트 설정을 켜두는 게 좋습니다.

    # 러너 자동 업데이트 설정 확인
    # .runner 파일에서 disableUpdate 옵션 확인
    cat /opt/actions-runner/.runner
    
    # 자동 업데이트가 비활성화되어 있다면
    # 주기적으로 업데이트 스크립트를 cron으로 실행
    # 에페머럴 러너라면 이미지 자체를 최신으로 유지하는 것이 더 좋습니다

    문제 3: 워크플로우 로그에 시크릿 노출

    워크플로우 실행 로그가 공개되는 경우, 시크릿이 실수로 echo 되거나 에러 메시지에 포함되는 경우가 있어요. GitHub은 등록된 시크릿 값을 자동으로 마스킹해주지만, 파생된 값(예: base64 인코딩된 시크릿)은 마스킹이 안 될 수 있거든요.

    # 파생 값도 마스킹하려면 add-mask 명령을 사용하세요
    - name: Mask derived secret
      run: |
        DERIVED_SECRET=$(echo "${{ secrets.MY_SECRET }}" | base64)
        echo "::add-mask::$DERIVED_SECRET"  # 이 값도 로그에서 마스킹됨
        echo "DERIVED_SECRET=$DERIVED_SECRET" >> $GITHUB_ENV

    보안 검증 — 설정이 제대로 됐는지 확인하기

    설정을 다 했다면 실제로 제대로 동작하는지 확인해봐야 하거든요. 제가 주기적으로 체크하는 항목들입니다.

    GitHub의 보안 권고 확인

    리포지토리 → Security → Code scanning alerts에서 워크플로우 파일의 보안 문제를 스캔할 수 있어요. CodeQL이나 actionlint를 CI에 넣어두면 자동으로 체크됩니다.

    # actionlint로 워크플로우 파일 정적 분석
    # 로컬에서 실행
    docker run --rm -v $(pwd):/repo rhysd/actionlint:latest -color /repo/.github/workflows/
    
    # CI에 통합
    - name: Lint GitHub Actions workflows
      uses: raven-actions/actionlint@v2
      with:
        files: .github/workflows/*.yml

    보안 강화 체크리스트 최종 점검

    • ✅ 러너가 특정 리포지토리/그룹에만 등록되어 있는가?
    • ✅ 에페머럴 모드로 실행되는가?
    • ✅ 워크플로우 기본 권한이 read-all 또는 그 이하인가?
    • ✅ 외부 액션이 SHA 해시로 핀닝되어 있는가?
    • ✅ Dependabot으로 액션 업데이트를 추적하는가?
    • ✅ 프로덕션 환경에 보호 규칙이 설정되어 있는가?
    • ✅ 장기 자격증명 대신 OIDC를 사용하는가?
    • ✅ 러너가 비권한 사용자로 실행되는가?
    • ✅ 러너 서버의 인바운드 포트가 닫혀 있는가?
    • ✅ 퍼블릭 리포지토리에 자체 호스팅 러너를 사용하지 않는가?
    GitHub Actions 자체 호스팅 러너 보안 강화 10가지 핵심 체크리스트 인포그래픽

    ▲ GitHub Actions 자체 호스팅 러너 보안 강화 요약 인포그래픽 — 10가지 핵심 체크리스트를 한눈에 확인하세요.


    마무리 — CI/CD 보안은 한 번이 아닙니다

    긴 글 읽어주셔서 감사합니다. 정리하자면, GitHub Actions 자체 호스팅 러너 보안의 핵심은 이렇습니다.

    1. 접근 최소화: 러너 범위를 필요한 리포지토리로만 제한
    2. 에페머럴 러너: 잡 실행 후 환경을 폐기해서 오염 방지
    3. 최소 권한: 워크플로우 권한을 필요한 것만 허용
    4. 공급망 공격 방어: 외부 액션을 SHA로 핀닝하고 Dependabot으로 관리
    5. 시크릿 보호: OIDC 활용, 환경 보호 규칙 설정
    6. 서버 하드닝: 비권한 사용자 실행, 네트워크 제한

    CI/CD 보안은 “한 번 설정하면 끝”이 아니거든요. 공급망 공격 기법은 계속 진화하고 있고, GitHub도 꾸준히 새로운 보안 기능을 추가하고 있어요. 저도 이 글 쓰면서 다시 한 번 제 홈랩 설정을 점검했는데, 고쳐야 할 부분이 몇 개 보이더라고요.

    💡 다음 글에서는 Kubernetes 기반의 Actions Runner Controller(ARC)를 활용한 에페머럴 러너 자동 스케일링 설정을 더 자세히 다룰 예정이에요. 홈랩에 k3s 올려서 직접 구성해본 경험을 공유해드릴 거니까 기대해주세요.

    궁금한 점이나 추가로 다뤄줬으면 하는 내용이 있으면 댓글로 남겨주세요. 제 경험이 여러분의 파이프라인을 조금 더 안전하게 만드는 데 도움이 됐으면 좋겠습니다. 🎉