13년차의 서버실

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

[태그:] 홈랩

  • [Cloud] GitHub Actions OIDC로 AWS 인증하기: 보안 강화 및 자격 증명 관리

    [Cloud] GitHub Actions OIDC로 AWS 인증하기: 보안 강화 및 자격 증명 관리

    GitHub Actions에서 AWS 인증, 아직도 IAM Access Key 쓰시나요?

    안녕하세요, 13년차 인프라 엔지니어이자 서버실 주인장입니다. 지난 몇 년간 수많은 CI/CD 파이프라인(Continuous Integration/Continuous Delivery Pipeline)을 구축하고 관리해왔는데, GitHub Actions와 AWS를 연동해서 사용하는 경우가 정말 많았거든요. 처음에는 저도 많은 분들처럼 AWS IAM(Identity and Access Management) 사용자의 Access Key와 Secret Key를 GitHub Secrets에 넣어 사용했습니다. 편리하긴 했죠. 근데 이게 뭔가 찜찜하더라고요.

    Access Key는 말 그대로 영구적인 자격 증명(Long-lived Credentials)이라 탈취 위험이 늘 존재하고, 정기적으로 Key를 교체(Rotation)해야 하는 관리 부담도 만만치 않았습니다. 만약 여러 레포지토리에서 같은 키를 쓴다면 더더욱 골치 아파지고요. 혹시 이런 경험 있으신가요? 저도 처음엔 이 방식밖에 없나 싶었는데, 다행히 더 안전하고 효율적인 방법이 등장했습니다. 바로 GitHub Actions OIDC(OpenID Connect)를 활용한 AWS 인증입니다! 오늘은 이 OIDC를 이용해 GitHub Actions에서 AWS에 안전하게 접근하는 방법을 제 경험을 바탕으로 자세히 알려드릴게요. CI/CD 파이프라인의 보안을 한 단계 끌어올리는 중요한 포인트가 될 겁니다.

    GitHub Actions OIDC를 이용한 AWS 인증의 전체적인 아키텍처 다이어그램입니다. GitHub Actions가 OIDC Provider 역할을 하고, AWS IAM이 이를 신뢰하여 임시 자격 증명을 발급하는 과정을 보여줍니다.

    OIDC(OpenID Connect)와 워크로드 아이덴티티(Workload Identity)가 뭔가요?

    본격적인 설정에 들어가기 전에, OIDC와 워크로드 아이덴티티라는 개념을 잠시 짚고 넘어가면 좋습니다. 복잡하게 들릴 수 있지만, 쉽게 말해볼게요.

    • OIDC (OpenID Connect, 오픈아이디 커넥트): OAuth 2.0 위에 구축된 인증(Authentication) 프로토콜입니다. 사용자(혹은 서비스)가 어떤 Identity Provider(신원 제공자)에게 “나 누구야”라고 인증받으면, Identity Provider는 ID Token이라는 걸 발급해줍니다. 이 토큰 안에는 “이 사람이 누구고, 어떤 방식으로 인증했는지” 같은 정보가 담겨있죠. AWS의 경우, GitHub Actions가 Identity Provider 역할을 하고, AWS IAM이 이 ID Token을 신뢰하는 Relying Party(의존 당사자)가 되는 겁니다.
    • 워크로드 아이덴티티 (Workload Identity): CI/CD 파이프라인의 워크로드(Workload)나 컨테이너처럼 사람이 아닌 시스템에 신원(Identity)을 부여하는 개념입니다. 기존의 Access Key 방식처럼 영구적인 자격 증명을 주지 않고, 필요할 때만 임시 자격 증명(Temporary Credentials)을 발급받아 사용하는 방식이에요. 이렇게 하면 자격 증명 탈취 위험을 최소화하고, 키 관리 부담을 없앨 수 있습니다.

    결론적으로, GitHub Actions OIDC를 사용하면 GitHub Actions 워크플로우가 직접 Access Key 없이 AWS에 “나 GitHub Actions의 이 레포지토리, 이 브랜치에서 실행된 애야!”라고 증명하고, AWS는 이 신원을 확인한 후 특정 권한을 가진 임시 자격 증명을 주는 방식이라고 이해하시면 됩니다. 진짜 편하고 안전하더라고요!

    GitHub Actions OIDC로 AWS 인증 설정 단계별 가이드

    자, 이제 실제로 GitHub Actions OIDC를 이용해 AWS에 인증하는 방법을 단계별로 따라해 볼까요? 제가 직접 해보니 몇 가지 포인트만 잘 기억하면 어렵지 않게 설정할 수 있었습니다.

    1단계: AWS IAM Identity Provider 생성하기

    가장 먼저 AWS IAM에서 GitHub Actions를 신뢰할 수 있는 OIDC Identity Provider로 등록해야 합니다. AWS 콘솔에서 진행하는 것이 가장 직관적입니다.

    1. AWS 콘솔에 로그인 후 IAM 서비스로 이동합니다.
    2. 왼쪽 탐색 메뉴에서 Access management (액세스 관리) > Identity Providers (자격 증명 공급자)를 클릭합니다.
    3. Add provider (공급자 추가) 버튼을 클릭합니다.
    4. Provider type (공급자 유형)으로 OpenID Connect를 선택합니다.
    5. Provider URL (공급자 URL)에 <code>https://token.actions.githubusercontent.com 을 입력합니다.
    6. Get thumbprint (지문 가져오기) 버튼을 클릭하여 서버 인증서 지문을 자동으로 가져옵니다.
    7. Audience (대상)에는 sts.amazonaws.com 을 입력하고 Add provider (공급자 추가)를 클릭하여 완료합니다.

    💡 팁: sts.amazonaws.com은 AWS Security Token Service의 기본 대상입니다. 다른 용도로 특정 서비스에만 OIDC를 사용한다면 해당 서비스의 Audience를 사용할 수도 있습니다.

    AWS IAM 콘솔에서 OIDC Identity Provider를 생성하는 화면입니다. Provider URL과 Audience를 정확히 입력하는 것이 중요합니다.

    2단계: AWS IAM Role 생성하기

    이제 이 OIDC Identity Provider를 통해 GitHub Actions가 임시 자격 증명을 요청할 수 있는 IAM Role을 생성해야 합니다. 이 역할에는 GitHub Actions가 AWS에서 수행할 작업에 대한 권한을 부여하게 됩니다.

    1. IAM 콘솔에서 Access management (액세스 관리) > Roles (역할)로 이동합니다.
    2. Create role (역할 생성) 버튼을 클릭합니다.
    3. Select type of trusted entity (신뢰할 수 있는 개체 유형 선택)에서 Custom trust policy (사용자 지정 신뢰 정책)를 선택합니다.
    4. 다음과 같이 신뢰 정책을 작성합니다. 여기서 StringEquals 조건이 핵심입니다!
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Federated": "arn:aws:iam::YOUR_AWS_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
          },
          "Action": "sts:AssumeRoleWithWebIdentity",
          "Condition": {
            "StringEquals": {
              "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
              "token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_ORG/YOUR_GITHUB_REPO:ref:refs/heads/main"
            }
          }
        }
      ]
    }

    ⚠️ 주의사항:

    • YOUR_AWS_ACCOUNT_ID: 여러분의 AWS 계정 ID로 교체해야 합니다.
    • YOUR_GITHUB_ORG/YOUR_GITHUB_REPO: GitHub 조직(Organization) 이름과 레포지토리(Repository) 이름으로 교체해야 합니다.
    • ref:refs/heads/main: 이 부분은 특정 브랜치(Branch)에서만 역할 가정을 허용하겠다는 의미입니다. *를 사용하여 모든 브랜치를 허용할 수도 있지만, 보안을 위해 특정 브랜치를 지정하는 것을 권장합니다. 예를 들어, 특정 태그(tag)에서만 허용하려면 ref:refs/tags/v*와 같이 설정할 수 있습니다.
    1. 정책 검토 후 다음 단계로 넘어갑니다.
    2. 이 역할에 필요한 Permissions (권한)을 부여합니다. 예를 들어 S3 버킷에 파일을 업로드해야 한다면 AmazonS3FullAccess 대신 필요한 최소한의 권한(Least Privilege)을 부여하는 것이 좋습니다. 저는 테스트를 위해 AmazonS3ReadOnlyAccess를 잠시 붙여봤습니다.
    3. 역할 이름을 지정하고(예: GitHubActionsOIDC-S3AccessRole) 역할을 생성합니다.

    3단계: GitHub Actions Workflow 설정하기

    마지막으로 GitHub Actions 워크플로우 YAML 파일에 OIDC를 통한 AWS 인증 로직을 추가합니다. configure-aws-credentials 액션을 사용하면 아주 쉽게 설정할 수 있습니다.

    name: Deploy to AWS S3 via OIDC
    
    on:
      push:
        branches:
          - main
    
    permissions:
      id-token: write # OIDC ID Token을 요청하기 위해 필수
      contents: read # 레포지토리 코드 읽기 권한
    
    jobs:
      deploy:
        runs-on: ubuntu-latest
        steps:
          - name: Checkout code
            uses: actions/checkout@v4
    
          - name: Configure AWS Credentials with OIDC
            uses: aws-actions/configure-aws-credentials@v4
            with:
              role-to-assume: arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/GitHubActionsOIDC-S3AccessRole # 2단계에서 생성한 역할 ARN
              aws-region: ap-northeast-2 # 여러분의 AWS 리전
              # role-session-name: optional-session-name # (선택 사항)
    
          - name: List S3 buckets (for verification)
            run: aws s3 ls

    ⚠️ 여기서 중요한 포인트! permissions: id-token: write 설정은 반드시 필요합니다. 이 권한이 없으면 GitHub Actions가 OIDC ID Token을 생성할 수 없어 AWS 인증이 실패합니다. 처음엔 이걸 몰라서 삽질 좀 했습니다 ㅎㅎ

    삽질 경험: “AccessDenied” 에러의 늪에서 벗어나기 ⚠️

    제가 처음 GitHub Actions OIDC를 설정하면서 가장 많이 겪었던 문제는 다름 아닌 “AccessDenied” 에러였습니다. 워크플로우 로그를 보면 An error occurred (AccessDenied) when calling the AssumeRoleWithWebIdentity operation: Not authorized to perform sts:AssumeRoleWithWebIdentity 이런 메시지가 뜨더라고요. 진짜 답답했죠.

    주요 원인은 대부분 IAM Role의 Trust Policy (신뢰 정책) 조건 문제였습니다. 제가 겪었던 실수와 해결 방법은 다음과 같습니다.

    • Audience 불일치: IAM Identity Provider를 생성할 때 Audience를 sts.amazonaws.com으로 설정했는데, IAM Role Trust Policy의 "token.actions.githubusercontent.com:aud" 조건에도 정확히 "sts.amazonaws.com"이 들어가야 합니다. 한 글자라도 다르면 인증이 실패합니다.
    • sub 조건 오류: 가장 흔한 실수입니다. "token.actions.githubusercontent.com:sub" 조건에 지정된 GitHub 레포지토리, 브랜치 정보가 정확해야 합니다. 예를 들어, repo:YOUR_GITHUB_ORG/YOUR_GITHUB_REPO:ref:refs/heads/main 에서 오타가 있거나, 워크플로우가 실행되는 브랜치와 다르면 에러가 발생합니다. 저는 처음에 main 브랜치로 설정해놓고 develop 브랜치에서 테스트하다가 한참을 헤맸습니다.
    • permissions: id-token: write 누락: GitHub Actions 워크플로우 자체에 OIDC 토큰을 생성할 권한이 없어서 생기는 문제입니다. 이 한 줄 때문에 몇 시간을 날린 적도 있어요. 꼭 확인하세요!
    • AWS 계정 ID 또는 역할 ARN 오타: 기본적인 실수지만, 의외로 자주 발생합니다. ARN을 복사 붙여넣기 할 때 다시 한번 확인하는 습관을 들이세요.

    문제가 발생하면 AWS CloudTrail 로그를 확인하는 것이 가장 좋습니다. AssumeRoleWithWebIdentity 호출이 실패한 이유가 자세히 기록되어 있으니 꼭 살펴보세요. 삽질 끝에 드디어 성공했을 때의 그 쾌감이란! 진짜 이 맛에 엔지니어 하는 것 같아요.

    드디어 성공! OIDC로 안전하게 AWS 리소스 접근하기 🎉

    위에 설명드린 대로 모든 설정을 마치고 GitHub Actions 워크플로우를 실행하면, 아래와 같이 성공적으로 AWS 리소스에 접근하는 모습을 볼 수 있습니다.

    GitHub Actions 워크플로우가 성공적으로 AWS CLI 명령어를 실행하여 S3 버킷 목록을 가져오는 로그입니다. OIDC 기반 인증이 정상적으로 작동했음을 보여줍니다.

    워크플로우 로그를 보면 aws s3 ls 명령어가 문제없이 실행되고, S3 버킷 목록이 출력되는 것을 확인할 수 있습니다. 이제 더 이상 GitHub Secrets에 Access Key를 저장할 필요가 없어졌습니다! 🎉

    이 방식의 가장 큰 장점은 바로 단기 자격 증명(Short-lived Credentials)이라는 점입니다. GitHub Actions 워크플로우가 실행될 때만 임시 자격 증명을 발급받아 사용하고, 워크플로우가 끝나면 만료되죠. 덕분에 키 탈취로 인한 보안 위험이 현저히 줄어들고, 키 교체 주기를 관리할 필요도 없어졌습니다. 관리적인 측면에서도 엄청난 이득이라고 생각해요.

    OIDC, CI/CD 보안의 새로운 표준이 되다

    오늘은 GitHub Actions OIDC를 이용해 AWS에 안전하게 인증하는 방법을 자세히 알아봤습니다. 제가 직접 경험하며 얻은 삽질 경험과 해결 과정도 솔직하게 공유해 드렸는데요. 이 방식은 단순히 편리함을 넘어 CI/CD 파이프라인의 보안을 근본적으로 강화하는 핵심 기술이라고 생각합니다.

    기존의 Access Key 방식과 OIDC를 활용한 AWS 인증 방식의 주요 특징을 비교한 표입니다. 보안성, 관리 용이성 등 여러 측면에서 OIDC의 장점을 한눈에 보여줍니다.

    핵심 장점을 다시 정리해볼까요?

    • 보안 강화: 영구적인 Access Key 없이 임시 자격 증명 사용으로 탈취 위험 최소화.
    • 관리 용이성: Access Key 생성, 배포, 교체 등의 관리 부담 해소.
    • 정교한 권한 제어: 특정 레포지토리, 특정 브랜치, 특정 태그에서만 AWS 리소스 접근 허용 가능.
    • 감사 추적 용이: CloudTrail을 통해 어떤 GitHub Actions 워크플로우가 어떤 역할로 AWS에 접근했는지 명확하게 추적 가능.

    저도 홈랩에서 다양한 CI/CD 환경을 구성하면서 OIDC의 강력함을 몸소 체험하고 있습니다. 앞으로는 OIDC 기반의 워크로드 아이덴티티가 CI/CD 보안의 표준으로 자리매김할 것이라고 확신합니다. 혹시 아직 Access Key를 사용하고 계시다면, 이번 기회에 OIDC로 전환해 보시는 것을 강력히 추천합니다! 처음엔 조금 낯설 수 있지만, 한번 설정해두면 정말 든든하거든요.

    다음 글에서는 AWS S3에 정적 웹사이트를 배포하는 과정을 GitHub Actions OIDC와 함께 자동화하는 방법에 대해 다뤄볼까 합니다. 기대해주세요! 그럼 다음 글에서 만나요! 👋

  • [HomeLabs] UPS와 홈서버 연동: Proxmox/TrueNAS 자동 종료 설정 완벽 가이드

    [HomeLabs] UPS와 홈서버 연동: Proxmox/TrueNAS 자동 종료 설정 완벽 가이드

    UPS와 홈서버 연동: Proxmox/TrueNAS 자동 종료 설정 완벽 가이드

    안녕하세요, 13년차의 서버실 주인장입니다. 홈랩을 운영하면서 가장 신경 쓰이는 부분이 뭘까요? 저는 주저 없이 전원 문제라고 말할 수 있어요. 특히 갑작스러운 정전은 애지중지 키워온 홈서버의 하드웨어뿐만 아니라, 그 안에 담긴 소중한 데이터까지 한순간에 날려버릴 수 있는 무서운 재앙이거든요.

    제가 처음 홈랩을 구성하고 몇 달 뒤였을 겁니다. 한창 드라마를 보는데 갑자기 집 전체가 퍽! 하고 암전되더라고요. 순간 ‘아, 망했다!’ 싶었죠. 다행히 서버는 무사했지만, 그날 이후로 UPS(무정전 전원 장치)의 필요성을 뼈저리게 느꼈습니다. 단순히 전원을 공급해주는 것을 넘어, 정전 시 서버를 안전하게 종료시키는 자동화가 정말 중요하다는 걸 깨달았죠.

    오늘은 저처럼 홈랩을 운영하시는 분들을 위해, UPS와 홈서버(특히 Proxmox VE와 TrueNAS)를 연동해서 정전 시 자동으로 시스템이 종료되도록 설정하는 방법을 차근차근 알려드릴게요. 13년 동안 이런저런 삽질을 하면서 터득한 경험을 바탕으로, 여러분은 좀 더 쉽게 성공하시길 바랍니다! 💡

    홈랩 UPS 연동 아키텍처: 정전 시 서버들을 안전하게 지키는 핵심 구성입니다.

    UPS와 NUT(Network UPS Tools)는 뭐 하는 건가요?

    먼저 핵심 개념부터 짚고 넘어갈게요. ‘UPS’는 다들 아실 겁니다. 쉽게 말해 보조 배터리 같은 역할을 하면서, 정전이 되면 일정 시간 동안 서버에 전원을 공급해주는 장치죠. 하지만 UPS의 진짜 가치는 단순히 전원을 유지하는 것을 넘어, 서버에게 “야, 지금 전기가 나갔어! 얼른 마무리하고 꺼져야 해!”라고 알려줄 수 있을 때 빛을 발합니다.

    이때 필요한 게 바로 NUT(Network UPS Tools)예요. NUT는 UPS와 서버 간의 통신을 담당하는 오픈소스 소프트웨어 스위트(Software Suite)입니다. UPS의 상태(배터리 잔량, 전원 상태 등)를 모니터링하고, 특정 조건(예: 배터리 잔량 20% 미만)이 되면 연결된 서버들에게 종료 신호를 보내는 역할을 하죠.

    • UPS 서버 (Server): 물리적인 UPS 장치에 직접 연결되어 UPS 상태를 읽어오는 역할을 해요. 보통 USB 케이블로 연결하죠. 이 서버에 upsd (UPS Daemon)가 실행되면서 다른 클라이언트들에게 UPS 정보를 제공합니다.
    • UPS 클라이언트 (Client): UPS 서버로부터 UPS 상태 정보를 받아, 특정 조건이 되면 스스로 종료하거나 미리 설정된 작업을 수행합니다. Proxmox VE나 TrueNAS 같은 홈서버들이 여기에 해당해요. upsmon (UPS Monitor)이 이 역할을 담당합니다.

    저는 보통 라즈베리 파이 같은 저전력 장치나, 최소한의 리소스를 할당한 가상 머신에 NUT 서버를 구성합니다. 아무래도 UPS가 서버 전체를 위한 장치이니, 그 정보를 관리하는 서버는 가급적 안정적이고 전력을 덜 먹는 게 좋더라고요.

    실전 구현: NUT 서버와 클라이언트 설정

    자, 이제 실전입니다. 저는 제가 직접 구성한 환경을 기준으로 설명해 드릴게요. UPS는 APC BR1500MS 같은 모델을 많이 쓰시는데, USB로 연결되는 대부분의 UPS는 NUT와 호환되니 크게 걱정하지 않으셔도 돼요. 제가 사용했던 UPS도 USB로 연결되는 모델이었거든요.

    1. NUT 서버 설정 (Debian/Ubuntu 기반 VM 또는 Raspberry Pi)

    먼저, UPS에 USB로 직접 연결될 NUT 서버를 설정해봅시다. 저는 Proxmox 위에 데비안(Debian) 가상 머신을 하나 띄워서 사용했어요.

    패키지 설치:

    sudo apt update
    sudo apt install nut

    /etc/nut/ups.conf 설정:
    이 파일은 UPS 장치의 종류와 연결 방식을 NUT에게 알려줍니다. UPS 모델에 따라 드라이버(driver)가 달라질 수 있으니, NUT 공식 문서를 참고해서 맞는 드라이버를 찾아주세요. 저는 usbhid-ups 드라이버를 사용했어요.

    [myups]
      driver = usbhid-ups
      port = auto
      desc = "My HomeLab UPS"
      # mincharge = 80  # 배터리 최소 충전량 (예: 80% 미만이면 경고)
      # override.battery.charge.low = 20 # 배터리 잔량 20% 미만 시 저전압 경고

    /etc/nut/nut.conf 설정:
    NUT의 동작 모드를 설정합니다. 여기서는 STANDALONE 대신 SERVER로 설정해야 다른 서버들이 이 UPS 정보를 가져갈 수 있어요.

    MODE=SERVER

    /etc/nut/upsd.users 설정:
    클라이언트들이 UPS 정보를 모니터링할 때 사용할 사용자 계정을 정의합니다. 보안을 위해 강력한 비밀번호를 사용하는 것이 좋겠죠.

    [upsmon]
      password = YourStrongPasswordHere
      actions = SET
      instcmds = ALL
      upsmon master

    /etc/nut/upsd.conf 설정:
    NUT 데몬이 어떤 IP 주소와 포트(기본 3493)에서 클라이언트의 연결을 받을지 설정합니다. 저는 홈랩 내부에서만 사용할 것이므로 내부 IP를 바인딩했어요.

    LISTEN 0.0.0.0 3493 # 모든 인터페이스에서 수신 (혹은 특정 내부 IP)

    NUT 서비스 재시작 및 확인:

    sudo systemctl restart nut-server nut-client
    sudo systemctl enable nut-server nut-client
    sudo upsc myups@localhost # UPS 정보 확인. 'myups'는 ups.conf에 설정한 이름입니다.

    upsc myups@localhost 명령어를 실행했을 때, UPS의 다양한 정보(배터리 잔량, 전압 등)가 잘 출력된다면 NUT 서버 설정은 성공입니다! 🎉

    NUT 서버 설정의 핵심, ups.conf 파일입니다. UPS 모델에 맞는 드라이버를 찾는 것이 정말 중요해요.

    2. Proxmox VE 클라이언트 설정

    이제 Proxmox VE 서버가 NUT 서버로부터 UPS 정보를 받아 정전 시 자동으로 종료되도록 설정해볼까요? Proxmox는 Debian 기반이라 NUT 클라이언트 설정이 비교적 간단해요.

    패키지 설치:

    apt update
    apt install nut

    /etc/nut/nut.conf 설정:
    Proxmox는 NUT 서버가 아닌 클라이언트 역할을 하므로 MODE=NETCLIENT로 설정합니다.

    MODE=NETCLIENT

    /etc/nut/upsmon.conf 설정:
    이 파일에서 어떤 UPS 서버를 모니터링할지, 그리고 어떤 조건에서 종료할지 정의합니다. MONITOR 라인에 NUT 서버의 IP 주소, UPS 이름, 사용자 이름, 비밀번호를 입력하세요.

    MONITOR [email protected] 1 upsmon YourStrongPasswordHere master
    # (192.168.1.100은 NUT 서버의 IP 주소로 바꿔주세요)
    
    MINWARNTIME 300   # UPS 경고 발생 후 최소 5분 대기
    FINALDELAY 5      # 시스템 종료까지 추가 대기 시간 (초)
    SHUTDOWNCMD "/sbin/shutdown -h now" # 종료 명령
    NOTIFYCMD "/usr/sbin/upssched-cmd" # 알림 스크립트 (선택 사항)
    
    NOTIFYFLAG ONLINE SYSLOG+WALL
    NOTIFYFLAG ONBATT SYSLOG+WALL
    NOTIFYFLAG LOWBATT SYSLOG+WALL
    NOTIFYFLAG FSD SYSLOG+WALL
    
    # Proxmox의 경우, 가상머신 및 컨테이너를 먼저 종료해야 합니다.
    # 저는 아래와 같이 별도 스크립트를 만들어서 사용했어요.
    # /etc/nut/upsmon.conf 에 직접 넣기보다는, SHUTDOWNCMD가 실행할 스크립트 안에 넣는 것이 좋습니다.
    # (예시: /etc/nut/ups_shutdown.sh)
    # SCRIPT: /etc/nut/ups_shutdown.sh
    # (스크립트 예시는 뒤에서 설명합니다)

    Proxmox 특화: 가상 머신/컨테이너(VM/CT) 종료 스크립트
    Proxmox는 단순히 shutdown -h now만 실행하면 호스트만 꺼지고 VM/CT는 강제 종료될 수 있어요. 이를 방지하려면 VM/CT를 먼저 안전하게 종료하는 스크립트를 작성하여 SHUTDOWNCMD에 연결해야 합니다. 저는 다음과 같은 스크립트를 사용했습니다.

    #!/bin/bash
    
    logger -t ups_shutdown "UPS shutting down Proxmox and VMs/CTs..."
    
    # 모든 VM 및 CT 목록 가져오기
    vms=$(qm list | awk 'NR>1 {print $1}')
    cts=$(pct list | awk 'NR>1 {print $1}')
    
    # VM 종료
    for vmid in $vms
    do
      status=$(qm status $vmid | awk '{print $2}')
      if [ "$status" = "running" ]; then
        logger -t ups_shutdown "Shutting down VM $vmid..."
        qm shutdown $vmid --timeout 30
        sleep 5
        if [ "$(qm status $vmid | awk '{print $2}')" = "running" ]; then
          logger -t ups_shutdown "VM $vmid did not shut down gracefully, stopping..."
          qm stop $vmid
        fi
      fi
    done
    
    # CT 종료
    for ctid in $cts
    do
      status=$(pct status $ctid | awk '{print $2}')
      if [ "$status" = "running" ]; then
        logger -t ups_shutdown "Shutting down CT $ctid..."
        pct shutdown $ctid --timeout 30
        sleep 5
        if [ "$(pct status $ctid | awk '{print $2}')" = "running" ]; then
          logger -t ups_shutdown "CT $ctid did not shut down gracefully, stopping..."
          pct stop $ctid
        fi
      fi
    done
    
    logger -t ups_shutdown "All VMs/CTs processed. Shutting down Proxmox host."
    sleep 10 # VM/CT 종료 대기 시간을 충분히 줍니다.
    /sbin/shutdown -h now

    이 스크립트를 /etc/nut/ups_shutdown.sh 로 저장하고 실행 권한을 부여한 뒤,

    sudo chmod +x /etc/nut/ups_shutdown.sh

    /etc/nut/upsmon.conf 파일의 SHUTDOWNCMD를 다음과 같이 수정하세요.

    SHUTDOWNCMD "/etc/nut/ups_shutdown.sh"

    NUT 클라이언트 서비스 재시작 및 확인:

    systemctl restart nut-client
    systemctl enable nut-client
    upsc [email protected] # NUT 서버의 UPS 정보 확인

    3. TrueNAS 클라이언트 설정

    TrueNAS는 NUT 클라이언트 기능이 웹 인터페이스에 통합되어 있어 설정이 훨씬 간편합니다. 제가 써보니 이 부분이 정말 편하더라고요!

    1. TrueNAS 웹 UI에 접속하세요.
    2. 좌측 메뉴에서 Services (서비스)를 클릭합니다.
    3. 서비스 목록에서 UPS를 찾아 Configure (설정) 버튼을 클릭하세요.
    4. 설정 창에서 다음 정보를 입력합니다:
      • UPS Type (UPS 유형): Slave (클라이언트 역할을 하므로)
      • Remote Host (원격 호스트): NUT 서버의 IP 주소 (예: 192.168.1.100)
      • Remote Port (원격 포트): 3493 (기본값)
      • Identifier (식별자): NUT 서버의 ups.conf에 설정한 UPS 이름 (예: myups)
      • Monitor User (모니터 사용자): upsmon (upsd.users에 설정한 사용자)
      • Monitor Password (모니터 비밀번호): YourStrongPasswordHere (upsd.users에 설정한 비밀번호)
      • Shutdown Mode (종료 모드): UPS reaches low battery (UPS 배터리가 부족할 때)
      • Shutdown Timer (종료 타이머): 120 (배터리 부족 감지 후 120초 뒤 종료)
    5. Save (저장) 버튼을 클릭하세요.
    6. 서비스 목록으로 돌아가서 UPS 서비스를 Enabled (활성화)로 설정하고 Start (시작) 버튼을 클릭합니다.

    TrueNAS 로그를 확인하여 UPS 서비스가 정상적으로 시작되었는지, NUT 서버와 통신하는지 확인해 보세요. /var/log/messages나 웹 UI의 시스템 로그에서 관련 메시지를 찾을 수 있어요.

    TrueNAS의 UPS 설정 화면입니다. 웹 UI 덕분에 직관적으로 설정할 수 있어요.

    ⚠️ 주의사항 및 트러블슈팅 (제 삽질 경험 공유)

    제가 이 설정을 하면서 겪었던 몇 가지 삽질 경험과 주의사항을 알려드릴게요. 여러분은 저처럼 고생하지 마시길!

    1. 방화벽 문제: NUT 서버와 클라이언트 간에 3493번 포트 통신이 제대로 되는지 꼭 확인하세요. 특히 NUT 서버로 설정한 VM이나 물리 장치에 ufw나 firewalld 같은 방화벽이 있다면 3493번 포트를 열어줘야 해요. sudo ufw allow 3493/tcp 같은 명령어로 열 수 있습니다. 저는 처음에 이걸 놓쳐서 ‘왜 연결이 안 되지?’ 하고 한참을 헤맸습니다. ㅠㅠ
    2. USB 드라이버 문제: UPS가 USB로 NUT 서버에 제대로 인식되지 않는 경우가 간혹 있어요. lsusb 명령어로 UPS 장치가 목록에 뜨는지 확인하고, dmesg | grep -i usb 명령어로 커널 로그를 확인해서 드라이버 로딩에 문제가 없는지 봐야 합니다. 간혹 특정 UPS 모델은 추가적인 드라이버나 커널 모듈이 필요할 수 있거든요.
    3. ups.conf 드라이버 오설정: 가장 흔한 실수 중 하나예요. UPS 모델에 맞는 정확한 드라이버를 사용해야 합니다. nut-drivers 패키지 설치 후 man ups.conf나 NUT 공식 문서를 참고하는 게 가장 정확합니다.
    4. 종료 스크립트 테스트: 실제 정전 상황을 흉내 내기 어렵다면, 강제로 lowbatt 신호를 보내는 방식으로 테스트할 수 있어요. sudo upsmon -c fsd (Force Shutdown) 같은 명령어를 사용하거나, upsmon.conf에서 SHUTDOWNCMD를 테스트용 스크립트로 바꿔서 실행해 보는 것도 좋은 방법입니다. 절대 운영 중인 서버에서 바로 테스트하지 마세요! 중요한 데이터가 날아갈 수 있습니다. 테스트는 항상 충분히 백업된 환경이나 테스트용 서버에서 진행하시고, 스크립트 실행 권한(chmod +x)도 잊지 마세요!
    5. Proxmox VM/CT 종료 순서: 위에서 설명했듯이 Proxmox는 호스트만 종료하면 VM/CT가 강제 종료돼요. 반드시 qm shutdown, pct shutdown 명령어를 사용하여 순차적으로 종료하는 스크립트를 사용해야 합니다.

    검증 및 결과: 이제 안심하고 홈랩을!

    모든 설정이 끝났다면, 이제 정말 잘 작동하는지 확인해야겠죠? 저는 조심스럽게 UPS의 전원 코드를 뽑아서 실제 정전 상황을 시뮬레이션해봤습니다. (물론 중요한 작업은 모두 백업해두고, 아주 짧게 시도했어요.)

    결과는? 🎉 대성공이었습니다!

    UPS가 배터리 모드로 전환되고, 설정해둔 MINWARNTIME이 지나자 Proxmox와 TrueNAS가 순차적으로 종료되기 시작했어요. Proxmox의 경우, 제가 만들어둔 스크립트 덕분에 VM과 CT들이 먼저 안전하게 꺼진 후 호스트가 종료되는 것을 로그로 확인할 수 있었습니다. TrueNAS도 깔끔하게 종료되었고요.

    로그 확인은 journalctl -u nut-client.service (Proxmox/Debian)나 /var/log/messages (TrueNAS)를 통해 할 수 있습니다. 여기에 UPS 상태 변화와 종료 명령이 정상적으로 기록되어 있다면 안심해도 좋아요.

    시스템 로그에서 확인하는 자동 종료 과정. 이 메시지를 보면 정말 뿌듯하더라고요!

    마무리하며: 홈랩의 든든한 보험, UPS 연동

    오늘은 홈랩의 안정성을 한 단계 끌어올리는 UPS 홈서버 연동, 특히 Proxmox UPS와 TrueNAS UPS 자동 종료 설정에 대해 알아봤습니다. 처음에는 NUT 설정이 조금 복잡하게 느껴질 수도 있지만, 한 번 제대로 설정해두면 갑작스러운 정전에도 소중한 장비와 데이터를 안전하게 지킬 수 있어요. 이건 마치 홈랩을 위한 든든한 보험 같은 존재랄까요?

    저도 처음엔 ‘이거까지 해야 하나?’ 싶었는데, 한번 정전을 겪고 나니 이제는 홈랩의 필수 요소라고 생각합니다. 여러분도 이 가이드를 통해 성공적으로 UPS 연동을 마치시고, 마음 편히 홈랩을 즐기시길 바랍니다. 다음번에는 또 다른 흥미로운 홈랩 이야기로 찾아올게요! 그때까지 즐거운 삽질(?) 되세요! 😉

    UPS 연동의 핵심 가치: 안전한 홈랩, 데이터 보호, 그리고 마음의 평화!

  • [3D Printer] 3D 프린터 필라멘트 선택 가이드: PLA, ABS, PETG 비교

    [3D Printer] 3D 프린터 필라멘트 선택 가이드: PLA, ABS, PETG 비교

    3D 프린터 필라멘트 선택 가이드: PLA, ABS, PETG 비교

    왜 필라멘트 선택이 중요할까요?

    안녕하세요, 13년차의 서버실 운영자입니다. 오늘은 좀 색다른 주제로 찾아왔는데요, 바로 3D 프린터 필라멘트 이야기입니다. 제가 홈랩을 운영하면서 이것저것 만들다 보니 3D 프린터를 정말 유용하게 쓰고 있거든요. 그런데 처음 3D 프린터를 시작하시는 분들이 가장 많이 겪는 고민 중 하나가 바로 어떤 3D 프린터 필라멘트를 써야 할까? 하는 겁니다. 종류도 너무 많고, 각각 특징도 달라서 저도 처음엔 삽질 좀 했습니다. 이 글에서는 가장 흔하게 사용되는 PLA 필라멘트, ABS 필라멘트, 그리고 PETG 필라멘트를 중심으로 각 필라멘트의 특징과 저의 실제 경험을 바탕으로 한 선택 가이드를 알려드릴게요! 💡 여러분의 3D 프린팅 여정에 멘토가 되어 드릴 준비가 되어 있습니다!

    3D 프린터 필라멘트, 이게 다 뭔가요?

    자, 그럼 3D 프린터 필라멘트가 정확히 뭘까요? 쉽게 말해, 3D 프린터가 3차원 물체를 만들 때 쓰는 재료를 말해요. 국수 가닥처럼 생긴 이 플라스틱 줄을 프린터 헤드가 녹여서 한 층씩 쌓아 올리는 방식이죠. 마치 그림을 그릴 때 물감이 필요한 것처럼, 3D 프린팅에서는 이 필라멘트가 바로 ‘물감’ 역할을 하는 셈이에요. 🎨 종류에 따라 강도, 유연성, 내열성, 출력 난이도 등 다양한 특성을 가지고 있어서, 만들고자 하는 결과물의 용도에 맞춰 적절한 필라멘트 종류를 선택하는 것이 정말 중요해요. 제가 처음엔 아무거나 막 썼다가 원하는 결과물이 안 나와서 고생 좀 했거든요.

    다양한 색상과 재질의 3D 프린터 필라멘트들이 롤 형태로 진열되어 있고, 한쪽에서는 3D 프린터가 필라멘트를 이용해 출력물을 만들고 있는 모습을 보여줍니다.

    PLA (PolyLactic Acid) 필라멘트: 초보자의 친구

    가장 먼저 소개해드릴 PLA 필라멘트 (PolyLactic Acid)는 3D 프린팅 입문자분들에게 제가 강력히 추천하는 재료입니다.

    • 장점:
      • 출력 난이도 (Ease of Printing): 가장 쉬운 편입니다. 변형(Warping)이 적고, 냄새도 거의 없어서 집 안에서도 부담 없이 쓸 수 있어요. 저도 처음엔 PLA로 감을 잡았죠.
      • 친환경적 (Eco-friendly): 옥수수 전분 같은 식물성 재료로 만들어져 생분해(Biodegradable)가 가능해요. 환경을 생각한다면 좋은 선택이죠.
      • 색상 다양성 (Color Variety): 시중에 정말 다양한 색상과 특수 효과(ex: 실크, 메탈릭) 필라멘트가 많아서 디자인적인 요소를 중요하게 생각하는 분들께 좋습니다.
    • 단점:
      • 내열성 (Heat Resistance): 열에 약해서 뜨거운 곳에 두면 쉽게 변형돼요. 여름철 차 안에 두면 흐물흐물해지는 경험을 할 수도 있죠 ⚠️.
      • 강도 및 내구성 (Strength & Durability): 다른 필라멘트에 비해 충격에 약하고 잘 부러지는 편입니다.
    • 활용: 피규어, 장난감, 시제품(Prototype), 교육용 모델 등.

    제가 홈랩에서 간단한 브라켓이나 테스트용 부품을 만들 때 주로 PLA를 써요. 출력 성공률이 높아서 마음이 편하거든요.

    ABS (Acrylonitrile Butadiene Styrene) 필라멘트: 견고함의 대명사

    다음은 ABS 필라멘트 (Acrylonitrile Butadiene Styrene)입니다. 레고 블록 아시죠? 바로 그 레고가 ABS로 만들어집니다. 내구성이 필요한 출력물에 주로 사용되죠.

    • 장점:
      • 강도 및 내구성 (Strength & Durability): PLA보다 훨씬 강하고 충격에 잘 견따니다. 기능성 부품이나 오래 사용해야 하는 제품에 적합해요.
      • 내열성 (Heat Resistance): PLA보다 높은 온도에서도 형태를 유지합니다. 뜨거운 환경에서 사용할 부품이라면 ABS가 좋은 선택이에요.
      • 후가공 용이성 (Post-processing): 아세톤 흄 스무딩(Acetone Fume Smoothing) 같은 후가공을 통해 표면을 매끄럽게 만들 수 있습니다.
    • 단점:
      • 출력 난이도 (Difficulty of Printing): PLA에 비해 출력이 까다로워요. 수축(Shrinkage) 현상이 심해서 변형(Warping)이 잘 일어나고, 챔버(Chamber)가 있는 프린터나 베드 히팅(Heated Bed)이 필수예요. 저도 처음 ABS 출력하다가 출력물이 베드에서 떨어져 나가서 여러 번 좌절했습니다 😭.
      • 냄새 및 유해 증기 (Fumes): 출력 시 특유의 플라스틱 냄새가 강하게 나고, 미세 플라스틱 입자나 유해 증기가 발생할 수 있어 환기가 매우 중요합니다.
    • 활용: 자동차 부품, 공구 손잡이, 기능성 프로토타입 등.

    제가 직접 써보니까, ABS는 확실히 튼튼하지만, 출력 환경을 잘 맞춰줘야 한다는 걸 깨달았어요. 환기 시설 없는 곳에서는 절대 사용하지 마세요!

    ABS 필라멘트로 제작된 기능성 부품들이 놓여 있고, 그 옆에서는 챔버가 있는 3D 프린터가 ABS 필라멘트를 사용하여 부품을 출력하고 있는 모습이 보입니다.

    PETG (Polyethylene Terephthalate Glycol) 필라멘트: 두 마리 토끼를 잡다

    마지막으로 소개해드릴 PETG 필라멘트 (Polyethylene Terephthalate Glycol)는 PLA와 ABS의 장점을 적절히 섞어 놓은 하이브리드 같은 존재입니다.

    • 장점:
      • 강도 및 유연성 (Strength & Flexibility): ABS만큼 강하면서도 PLA보다 유연성이 좋아요. 잘 깨지지 않으면서도 어느 정도 휘어지는 특성이 있죠.
      • 내열성 및 내화학성 (Heat & Chemical Resistance): PLA보다 내열성이 좋고, 산이나 알칼리 같은 화학 물질에도 비교적 강합니다.
      • 출력 난이도 (Ease of Printing): ABS보다는 훨씬 쉽고, PLA보다는 약간 까다로운 정도예요. 베드 히팅은 권장되지만, 챔버 없이도 비교적 괜찮은 결과물을 얻을 수 있습니다.
      • 식품 안전성 (Food Safety): 일부 PETG 필라멘트는 식품 용기로도 사용되는 PET 재질과 비슷해서, 식품과 접촉하는 용도로도 고려해볼 수 있어요. (물론 직접적인 식품 용도로는 추가적인 확인이 필요합니다.)
    • 단점:
      • 끈적임 (Stringing): 출력 시 거미줄처럼 실오라기가 생기는 스트링징(Stringing) 현상이 자주 발생할 수 있어요. 리트랙션(Retraction) 설정 조절이 중요해요.
      • 표면 마감 (Surface Finish): ABS나 PLA에 비해 표면이 다소 거칠게 느껴질 수 있습니다.
    • 활용: 물병, 기능성 부품, 야외용 부품, 기계 부품 등.

    제가 실제로 PETG를 써보니까, 확실히 범용성이 높더라고요. 튼튼하면서도 출력 부담이 덜해서, PLA에서 한 단계 업그레이드하고 싶은 분들께 딱입니다.

    PETG 필라멘트로 제작된 유연한 기계 부품이 살짝 휘어져 있는 모습과, 3D 프린터의 노즐에서 녹은 PETG 필라멘트가 정확하게 쌓이는 과정이 클로즈업되어 보입니다.

    필라멘트 종류별 비교: 한눈에 보는 선택 가이드

    자, 그럼 지금까지 살펴본 세 가지 3D 프린터 필라멘트를 한눈에 비교해볼까요? 제가 직접 사용하면서 느꼈던 점들을 바탕으로 표로 정리해봤습니다. 🤓

    특징 PLA (PolyLactic Acid) ABS (Acrylonitrile Butadiene Styrene) PETG (Polyethylene Terephthalate Glycol)
    출력 난이도 쉬움 (초보자 추천) ✅ 어려움 (전문가용) ⚠️ 중간 (베드 히팅 권장)
    강도 / 내구성 낮음 (잘 부러짐) 매우 높음 (견고함) 💪 높음 (유연성 좋음)
    내열성 낮음 (뜨거운 곳에 약함) 높음 (고온에 강함) 중간 (PLA보다 좋음)
    유연성 낮음 (단단함) 낮음 (단단함) 높음 (잘 휘어짐)
    냄새 / 유해 증기 거의 없음 (약간 달콤한 향) 강함 (환기 필수) 💨 약함 (PLA와 유사)
    변형 (Warping) 적음 심함 (수축률 높음) 적음 (ABS보다 적음)
    후가공 사포질, 도색 사포질, 아세톤 흄 스무딩 사포질, 도색
    주요 용도 피규어, 장난감, 시제품 기능성 부품, 공구, 레고 기계 부품, 용기, 야외용품
    가격대 저렴한 편 중간 정도 중간 정도

    PLA, ABS, PETG 필라멘트의 주요 특징(강도, 내열성, 출력 난이도 등)을 아이콘과 그래프로 시각화하여 한눈에 비교할 수 있는 인포그래픽입니다.

    마무리: 나만의 최적 필라멘트를 찾아서

    어떠신가요? 3D 프린터 필라멘트 선택에 대한 감이 좀 잡히셨나요? 결국 어떤 필라멘트가 ‘최고’라고 단정할 수는 없어요. 중요한 건 여러분이 만들고자 하는 출력물의 용도와 3D 프린터의 성능, 그리고 본인의 숙련도에 맞춰 최적의 필라멘트 종류를 선택하는 것입니다.

    • 초보자라면 안전하게 PLA 필라멘트로 시작해서 3D 프린팅의 재미를 느껴보세요.
    • 더 강하고 견고한 출력물이 필요하고 출력 환경이 갖춰져 있다면 ABS 필라멘트에 도전해보는 것도 좋습니다.
    • PLA의 쉬운 출력성과 ABS의 강도를 동시에 원한다면 PETG 필라멘트가 아주 좋은 대안이 될 거예요.

    저도 처음엔 무작정 비싼 필라멘트가 좋겠지 하고 샀다가 낭패를 본 적이 많습니다. 😅 하지만 여러 번의 실패(삽질이라고 하죠!)를 통해 각 재료의 특성을 이해하고 나니, 이제는 어떤 출력물이든 자신 있게 만들 수 있게 됐어요.
    이 글이 여러분의 3D 프린팅 생활에 작은 도움이라도 되었기를 바랍니다. 궁금한 점이 있다면 언제든지 댓글로 남겨주세요! 다음번에는 각 필라멘트별 최적 출력 설정 팁에 대해 다뤄볼까 합니다. 기대해주세요! 🎉

  • [AI] LLM 파인튜닝 실전 가이드: LoRA, QLoRA로 커스텀 모델 만들기

    [AI] LLM 파인튜닝 실전 가이드: LoRA, QLoRA로 커스텀 모델 만들기

    LLM 파인튜닝 실전 가이드: LoRA, QLoRA로 커스텀 모델 만들기

    안녕하세요, 13년차 인프라 엔지니어 ‘서버실’입니다. 요즘 LLM(Large Language Model)의 세상이 정말 빠르게 변하고 있죠? ChatGPT 같은 거대 모델들을 보면서, ‘아, 나도 우리 회사 데이터로, 혹은 나만의 특화된 지식으로 LLM을 만들 수 없을까?’ 하는 생각, 혹시 해보셨나요?

    사실 저도 이런 꿈을 꾸다가 거대한 모델을 통째로 학습시키려면 엄청난 GPU 자원이 필요하다는 현실의 벽에 부딪혔어요. 엔비디아(NVIDIA) H100 같은 최신 GPU는 가격도 만만치 않고요. 게다가 학습 시간도 정말 어마어마하더라고요. 홈랩에서 저만의 서버를 돌리는 저 같은 사람에게는 엄두도 못 낼 일이었습니다. 😅

    하지만 희망은 있더라고요! 바로 PEFT(Parameter-Efficient Fine-Tuning, 파라미터 효율적 파인튜닝) 기법 덕분입니다. 특히 LoRA(Low-Rank Adaptation)와 이를 더 발전시킨 QLoRA(Quantized LoRA)는 제한된 자원으로도 LLM 파인튜닝을 가능하게 해주는 정말 혁신적인 방법이거든요.

    오늘은 제가 직접 여러 모델을 파인튜닝하며 겪었던 삽질 경험과 함께, LoRA와 QLoRA를 활용해서 나만의 커스텀 LLM을 만드는 실전 가이드를 여러분께 공유해드릴게요. 자, 그럼 함께 시작해볼까요?

    LLM 파인튜닝의 핵심, LoRA와 QLoRA의 기본 원리를 한눈에 볼 수 있는 다이어그램입니다.

    핵심 개념 설명: PEFT, LoRA, QLoRA 이해하기

    본격적인 실전에 앞서, 핵심 개념들을 짚고 넘어가는 게 중요해요. 제가 처음엔 이 용어들이 좀 헷갈렸거든요.

    • LLM (Large Language Model, 거대 언어 모델): 방대한 텍스트 데이터를 학습하여 사람의 언어를 이해하고 생성하는 능력을 가진 AI 모델입니다. 예를 들면 GPT-3, Llama 2 같은 모델들이죠.
    • Fine-tuning (파인튜닝): 이미 학습된 거대 모델(Pre-trained Model)을 특정 작업이나 데이터셋에 맞게 추가로 학습시키는 과정입니다. 예를 들어, 의료 질문 답변에 특화된 모델을 만들고 싶다면, 의료 데이터로 파인튜닝하는 식이죠.
    • PEFT (Parameter-Efficient Fine-Tuning, 파라미터 효율적 파인튜닝): 이 녀석이 바로 우리의 구세주입니다! 기존의 파인튜닝은 모델 전체의 모든 파라미터를 업데이트해야 해서 엄청난 자원이 필요했어요. 하지만 PEFT는 모델의 아주 작은 부분(소수의 파라미터)만 학습시키거나, 기존 모델에 작은 모듈을 추가해서 학습 효율을 극대화하는 기법들을 총칭합니다. 덕분에 적은 GPU 메모리로도 파인튜닝이 가능해지는 거죠.
    • LoRA (Low-Rank Adaptation, 저랭크 적응): PEFT의 대표적인 방법 중 하나에요. 쉽게 말해, 거대한 LLM의 가중치(weights)를 직접 수정하는 대신, 원본 가중치 옆에 아주 작은 두 개의 행렬(low-rank matrices)을 추가해서 학습시키는 방식입니다. 이 작은 행렬들만 학습시키고, 원본 모델의 가중치는 그대로 두는 거죠. 이렇게 하면 학습해야 할 파라미터 수가 극적으로 줄어들고, 학습된 어댑터(adapter)의 크기도 매우 작아서 저장 및 로드도 훨씬 수월해요.
    • QLoRA (Quantized LoRA, 양자화된 LoRA): LoRA를 한 단계 더 발전시킨 기법이에요. 기존 LoRA는 원본 모델의 가중치를 16비트 부동소수점(FP16)으로 불러와야 했는데, QLoRA는 이를 4비트 정수(4-bit quantization)로 양자화(quantization)해서 불러옵니다. 이렇게 되면 모델을 로드하는 데 필요한 GPU 메모리가 획기적으로 줄어들어요. 제가 홈랩에서 GPU 메모리가 부족해서 겪었던 수많은 삽질 끝에 찾은 빛과 같은 존재였습니다! 덕분에 8GB나 12GB GPU로도 7B(70억 개 파라미터) 이상의 LLM을 파인튜닝할 수 있게 된 거죠.

    LLM 파인튜닝 실전 구현: LoRA, QLoRA 단계별 적용

    자, 이제 이론은 충분해요! 제가 직접 홈랩에서 시도했던 과정을 바탕으로, LoRA와 QLoRA를 활용한 모델 학습 실전 가이드를 단계별로 알려드릴게요.

    1. 환경 준비

    먼저 필요한 라이브러리들을 설치해야 합니다. 파이썬(Python) 환경은 기본이겠죠? 저는 주로 가상 환경(Virtual Environment)을 만들어서 작업하는데, 깔끔하더라고요.

    # 가상 환경 생성 및 활성화
    python -m venv llm_finetune_env
    source llm_finetune_env/bin/activate
    
    # 필요한 라이브러리 설치
    # transformers: Hugging Face 모델 및 트레이너
    # peft: 파라미터 효율적 파인튜닝 (LoRA, QLoRA 등)
    # bitsandbytes: 4비트 양자화 지원
    # accelerate: 분산 학습 가속화
    # datasets: 데이터셋 로드 및 처리
    # trl: 트랜스포머 강화 학습 (SFTTrainer 사용)
    pip install transformers peft bitsandbytes accelerate datasets trl torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
    

    여기서 <code>torch 설치 시 CUDA 버전에 맞는 URL을 사용해야 해요. 저는 CUDA 12.1 환경이라 cu121을 썼지만, 여러분의 환경에 맞춰 조절해주세요. ⚠️ GPU 드라이버와 CUDA Toolkit 버전이 제대로 맞지 않으면 모델 학습 시 에러가 발생할 수 있으니 꼭 확인하셔야 합니다!

    2. 데이터셋 준비

    파인튜닝에 사용할 데이터셋을 준비해야 합니다. 저는 간단한 질문-답변 형식의 JSON 파일을 사용했어요. 실제 프로젝트에서는 여러분의 목적에 맞는 데이터를 잘 정제하는 것이 무엇보다 중요합니다.

    # example_dataset.json
    [
      {
        "instruction": "다음 질문에 대해 간결하게 답변해 주세요.",
        "input": "LLM 파인튜닝이 무엇인가요?",
        "output": "LLM 파인튜닝은 이미 학습된 거대 언어 모델을 특정 작업이나 데이터셋에 맞게 추가로 학습시키는 과정입니다."
      },
      {
        "instruction": "LoRA의 장점을 설명해 주세요.",
        "input": "",
        "output": "LoRA는 원본 모델의 가중치를 고정하고 작은 행렬만 학습하여, 파라미터 수를 획기적으로 줄이고 학습 효율을 높이는 파인튜닝 기법입니다."
      }
    ]
    

    이 데이터를 파이썬에서 불러와서 datasets 라이브러리로 처리하면 돼요.

    3. 모델 로드 및 양자화 설정 (QLoRA)

    이제 Hugging Face에서 적당한 LLM 모델을 불러옵니다. 저는 Llama 2 7B 모델을 예시로 들어볼게요. QLoRA를 사용하려면 bitsandbytes 설정을 잘 해줘야 합니다.

    import torch
    from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
    
    # 모델 ID (예시: 메타의 Llama-2 7B 모델)
    model_id = "meta-llama/Llama-2-7b-hf" # 실제 사용 시 접근 권한 필요
    
    # 4비트 양자화 설정
    bnb_config = BitsAndBytesConfig(
        load_in_4bit=True, # 4비트 양자화로 로드
        bnb_4bit_use_double_quant=True, # 이중 양자화 사용
        bnb_4bit_quant_type="nf4", # NormalFloat 4 (NF4) 양자화 타입
        bnb_4bit_compute_dtype=torch.bfloat16 # 계산 시 사용할 데이터 타입
    )
    
    # 토크나이저 및 모델 로드
    tokenizer = AutoTokenizer.from_pretrained(model_id)
    model = AutoModelForCausalLM.from_pretrained(
        model_id,
        quantization_config=bnb_config,
        device_map="auto" # 여러 GPU가 있다면 자동으로 분배
    )
    model.config.use_cache = False # 학습 중 캐시 비활성화
    model.config.pretraining_tp = 1 # 사전 학습 텐서 병렬 처리 설정
    

    여기서 model_id는 실제 접근 가능한 모델 ID로 변경하셔야 해요. Llama 2 같은 모델은 Hugging Face에서 접근 권한을 요청해야 하거든요. 저는 보통 공개된 모델 중 하나를 선택해서 실험합니다.

    4. LoRA 설정 및 모델 학습 준비

    peft 라이브러리를 사용해서 LoRAConfig를 정의합니다. 여기서 r (LoRA 랭크)와 lora_alpha 값이 정말 중요해요.

    from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
    
    # 4비트 학습을 위해 모델 준비
    model = prepare_model_for_kbit_training(model)
    
    # LoRA 설정
    lora_config = LoraConfig(
        r=16, # LoRA 랭크. 값이 클수록 표현력이 좋지만 파라미터도 늘어남.
        lora_alpha=32, # LoRA 스케일링 계수. r의 두 배 정도가 일반적.
        target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], # LoRA를 적용할 모듈
        bias="none", # 편향(bias) 학습 여부. 'none'이 일반적.
        lora_dropout=0.05, # LoRA 레이어에 적용할 드롭아웃
        task_type="CAUSAL_LM", # 작업 유형 (인과 언어 모델)
    )
    
    # PEFT 모델 생성
    model = get_peft_model(model, lora_config)
    model.print_trainable_parameters() # 학습 가능한 파라미터 수 확인
    

    target_modules는 LoRA를 적용할 트랜스포머 블록 내의 레이어들을 지정해요. 주로 쿼리(query), 키(key), 밸류(value), 아웃풋(output) 프로젝션 레이어에 적용하죠.

    LoRA 설정 후 학습 가능한 파라미터 수가 얼마나 줄었는지 확인하는 모습입니다. 정말 극적으로 줄어들죠?

    5. 트레이너 설정 및 모델 학습

    이제 trl 라이브러리의 SFTTrainer를 사용해서 모델 학습을 시작합니다. SFTTrainer는 지도 파인튜닝(Supervised Fine-Tuning)에 특화되어 있어서 데이터셋 처리가 정말 편해요.

    from trl import SFTTrainer
    from transformers import TrainingArguments
    from datasets import load_dataset
    
    # 데이터셋 로드 (위에서 만든 example_dataset.json 사용)
    dataset = load_dataset("json", data_files="example_dataset.json")
    
    # 훈련 인자 설정
    training_args = TrainingArguments(
        output_dir="./results", # 결과 저장 디렉토리
        num_train_epochs=3, # 학습 에포크 수
        per_device_train_batch_size=2, # GPU당 배치 크기 (메모리 제약 시 줄임)
        gradient_accumulation_steps=4, # 기울기 누적 스텝 수 (가상 배치 크기 증가)
        optim="paged_adamw_8bit", # 8비트 AdamW 옵티마이저 (메모리 효율적)
        learning_rate=2e-4, # 학습률
        logging_steps=10, # 로깅 스텝
        save_strategy="epoch", # 에포크마다 모델 저장
        evaluation_strategy="no", # 평가 전략 (간단 예시에서는 평가 생략)
        fp16=False, # QLoRA 사용 시 FP16은 비활성화
        bf16=True, # bfloat16 사용 (A100, RTX 30/40 시리즈 등 지원 GPU)
    )
    
    # SFTTrainer 생성
    trainer = SFTTrainer(
        model=model,
        train_dataset=dataset["train"],
        peft_config=lora_config,
        dataset_text_field="input", # 텍스트 필드 지정 (데이터셋 구조에 따라 다름)
        max_seq_length=512, # 최대 시퀀스 길이
        tokenizer=tokenizer,
        args=training_args,
    )
    
    # 학습 시작!
    trainer.train()
    
    # 학습된 어댑터 저장
    trainer.model.save_pretrained("./my_llm_adapter")
    tokenizer.save_pretrained("./my_llm_adapter")
    

    gradient_accumulation_steps는 메모리가 부족할 때 배치 크기를 효과적으로 늘리는 방법이에요. per_device_train_batch_size * gradient_accumulation_steps가 실제 효과적인 배치 크기가 됩니다. bf16=True는 bfloat16을 지원하는 GPU에서 모델 학습 속도를 높이고 메모리 효율을 좋게 만듭니다.

    ⚠️ 주의사항 및 트러블슈팅 (삽질 경험 공유)

    제가 이 과정에서 정말 많이 겪었던 문제들이 몇 가지 있어요. 독자 여러분은 저처럼 삽질하지 마시라고 공유해드립니다!

    • ⚠️ CUDA Out of Memory (OOM) 에러: 가장 흔하게 만나는 문제일 거예요. 특히 GPU 메모리가 8GB, 12GB 정도라면 QLoRA를 써도 OOM이 발생할 수 있어요.
      • 해결책: per_device_train_batch_size를 1로 줄이고, gradient_accumulation_steps를 늘려보세요. max_seq_length도 줄이는 것이 도움이 될 수 있습니다. bitsandbytes의 bnb_4bit_compute_dtype을 torch.bfloat16 대신 torch.float16으로 바꿔보는 것도 방법입니다. (단, bf16=True 대신 fp16=True로 설정해야 합니다.)
    • ⚠️ 모델 학습 속도 저하: QLoRA는 메모리 효율적이지만, 4비트 양자화된 가중치를 매번 역양자화(dequantize)해서 계산해야 하므로 학습 속도가 약간 느려질 수 있어요.
      • 해결책: bf16=True를 사용하면 (지원 GPU 한정) 속도 개선에 도움이 됩니다. 데이터 로딩 파이프라인 최적화(예: num_workers 설정)도 고려해보세요.
    • ⚠️ 데이터셋 포맷 오류: SFTTrainer의 dataset_text_field나 formatting_func 설정이 데이터셋 구조와 맞지 않으면 에러가 납니다.
      • 해결책: 데이터셋 JSON 파일의 키(key)와 dataset_text_field가 정확히 일치하는지 확인해야 해요. 만약 복잡한 포맷이라면 formatting_func를 직접 구현해서 데이터를 원하는 형태로 만들어줘야 합니다. 저도 여기서 한참 헤맸거든요.
    • ⚠️ 커스텀 모델의 성능 부족: 아무리 파인튜닝을 해도 원하는 결과가 나오지 않을 때가 있어요.
      • 해결책: 가장 큰 원인은 데이터셋의 품질과 양입니다. 충분히 다양하고 고품질의 데이터가 아니라면 모델은 제대로 학습되지 않아요. LoRA 하이퍼파라미터(r, lora_alpha, learning_rate)를 튜닝해보는 것도 중요합니다. 저는 wandb(Weights & Biases) 같은 툴을 써서 실험 결과를 추적하며 모델 학습의 최적 파라미터를 찾곤 합니다.

    파인튜닝 모델 검증 및 결과 확인 🎉

    자, 이제 학습이 끝났으니 우리가 만든 커스텀 LLM이 얼마나 잘 작동하는지 확인해볼 차례예요!

    1. 학습된 모델 로드

    학습된 LoRA 어댑터와 토크나이저를 다시 불러옵니다. 이때 원본 모델도 함께 로드해야 해요.

    from transformers import AutoTokenizer, AutoModelForCausalLM
    from peft import PeftModel, PeftConfig
    import torch
    
    # 원본 모델 로드 (QLoRA 설정과 동일하게)
    model_id = "meta-llama/Llama-2-7b-hf"
    bnb_config = BitsAndBytesConfig(
        load_in_4bit=True,
        bnb_4bit_use_double_quant=True,
        bnb_4bit_quant_type="nf4",
        bnb_4bit_compute_dtype=torch.bfloat16
    )
    base_model = AutoModelForCausalLM.from_pretrained(
        model_id,
        quantization_config=bnb_config,
        device_map="auto"
    )
    tokenizer = AutoTokenizer.from_pretrained(model_id)
    
    # 학습된 LoRA 어댑터 로드
    peft_model_id = "./my_llm_adapter"
    model = PeftModel.from_pretrained(base_model, peft_model_id)
    
    # 모델을 평가 모드로 전환 (Dropout 등 비활성화)
    model.eval()
    

    2. 추론 (Inference)

    이제 우리의 커스텀 모델에 질문을 던져봅시다. 학습 데이터셋에 있던 내용과 비슷한 질문을 던져보면, 훨씬 더 정확하고 원하는 형식의 답변을 생성하는 것을 볼 수 있을 거예요.

    # 질문 생성 (데이터셋 형식과 유사하게)
    prompt = "### Instruction:\n다음 질문에 대해 간결하게 답변해 주세요.\n\n### Input:\nLLM 파인튜닝은 무엇인가요?\n\n### Output:"
    
    # 토크나이저로 인코딩
    inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
    
    # 모델로 답변 생성
    with torch.no_grad():
        outputs = model.generate(
            **inputs,
            max_new_tokens=200, # 최대 생성 토큰 수
            do_sample=True, # 샘플링 기반 생성
            top_p=0.9, # 상위 p 확률 내에서 샘플링
            temperature=0.7, # 창의성 조절
        )
    
    # 결과 디코딩 및 출력
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    print(response)
    

    출력된 답변을 보면, 우리가 파인튜닝한 의도대로 간결하고 정확한 정보가 나오는 것을 확인할 수 있어요. 처음엔 정말 너무 기특해서 박수까지 쳤다니까요! 🎉

    파인튜닝된 LLM이 질문에 대해 답변하는 모습입니다. 우리가 의도한 대로 잘 대답하고 있죠?

    마무리 💡

    오늘은 13년차 인프라 엔지니어인 제가 직접 경험하며 익힌 LLM 파인튜닝의 세계, 특히 LoRA와 QLoRA 기법을 활용한 커스텀 LLM 구축 방법에 대해 자세히 알아봤습니다.

    이 방법들을 통해 우리는 다음과 같은 장점을 얻을 수 있더라고요.

    • ✅ GPU 메모리 효율성: QLoRA 덕분에 적은 GPU 자원으로도 거대 모델을 파인튜닝할 수 있게 되었습니다. 저처럼 홈랩을 운영하는 분들에게는 정말 큰 축복이에요!
    • ✅ 빠른 모델 학습: 전체 모델을 학습하는 대신 소수의 파라미터만 학습하여 시간을 절약할 수 있습니다.
    • ✅ 저장 및 배포 용이성: 학습된 LoRA 어댑터는 크기가 매우 작아서 관리하고 공유하기가 훨씬 편해요.

    물론, 이 과정에서 수많은 삽질과 시행착오가 있었지만, 결국 원하는 결과를 얻었을 때의 뿌듯함은 정말 대단했습니다. 여러분도 저의 경험이 시행착오를 줄이는 데 도움이 되기를 바라요.

    이제 여러분은 나만의 특화된 LLM을 만들 수 있는 강력한 도구를 손에 넣으신 거예요. 다음 단계로는 더 다양한 데이터셋으로 실험해보거나, Mistral, Gemma 같은 다른 오픈소스 LLM에 적용해보는 것도 정말 좋은 도전이 될 거예요. 궁금한 점이나 추가로 다루었으면 하는 주제가 있다면 언제든지 댓글로 남겨주세요! 다음 글에서는 이렇게 파인튜닝된 모델을 실제 서비스에 배포하는 과정에 대해 다뤄볼까 합니다. 기대해주세요!

    LoRA와 QLoRA의 주요 장점과 고려 사항을 요약한 인포그래픽입니다.

  • [Cloud] Terraform State 관리 완벽 가이드: S3, DynamoDB 활용 모범 사례

    [Cloud] Terraform State 관리 완벽 가이드: S3, DynamoDB 활용 모범 사례

    안녕하세요, 13년차의 서버실 주인장입니다. 오늘은 인프라 엔지니어라면 한 번쯤은 머리를 싸매고 고민했을 주제, 바로 Terraform State 관리에 대해 이야기해보려고 합니다.

    제가 처음 Terraform을 접했을 때, 로컬에 생성되는 <code>terraform.tfstate 파일이 그렇게 귀찮을 수가 없더라고요. 혼자 작업할 때는 괜찮았는데, 팀원들과 함께 프로젝트를 진행하면서부터는 ‘이거 큰일 나겠다!’ 싶었습니다. 서로 다른 사람이 동시에 terraform apply를 실행하면 어떻게 될까요? 네, 상상만 해도 끔찍하죠. State 파일이 꼬여서 인프라가 엉망진창이 되는 대참사를 막기 위해, 오늘은 AWS S3와 DynamoDB를 활용한 Terraform State 관리 모범 사례를 완벽하게 파헤쳐 보려고 합니다. 💡

    Terraform State 관리의 전체 아키텍처 다이어그램입니다. S3와 DynamoDB가 어떻게 상호작용하는지 한눈에 볼 수 있습니다.

    Terraform State, 왜 중요한가요?

    Terraform State(테라폼 상태 파일)는 Terraform이 관리하는 실제 인프라 자원들의 현황을 기록한 파일입니다. 쉽게 말해, “지금 내가 관리하고 있는 인프라가 어떤 모습으로 생겼는지”를 알려주는 지도 같은 거죠. 이 파일이 있어야 Terraform은 다음에 apply를 실행했을 때, 현재 인프라 상태와 새로운 설정 파일(.tf) 간의 차이점을 파악하고, 필요한 변경 사항만 적용할 수 있습니다.

    만약 State 파일이 없거나, 여러 사람이 각자 다른 버전을 가지고 있다면 어떻게 될까요? Terraform은 인프라의 실제 상태를 알 수 없어서 엉뚱한 자원을 생성하거나, 심지어는 멀쩡한 자원을 삭제해버리는 불상사가 발생할 수 있습니다. 그래서 안정적인 IaC(Infrastructure as Code, 코드형 인프라) 운영을 위해서는 Terraform State 관리가 정말 중요합니다. 특히 팀 협업 환경에서는 이 State 파일을 안전하고 일관되게 관리하는 것이 핵심 중의 핵심입니다.

    S3 백엔드로 원격 State 관리하기

    로컬에 State 파일을 두는 것은 개인적인 실험 환경에서는 괜찮지만, 실제 프로덕션 환경이나 팀 프로젝트에서는 절대 금물입니다. 제가 예전에 홈랩에서 이것저것 테스트하다가 USB를 날려먹으면서 State 파일도 같이 날려버린 적이 있었거든요. 그때 복구하느라 며칠 밤낮을 새웠던 걸 생각하면 아직도 아찔합니다. 😨

    그래서 우리는 S3 백엔드(S3 backend)를 사용해서 Terraform State 파일을 원격으로 저장할 겁니다. AWS S3는 객체 스토리지 서비스로, 높은 내구성(durability), 가용성(availability), 그리고 버전 관리(versioning) 기능을 제공해서 Terraform State를 저장하기에 아주 적합합니다. S3에 State 파일을 저장하면 팀원 누구나 안전하게 접근할 수 있거든요.

    1. S3 버킷 생성하기

    먼저 Terraform State 파일을 저장할 S3 버킷을 생성해야 합니다. 버킷 이름은 전역적으로 고유해야 하며, 버전 관리(Versioning)를 활성화하는 것을 강력히 권장합니다. 혹시 모를 실수로 State 파일이 변경되거나 삭제되더라도 이전 버전으로 복구할 수 있거든요. 저도 예전에 실수로 terraform destroy를 잘못 날렸다가 S3 버전 관리 덕분에 한숨 돌린 적이 있습니다.

    aws s3api create-bucket \
      --bucket my-terraform-state-bucket-13years-infra \
      --region ap-northeast-2 \
      --create-bucket-configuration LocationConstraint=ap-northeast-2
    
    aws s3api put-bucket-versioning \
      --bucket my-terraform-state-bucket-13years-infra \
      --versioning-configuration Status=Enabled
    

    콘솔에서 직접 생성하셔도 됩니다. 이때 중요한 건, “버전 관리”를 꼭 켜주세요! 그리고 필요하다면 서버 측 암호화(Server-side encryption)도 적용하는 게 좋습니다. 💡

    Terraform State 저장을 위한 AWS S3 백엔드 버킷 설정 화면입니다. 버전 관리와 암호화 설정이 중요합니다.

    2. Terraform 구성 파일에 S3 백엔드 설정 추가하기

    이제 Terraform 프로젝트의 main.tf (또는 별도의 backend.tf) 파일에 S3 백엔드 설정을 추가합니다.

    terraform {
      backend "s3" {
        bucket         = "my-terraform-state-bucket-13years-infra" # 위에서 생성한 S3 버킷 이름
        key            = "global/terraform.tfstate"                 # S3 버킷 내 State 파일 경로
        region         = "ap-northeast-2"                           # S3 버킷 리전
        encrypt        = true                                       # State 파일 암호화 활성화
        # dynamodb_table = "my-terraform-locks"                     # State Locking을 위해 나중에 추가 (지금은 주석 처리)
      }
    }
    
    # 예시: VPC를 생성하는 리소스
    resource "aws_vpc" "main" {
      cidr_block = "10.0.0.0/16"
      tags = {
        Name = "MyVPC-ManagedByTerraform"
      }
    }
    

    여기서 key는 S3 버킷 내에서 State 파일이 저장될 경로와 파일명을 지정합니다. 저는 보통 프로젝트별로 또는 환경별로 구분해서 project-name/env/terraform.tfstate 이런 식으로 관리하곤 합니다.

    3. `terraform init` 실행하기

    설정을 추가했다면, 이제 terraform init 명령어를 실행해야 합니다. 이 명령어가 백엔드 설정을 초기화하고, S3 버킷에 State 파일을 생성하거나 기존 파일을 연결합니다.

    terraform init
    

    성공적으로 실행되면 다음과 같은 메시지를 볼 수 있습니다.

    Initializing the backend...
    Successfully configured the backend "s3"! Terraform will now
    persist and retrieve its state from this configuration.
    
    ... (다른 초기화 메시지) ...
    
    Terraform has been successfully initialized!
    

    이제 여러분의 State 파일은 안전하게 S3에 저장됩니다. ✅

    DynamoDB로 State Locking 구현하기

    S3 백엔드만으로는 협업 시 발생할 수 있는 문제를 완전히 해결할 수 없습니다. 바로 동시성 문제(concurrency issue)죠. 여러 사람이 동시에 terraform apply를 실행하면 어떻게 될까요? State 파일이 꼬여버리거나, 한 사람이 작업하는 동안 다른 사람이 같은 자원을 변경해서 예상치 못한 결과가 나올 수 있습니다. 제가 처음 이 문제에 부딪혔을 때, “이거 진짜 난감하네…” 싶었습니다. 😅

    그래서 필요한 게 바로 State Locking(상태 잠금)입니다. Terraform은 AWS DynamoDB를 활용하여 이 State Locking을 구현할 수 있습니다. DynamoDB는 NoSQL 데이터베이스 서비스로, 높은 성능과 가용성을 제공하며, 특히 잠금 메커니즘을 구현하기에 아주 적합합니다. 한 번에 한 사람만 State 파일을 변경할 수 있도록 잠금을 걸어주는 거죠.

    1. DynamoDB 테이블 생성하기

    DynamoDB 테이블을 생성합니다. 이때 테이블 이름은 자유롭게 지정할 수 있지만, 파티션 키(Partition Key)는 반드시 LockID로 설정해야 합니다. 이 LockID를 기준으로 잠금 레코드가 저장되기 때문입니다.

    AWS CLI를 사용한 생성 예시입니다.

    aws dynamodb create-table \
      --table-name my-terraform-locks \
      --attribute-definitions AttributeName=LockID,AttributeType=S \
      --key-schema AttributeName=LockID,KeyType=HASH \
      --billing-mode PAY_PER_REQUEST
    

    PAY_PER_REQUEST 모드를 사용하면 사용한 만큼만 비용을 지불하므로, 트래픽이 많지 않은 State Locking 용도로는 비용 효율적입니다.

    2. Terraform 구성 파일에 DynamoDB 설정 추가하기

    이제 이전에 설정했던 S3 백엔드 블록에 dynamodb_table 인자를 추가합니다.

    terraform {
      backend "s3" {
        bucket         = "my-terraform-state-bucket-13years-infra"
        key            = "global/terraform.tfstate"
        region         = "ap-northeast-2"
        encrypt        = true
        dynamodb_table = "my-terraform-locks" # 새로 생성한 DynamoDB 테이블 이름
      }
    }
    
    # ... (나머지 리소스 정의) ...
    

    3. `terraform init` 다시 실행하기

    백엔드 설정을 변경했으니, terraform init 명령어를 다시 실행해야 합니다. Terraform이 변경된 백엔드 설정을 인식하고 DynamoDB 테이블을 활용하기 시작합니다.

    terraform init
    

    이제부터 terraform apply나 terraform destroy 같은 State를 변경하는 명령어를 실행할 때, Terraform은 DynamoDB 테이블에 잠금 레코드를 생성하여 다른 사용자가 동시에 작업을 하지 못하도록 막아줍니다. 협업 환경에서 안정성을 크게 높여주는 핵심 기능이죠! 🎉

    ⚠️ 삽질 경험: 흔한 실수와 해결책

    제가 이 설정을 하면서 겪었던 몇 가지 삽질 경험과 그 해결책을 공유해 드릴게요. 여러분은 저처럼 고생하지 마시라고요! 😅

    • S3 버킷 권한 문제: terraform init이나 apply 시 “Access Denied” 오류가 발생하면, Terraform을 실행하는 IAM 사용자 또는 역할에 S3 버킷에 대한 s3:GetObject, s3:PutObject, s3:ListBucket 등의 권한이 제대로 부여되었는지 확인해야 합니다. 특히 S3 버킷 정책(Bucket Policy)이 설정되어 있다면 더욱 꼼꼼히 봐야 합니다.
    • DynamoDB 테이블 이름 오타 또는 권한 부족: DynamoDB 테이블 이름을 backend "s3" 블록에 잘못 적거나, 테이블에 대한 dynamodb:GetItem, dynamodb:PutItem, dynamodb:DeleteItem 권한이 없으면 잠금 오류가 발생합니다. 반드시 LockID 파티션 키로 테이블이 생성되었는지, 그리고 IAM 권한이 충분한지 확인하세요.
    • `terraform init` 재실행 누락: 백엔드 설정을 변경하고 terraform init을 다시 실행하지 않아서 변경사항이 적용되지 않는 경우가 많습니다. 저도 자주 까먹어서 “분명히 설정했는데 왜 안 되지?” 하고 한참 헤맸던 적이 있습니다. 🤦‍♂️ 백엔드 설정이 바뀌면 무조건 terraform init! 기억하세요.
    • S3 버전 관리 미활성화: 실수로 terraform state rm 같은 명령어를 잘못 실행해서 State 파일이 삭제되었을 때, S3 버전 관리가 활성화되어 있다면 이전 버전으로 복구할 수 있습니다. 이거 정말 생명줄 같은 기능이니 꼭 활성화하세요!

    이런 작은 실수가 큰 문제로 이어질 수 있으니, 항상 주의 깊게 확인하는 습관을 들이는 것이 중요합니다.

    Terraform State 관리 모범 사례를 요약한 인포그래픽입니다. 핵심 원칙들을 한눈에 파악할 수 있습니다.

    검증 및 결과: 이제 안심하고 협업하세요!

    모든 설정이 완료되었다면, 이제 팀원들과 함께 걱정 없이 Terraform을 사용할 수 있습니다. State Locking이 제대로 작동하는지 확인하는 가장 좋은 방법은, 두 명의 사용자가 동시에 terraform apply를 실행해보는 것입니다. 한 명의 작업이 진행되는 동안 다른 한 명은 잠금 오류 메시지를 받게 될 거예요. 이렇게 되면 성공입니다!

    또한, terraform state list 명령어를 통해 S3에 저장된 State 파일의 내용을 확인하거나, AWS 콘솔에서 S3 버킷과 DynamoDB 테이블의 항목들을 직접 확인해볼 수도 있습니다. DynamoDB 테이블에는 LockID를 가진 항목이 잠금 상태를 나타냅니다.

    terraform state list
    

    이제 여러분의 IaC 상태 관리(IaC state management)는 훨씬 더 안정적이고 견고해졌을 겁니다. 팀원들과의 Terraform 협업(Terraform collaboration)도 한층 부드러워질 거고요. 드디어 됐다! 이 안정감, 진짜 편하더라고요. 👍

    Terraform 명령어를 실행하여 State가 성공적으로 관리되고 있음을 보여주는 스크린샷입니다. DynamoDB 락도 확인해볼 수 있습니다.

    마무리하며: 더 나은 IaC 여정을 위해

    오늘은 Terraform State를 S3와 DynamoDB를 활용하여 안전하게 관리하는 방법에 대해 자세히 알아봤습니다. 13년차 엔지니어로서 직접 겪었던 경험과 삽질까지 솔직하게 공유해 드렸는데, 도움이 되셨으면 좋겠습니다.

    Terraform State 관리는 단순한 파일 저장을 넘어, 안정적인 인프라 운영과 효율적인 팀 협업을 위한 필수적인 요소입니다. S3의 내구성과 버전 관리, 그리고 DynamoDB의 강력한 잠금 기능을 활용하면, 여러분의 IaC 여정은 훨씬 더 순탄해질 거예요.

    다음 글에서는 Terraform State 파일을 더욱 안전하게 보호하기 위한 추가적인 보안 설정(예: IAM 정책 강화, S3 버킷 정책, VPC Endpoint 활용 등)에 대해 더 깊게 다뤄볼 예정입니다. 기대해주세요! 😊

  • [k8s] 쿠버네티스 서비스 메시: Istio vs Linkerd 심층 비교 및 선택 가이드

    [k8s] 쿠버네티스 서비스 메시: Istio vs Linkerd 심층 비교 및 선택 가이드

    안녕하세요, 13년차의 서버실 주인장입니다. 😎

    마이크로서비스 아키텍처(MSA)가 대세가 되면서 애플리케이션 개발은 훨씬 유연해졌지만, 그만큼 인프라 운영의 복잡도는 기하급수적으로 늘어나더라고요. 수많은 서비스 간의 통신, 트래픽 관리, 보안, 그리고 장애 발생 시 원인 추적까지… 저도 처음엔 이게 뭔가 싶어서 삽질 좀 많이 했었죠. 특히 쿠버네티스(Kubernetes) 환경에서 이런 고민은 더 커지더라고요.

    그래서 등장한 개념이 바로 서비스 메시(Service Mesh)입니다. 서비스 메시는 이런 복잡한 문제들을 깔끔하게 해결해 줄 수 있는 강력한 도구인데요, 그중에서도 가장 많이 언급되는 두 가지가 바로 Istio와 Linkerd입니다. 오늘은 이 두 서비스 메시 솔루션을 심층적으로 비교해 보고, 여러분의 환경에 어떤 것이 더 적합할지 함께 고민해 보는 시간을 가져볼까 합니다. 제가 홈랩에서 직접 써보면서 느꼈던 점들을 솔직하게 공유해 드릴게요!

    쿠버네티스 환경에서 서비스 메시가 어떻게 동작하는지 보여주는 개념도입니다. 데이터 플레인과 컨트롤 플레인의 역할을 시각적으로 나타냅니다.

    서비스 메시(Service Mesh)란 무엇인가요?

    쉽게 말해 서비스 메시는 마이크로서비스 간의 통신을 관리하고 제어하는 인프라 계층입니다. 애플리케이션 코드 변경 없이 트래픽 관리, 보안, 관측 가능성(Observability) 기능을 제공하거든요. 마치 서비스들 사이에 ‘스마트한 프록시’를 두는 것과 같다고 생각하시면 편합니다.

    • 데이터 플레인(Data Plane): 실제 서비스 간 트래픽을 가로채고 처리하는 부분입니다. 보통 사이드카(Sidecar) 프록시 형태로 각 서비스 파드(Pod) 옆에 배포됩니다. Istio는 Envoy를, Linkerd는 자체 개발한 Rust 기반 프록시를 사용합니다.
    • 컨트롤 플레인(Control Plane): 데이터 플레인의 프록시들을 제어하고 구성하는 부분입니다. 트래픽 라우팅 규칙, 정책, 인증서 등을 관리합니다.

    이런 분리 덕분에 개발자는 비즈니스 로직에만 집중하고, 인프라 엔지니어는 서비스 메시를 통해 네트워크 관련 문제들을 중앙에서 효율적으로 관리할 수 있게 되더라고요. 이거 진짜 편하더라고요! 처음엔 설치하고 설정하는 게 좀 번거로웠지만, 한 번 구축해두니 마이크로서비스 운영이 훨씬 수월해졌습니다. 🎉

    Istio 깊게 파보기: 강력함과 유연함의 상징

    Istio(이스티오)는 Google, IBM, Lyft가 함께 개발한 오픈소스 서비스 메시 프로젝트입니다. 가장 강력하고 기능이 풍부하다는 평을 받죠. 저도 처음엔 Istio의 방대한 기능에 압도당했었는데, 하나하나 파고들수록 그 유연함에 감탄했었습니다.

    Istio의 주요 특징

    • Envoy 프록시(Proxy): 데이터 플레인으로 업계 표준에 가까운 Envoy 프록시를 사용합니다. 높은 성능과 확장성을 제공하죠.
    • 강력한 트래픽 관리(Traffic Management): A/B 테스팅, 카나리 배포(Canary Deployment), 서킷 브레이킹(Circuit Breaking), 타임아웃, 재시도 등 고급 트래픽 제어 기능을 제공합니다. 제가 특정 서비스의 트래픽을 10%만 신규 버전으로 보내보고 싶을 때 정말 유용하게 썼습니다.
    • 정책 시행(Policy Enforcement): 쿼터(Quota), 속도 제한(Rate Limiting) 등 다양한 정책을 정의하고 적용할 수 있습니다.
    • 보안(Security): 서비스 간 상호 TLS(mTLS)를 기본으로 제공하며, 강력한 인증(Authentication) 및 권한 부여(Authorization) 정책을 설정할 수 있습니다.
    • 관측 가능성(Observability): Prometheus, Grafana, Jaeger 등 다양한 도구와 통합되어 풍부한 메트릭(Metrics), 로그(Logs), 트레이스(Traces)를 수집합니다.

    Istio는 엔터프라이즈 환경이나 매우 복잡한 마이크로서비스 아키텍처를 운영하는 팀에 딱 맞아떨어지더라고요. 기능이 많은 만큼 학습 곡선이 높고, 리소스 소모도 Linkerd에 비해 큰 편입니다. 저도 처음에 설정 파일 보면서 ‘이걸 다 알아야 하나’ 싶었는데, 핵심 기능 위주로 접근하니 그래도 괜찮더라고요.

    Linkerd 깊게 파보기: 심플함과 성능의 미학

    Linkerd(링커디)는 CNCF(Cloud Native Computing Foundation)에서 졸업(Graduated)한 프로젝트로, Buoyant가 주도하고 있습니다. Istio에 비해 기능은 적지만, 가볍고 빠르며 사용하기 쉽다는 게 장점이더라고요. 제 홈랩처럼 리소스가 제한적인 환경에서는 Linkerd의 매력이 상당했습니다.

    Linkerd의 주요 특징

    • Rust 기반 프록시: 데이터 플레인에 경량화된 Rust 기반의 프록시를 사용합니다. 메모리 사용량과 지연 시간(Latency)이 매우 낮아 성능이 뛰어납니다.
    • 간결한 기능(Simplicity): Istio처럼 모든 기능을 제공하기보다는, 핵심적인 트래픽 관리, 관측 가능성, 보안 기능에 집중합니다. 복잡한 설정 없이도 빠르게 시작할 수 있습니다.
    • 자동 mTLS(Automatic mTLS): 서비스 간 통신을 자동으로 암호화하여 보안을 강화합니다. 별다른 설정 없이도 적용되는 점이 좋더라고요.
    • 대시보드(Dashboard): Linkerd CLI와 함께 제공되는 대시보드는 서비스의 상태, 트래픽 흐름, 지연 시간 등을 직관적으로 보여줍니다. 처음 써봤을 때 ‘와, 한눈에 다 보이네!’ 싶었습니다.

    Linkerd는 빠르고 가벼우며, 비교적 작은 규모의 팀이나 복잡한 설정보다는 핵심 기능에 집중하고 싶은 프로젝트에 이상적입니다. 특히 성능이 중요한 환경이라면 Linkerd가 좋은 선택이 될 수 있습니다. 하지만 Istio처럼 세밀한 트래픽 제어가 필요한 경우에는 아쉬울 수 있습니다.

    Istio와 Linkerd의 주요 구성 요소와 기능을 비교하여 보여주는 다이어그램입니다. 각 서비스 메시의 강점이 시각적으로 강조됩니다.

    Istio vs Linkerd 심층 비교 테이블

    이제 두 서비스 메시의 핵심적인 차이점을 한눈에 볼 수 있도록 표로 정리해볼게요. 제가 직접 써보면서 느낀 점들도 함께 녹여냈으니, 여러분의 선택에 도움이 되었으면 좋겠습니다.

    구분 Istio Linkerd
    개발 주체 Google, IBM, Lyft (커뮤니티 중심) Buoyant (CNCF 졸업 프로젝트)
    데이터 플레인 프록시 Envoy Proxy Rust 기반 경량 프록시
    복잡도 & 학습 곡선 높음 (다양한 기능, 세밀한 설정) 낮음 (간결한 기능, 쉬운 시작)
    성능 & 리소스 리소스 소모 상대적으로 높음 (기능이 많아서) 매우 낮음 (경량화, 고성능)
    트래픽 관리 매우 강력하고 세밀함 (A/B, 카나리, 서킷 브레이커 등) 필수적인 기능 위주 (mTLS, HTTP/gRPC 리트라이 등)
    보안 mTLS, 인증/권한 부여 정책, JWT 검증 등 자동 mTLS 기본 제공, 정책은 Istio보다 적음
    관측 가능성 Prometheus, Grafana, Jaeger 등 통합 (풍부한 메트릭/트레이스) 내장 대시보드, Grafana 통합 (핵심 메트릭)
    확장성 Mixer(구), WASM 등 플러그인 확장 용이 비교적 제한적 (핵심 기능에 집중)
    주요 사용 사례 대규모 엔터프라이즈, 복잡한 마이크로서비스 아키텍처, 세밀한 제어 필요 성능 중시, 빠른 시작, 리소스 제약 환경, 심플한 관리 선호

    어떤 서비스 메시를 선택해야 할까요? 선택 가이드 및 실전 팁

    결론부터 말하자면 ‘정답은 없다’는 거죠. 여러분의 프로젝트 요구사항, 팀의 역량, 그리고 인프라 환경에 따라 최적의 선택이 달라질 수 있습니다.

    1. 복잡한 트래픽 제어가 필수라면 Istio: A/B 테스팅, 카나리 배포, 폴트 인젝션(Fault Injection) 등 매우 세밀하고 다양한 트래픽 제어 전략이 필요하다면 Istio가 좋은 선택입니다. 초반 학습 비용은 들겠지만, 그만큼 강력한 기능을 제공하거든요.
    2. 심플함과 성능이 최우선이라면 Linkerd: 서비스 메시의 핵심 기능(mTLS, 기본적인 트래픽 관리, 관측 가능성)만으로 충분하고, 리소스 효율성과 낮은 지연 시간이 중요하다면 Linkerd가 탁월합니다. 제가 홈랩에서 가볍게 실험할 때는 Linkerd가 정말 편하더라고요. 설치도 쉽고, 금방 결과물을 볼 수 있었어요.
    3. 팀의 역량과 학습 곡선 고려: Istio는 강력한 만큼 설정할 것이 많고 복잡합니다. 팀에 서비스 메시 전문가가 있거나, 학습에 충분한 시간을 투자할 여력이 있다면 Istio를 고려해볼 만합니다. Linkerd는 비교적 쉽게 시작하고 운영할 수 있어서, 서비스 메시를 처음 도입하는 팀에 부담이 덜할 수 있습니다.
    4. 기존 에코시스템 통합: 이미 Prometheus, Grafana, Jaeger 같은 특정 도구들을 깊게 사용하고 있다면, 해당 도구들과의 통합이 얼마나 쉬운지도 고려해야 합니다. 두 솔루션 모두 잘 통합되지만, Istio는 더 넓은 스펙트럼의 통합을 지원하는 경향이 있습니다.

    제가 실제로 여러 프로젝트에서 두 가지를 모두 사용해보니, ‘작은 규모부터 시작해서 점진적으로 확장할 계획이라면 Linkerd로 가볍게 시작하고, 나중에 필요하다면 Istio로 마이그레이션을 고려하는 것도 한 방법’이라는 생각을 하게 되었습니다. 💡

    ⚠️ 주의사항 및 트러블슈팅: 삽질은 나의 힘!

    서비스 메시를 도입하면 모든 문제가 해결될 것 같지만, 사실 새로운 종류의 삽질이 시작되기도 합니다. 저도 처음엔 많이 헤맸거든요.

    • 리소스 오버헤드(Resource Overhead): 서비스 메시의 사이드카 프록시는 각 파드에 추가되므로, 네트워크 오버헤드와 함께 CPU, 메모리 사용량이 증가합니다. 특히 Istio는 이 부분이 Linkerd보다 더 두드러질 수 있어요. 항상 리소스 모니터링을 철저히 해야 합니다.
    • 설정 복잡도: 특히 Istio의 VirtualService, DestinationRule, Gateway 같은 CRD(Custom Resource Definition)들은 처음 접할 때 매우 혼란스러울 수 있습니다. YAML 파일을 작성할 때 인덴트(Indent) 하나만 잘못돼도 에러가 나기 일쑤였죠. 😅 공식 문서와 예제를 꼼꼼히 보면서 익숙해지는 수밖에 없습니다.
    • 디버깅의 어려움: 서비스 메시 계층에서 문제가 발생하면, 트래픽이 애플리케이션에 도달하기 전에 차단되거나 잘못 라우팅될 수 있습니다. 이때는 프록시의 로그를 확인하거나, Istio의 istioctl analyze, Linkerd의 linkerd check 같은 진단 도구를 적극 활용해야 합니다. 제가 한번은 특정 서비스로 요청이 계속 실패해서 몇 시간을 날렸는데, 알고 보니 서비스 메시 정책 설정이 잘못되어 외부 트래픽을 아예 막고 있었더라고요. 🤦‍♂️

    이런 삽질을 줄이려면, 도입 전에 작은 스케일로 PoC(개념 증명)를 충분히 해보고, 팀원들과 함께 학습하는 시간을 가지는 것이 중요하다고 생각합니다. 그리고 문서화! 정말 중요합니다.

    서비스 메시 도입 후 Prometheus와 Grafana를 통해 수집된 트래픽, 에러율, 지연 시간 등의 메트릭을 시각적으로 보여주는 대시보드 예시입니다.

    결과 및 검증: 잘 동작하고 있나?

    서비스 메시를 성공적으로 배포했다면, 이제 제대로 동작하는지 확인해야겠죠? 저는 주로 다음과 같은 방법으로 검증했습니다.

    1. 대시보드 확인: Linkerd는 자체 대시보드를 제공하고, Istio는 Kiali 같은 도구와 통합하여 서비스 간 트래픽 흐름, 오류율, 지연 시간 등을 시각적으로 보여줍니다. 여기서 이상 징후를 빠르게 파악할 수 있어요.
    2. 메트릭 및 로그 분석: Prometheus와 Grafana를 통해 수집되는 메트릭을 확인하여 서비스 메시가 트래픽을 정상적으로 처리하고 있는지, 리소스 사용량에 문제는 없는지 주기적으로 모니터링합니다.
    3. 트레이싱(Tracing): Jaeger 같은 분산 트레이싱 도구를 사용하여 특정 요청이 여러 서비스를 거쳐 어떻게 처리되는지 엔드투엔드(End-to-End)로 추적할 수 있습니다. 마이크로서비스 환경에서 문제 발생 시 원인 서비스를 특정하는 데 정말 큰 도움이 되더라고요.
    4. 정책 테스트: 설정한 트래픽 라우팅 규칙이나 보안 정책이 의도한 대로 동작하는지 직접 테스트 요청을 보내 확인합니다. 예를 들어, 특정 버전으로 10% 트래픽을 라우팅하는 카나리 배포를 했다면, 실제로 그 비율대로 트래픽이 분산되는지 확인하는 거죠.

    이런 검증 과정을 통해 서비스 메시가 안정적으로 운영될 수 있도록 지속적으로 관리해야 합니다. 처음엔 낯설겠지만, 익숙해지면 엄청난 효율성을 가져다줄 거예요.

    Istio와 Linkerd 중 하나를 선택하기 위한 의사결정 흐름을 보여주는 인포그래픽입니다. 프로젝트 규모, 기능 요구사항, 팀 역량 등을 기준으로 선택 과정을 요약합니다.

    마무리: 나에게 맞는 서비스 메시 찾기

    오늘은 쿠버네티스 서비스 메시의 양대 산맥인 Istio와 Linkerd에 대해 깊이 있게 알아보고 비교해 봤습니다. 두 솔루션 모두 마이크로서비스 운영의 복잡도를 줄여주고, 트래픽 관리, 보안, 관측 가능성을 향상시키는 강력한 도구라는 점은 변함이 없습니다.

    핵심은 여러분의 프로젝트가 무엇을 더 중요하게 생각하는가입니다. 강력한 기능과 세밀한 제어가 필요하다면 Istio를, 심플함, 경량성, 그리고 빠른 도입이 중요하다면 Linkerd를 고려해 보세요. 저도 처음엔 무조건 기능이 많은 Istio가 최고인 줄 알았는데, 막상 홈랩에서는 Linkerd의 가벼움에 반했거든요. ㅎㅎ

    어떤 솔루션을 선택하든, 충분한 PoC와 학습, 그리고 지속적인 모니터링이 필수입니다. 서비스 메시 도입은 한 번의 설정으로 끝나는 것이 아니라, 마이크로서비스 운영 철학의 변화를 가져오는 과정이라고 생각합니다.

    혹시 Istio나 Linkerd를 사용하면서 겪었던 재미있는 삽질 경험이나 꿀팁이 있다면 댓글로 공유해 주세요! 다음번에는 서비스 메시를 실제 쿠버네티스 클러스터에 배포하고 운영하는 더 구체적인 방법을 다뤄볼까 합니다. 기대해 주세요! 😊

  • [k8s] k3s로 경량 쿠버네티스 클러스터 구축: 홈랩과 엣지 완벽 가이드

    [k8s] k3s로 경량 쿠버네티스 클러스터 구축: 홈랩과 엣지 완벽 가이드

    k3s로 경량 쿠버네티스 클러스터 구축하기: 13년차 엔지니어의 삽질 완전 정복

    안녕하세요, 13년차의 서버실 주인장입니다. 오늘은 제가 최근 홈랩에서 직접 구축해보고, 엣지 컴퓨팅 환경에서도 정말 유용하겠다 싶었던 기술, k3s(케이쓰리 에스)에 대해 이야기해보려고 해요. 기존 쿠버네티스(Kubernetes)는 아무래도 무겁고 복잡해서 홈랩이나 리소스가 제한적인 엣지 환경에 도입하기가 쉽지 않았거든요. 근데 k3s를 만나고 나서 생각이 확 바뀌었습니다. 가볍고 빠르면서도 쿠버네티스의 강력한 기능을 고스란히 쓸 수 있다는 게 정말 매력적이더라고요!

    저도 처음엔 이게 뭔가 싶었는데, 실제로 써보니까 ‘아, 이거 진짜 물건이다!’ 싶었습니다. 제 경험을 바탕으로 k3s 경량 쿠버네티스를 활용해서 클러스터를 어떻게 구축하는지, 그리고 어떤 삽질을 겪었고 어떻게 해결했는지 솔직하게 공유해볼게요. 특히 엣지 컴퓨팅(Edge Computing)이나 저처럼 홈랩(Home Lab)을 운영하는 분들께 큰 도움이 될 거라 생각합니다.

    💡 k3s, 왜 선택했을까? 경량 쿠버네티스의 진짜 매력

    혹시 이런 경험 있으신가요? 작은 라즈베리 파이(Raspberry Pi)나 오래된 미니 PC에 k3s를 포함한 쿠버네티스를 올려보고 싶었는데, 리소스 문제나 복잡한 설치 과정 때문에 포기했던 경험 말이죠. 저도 그랬거든요. 일반적인 쿠버네티스 배포판은 마스터 노드(Master Node) 하나만 해도 꽤 많은 메모리와 CPU를 요구합니다. 그런데 k3s는 이런 제약을 확 낮춰줍니다. 말 그대로 ‘경량(Lightweight)’ 쿠버네티스죠.

    k3s는 Rancher Labs에서 개발한 CNCF(Cloud Native Computing Foundation) 샌드박스 프로젝트로, 리소스가 제한적인 환경, 예를 들면 IoT 디바이스, 엣지 서버, 그리고 저의 사랑스러운 홈랩 같은 곳에 최적화되어 있습니다. 바이너리 파일 하나로 설치가 끝나고, 필요 없는 기능들을 쳐내서 메모리 사용량도 훨씬 적어요. 제가 직접 해보니, 512MB RAM 이상이면 마스터 노드를 구동할 수 있더라고요! 정말 놀랍지 않습니까? 🎉

    k3s 경량 쿠버네티스 클러스터의 간소화된 아키텍처 다이어그램: 마스터 노드와 여러 워커 노드가 연결되어 엣지 및 홈랩 환경에서 효율적으로 동작하는 모습을 보여줍니다.

    k3s 기본 이해: 경량 쿠버네티스의 핵심 개념

    쉽게 말해 k3s는 쿠버네티스(Kubernetes)의 모든 핵심 기능을 제공하면서도, 배포판을 경량화한 버전입니다. k3s는 기존 쿠버네티스가 제공하는 API Server, Controller Manager, Scheduler, etcd 같은 핵심 컴포넌트들을 모두 포함하고 있어요. 하지만 몇 가지 차이점이 있습니다.

    • 단일 바이너리(Single Binary): 설치 파일이 하나로 되어 있어 배포가 정말 간편합니다.
    • SQLite3 내장(Embedded SQLite3): 기본적으로 외부 etcd 대신 SQLite3를 데이터 저장소로 사용합니다. 물론 PostgreSQL, MySQL, etcd 등 외부 DB도 연결할 수 있어요.
    • 불필요한 기능 제거: 레거시(Legacy) 코드나 클라우드 제공업체(Cloud Provider) 관련 기능을 제거하여 경량화했습니다.
    • 컨테이너 런타임(Container Runtime) 변경: 도커(Docker) 대신 containerd(컨테이너디)를 기본 컨테이너 런타임으로 사용합니다.

    이런 특징들 덕분에 k3s는 일반적인 쿠버네티스에 비해 설치도 빠르고 리소스 소모도 적어서, 소규모 프로젝트나 학습용, 그리고 저처럼 홈랩에서 다양한 실험을 해보고 싶을 때 최적의 선택이 됩니다. 컨테이너 오케스트레이션(Container Orchestration)이라는 쿠버네티스의 핵심 개념은 그대로 가져가면서도, 훨씬 쉽게 접근할 수 있게 해주는 거죠.

    실전! k3s 경량 쿠버네티스 클러스터 구축 스텝별 가이드

    자, 이제 직접 k3s 클러스터를 구축해볼 시간입니다. 저는 라즈베리 파이 4B 두 대와 오래된 미니 PC 한 대를 이용해서 클러스터를 만들어봤어요. OS는 모두 Ubuntu Server 22.04 LTS를 사용했습니다. 여러분도 리눅스(Linux) 기반의 어떤 장비든 준비하시면 됩니다.

    1. k3s 마스터 노드(Server) 설치

    가장 먼저 클러스터의 ‘뇌’ 역할을 할 마스터 노드를 설치합니다. k3s는 정말 설치가 간단해서 명령어 한 줄이면 끝나버려요. 💡 팁: 실제 운영 환경에서는 스크립트 내용을 확인하고 사용하는 것이 좋습니다.

    
    curl -sfL https://get.k3s.io | sh -
    

    이 명령어를 실행하면 k3s가 자동으로 설치되고 서비스로 등록되어 실행됩니다. 설치가 끝나면 제대로 작동하는지 확인해볼까요?

    
    sudo k3s kubectl get nodes
    

    기본적으로 k3s는 자체적으로 제공하는 k3s kubectl 명령어를 사용하도록 설정되어 있어요. 처음엔 조금 어색하지만 익숙해지니 괜찮더라고요. k3s가 정상적으로 설치되었다면 마스터 노드가 Ready 상태로 보일 겁니다. 예를 들면 이런 식이죠:

    
    NAME         STATUS   ROLES                  AGE   VERSION
    master-node   Ready    control-plane,master   2m    v1.28.3+k3s1
    

    2. k3s 워커 노드(Agent) 추가

    이제 마스터 노드에 연결할 워커 노드(Agent Node)를 추가해봅시다. 워커 노드 역시 명령어 한 줄로 설치되는데, 다만 마스터 노드와 연결하기 위한 토큰(Token)이 필요해요. 이 토큰은 마스터 노드에서 확인할 수 있습니다.

    
    sudo cat /var/lib/rancher/k3s/server/node-token
    

    이 명령어를 실행하면 긴 문자열이 나올 텐데, 이게 바로 워커 노드를 연결할 때 필요한 토큰입니다. 이 토큰과 마스터 노드의 IP 주소를 알고 있다면, 워커 노드에서 다음 명령어를 실행해주세요. <master-ip> 부분은 마스터 노드의 실제 IP 주소로, <token> 부분은 위에서 확인한 토큰으로 바꿔주셔야 합니다.

    
    curl -sfL https://get.k3s.io | K3S_URL=https://<master-ip>:6443 K3S_TOKEN=<token> sh -
    

    워커 노드 설치 후, 다시 마스터 노드에서 sudo k3s kubectl get nodes 명령어를 실행해보세요. 몇 분 뒤에 워커 노드가 Ready 상태로 보인다면 성공입니다! 🎉

    k3s 마스터 및 워커 노드 구성과 kubectl 환경 설정이 완료된 명령줄 인터페이스 스크린샷입니다.

    3. kubectl 환경 설정으로 편하게 사용하기

    매번 sudo k3s kubectl이라고 치는 게 귀찮으시죠? 저도 그랬거든요. 일반적인 kubectl 명령어를 사용할 수 있도록 환경 설정을 해봅시다. 마스터 노드에서 다음 명령어를 실행하여 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
    export KUBECONFIG=~/.kube/config
    

    마지막 export 명령어는 현재 세션에만 적용되므로, 로그인할 때마다 자동으로 적용되게 하려면 .bashrc나 .zshrc 파일에 추가하는 것이 좋습니다. 이제 kubectl get nodes 명령어를 실행해보세요. 잘 동작할 겁니다!

    ⚠️ k3s 설치 중 만난 난관들 & 해결법

    제가 13년차 엔지니어라고 해도 삽질은 피할 수 없죠! k3s 설치 중 저를 괴롭혔던 몇 가지 문제와 해결법을 공유합니다. 혹시 여러분도 비슷한 문제를 겪는다면 참고가 되셨으면 좋겠어요.

    1. 워커 노드가 연결되지 않는 문제 (방화벽!)

      처음엔 마스터 노드와 워커 노드 간 통신이 안 되는 거예요. kubectl get nodes를 하면 워커 노드가 NotReady 상태로 계속 머물러 있었죠. 알고 보니 방화벽(Firewall) 문제였습니다. Ubuntu Server는 기본적으로 ufw(Uncomplicated Firewall)가 활성화되어 있는 경우가 많거든요. k3s는 기본적으로 6443/tcp 포트를 사용하는데, 이 포트가 막혀있었던 겁니다. 🤯

      해결법: 마스터 노드와 워커 노드 모두에서 필요한 포트를 열어줬습니다.

      
      sudo ufw allow 6443/tcp  # k3s API Server
      sudo ufw allow 10250/tcp # Kubelet (필수)
      sudo ufw reload
      

      특히 마스터 노드는 워커 노드의 Kubelet과 통신해야 하므로 10250 포트도 열어주는 것이 좋습니다. 이 문제를 해결하고 나니 워커 노드가 바로 Ready 상태로 바뀌더라고요. 역시 네트워크 문제는 언제나 기본부터 체크해야 합니다.

    2. 토큰(Token) 불일치로 인한 연결 실패

      간혹 워커 노드를 추가할 때 K3S_TOKEN 값을 잘못 입력하는 경우가 있어요. 아니면 복사 붙여넣기 할 때 공백이 들어간다거나요. 그럼 당연히 워커 노드가 마스터에 연결되지 않습니다.

      해결법: 워커 노드에서 k3s 서비스를 중지하고 삭제한 후, 정확한 토큰 값으로 다시 설치했습니다.

      
      sudo systemctl stop k3s-agent
      sudo systemctl disable k3s-agent
      sudo k3s-uninstall.sh # 워커 노드에 설치된 k3s 삭제 스크립트
      # 정확한 토큰으로 다시 설치
      curl -sfL https://get.k3s.io | K3S_URL=https://<master-ip>:6443 K3S_TOKEN=<correct-token> sh -
      
    3. 로그 확인의 중요성

      어떤 문제가 발생하든, 가장 먼저 해야 할 일은 로그를 확인하는 것입니다. k3s 서비스의 로그는 journalctl 명령어를 통해 볼 수 있어요.

      
      sudo journalctl -u k3s --since "10 minutes ago" -f
      # 워커 노드는
      sudo journalctl -u k3s-agent --since "10 minutes ago" -f
      

      로그를 보면 에러 메시지나 경고 메시지를 통해 문제의 원인을 파악할 수 있는 경우가 많습니다. 저는 방화벽 문제도 로그에서 connection refused 같은 메시지를 보고 눈치챘거든요.

    🎉 완성! k3s 클러스터에 애플리케이션 배포하기

    클러스터가 성공적으로 구축되었다면, 이제 간단한 애플리케이션을 배포해서 잘 동작하는지 확인해봐야죠. 저는 웹 서버로 유명한 Nginx(엔진엑스)를 배포해볼게요. 다음 YAML 파일을 nginx-deployment.yaml로 저장합니다.

    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      selector:
        app: nginx
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80
      type: NodePort
    

    저장한 파일을 클러스터에 배포합니다.

    
    kubectl apply -f nginx-deployment.yaml
    

    배포가 성공적으로 완료되었다면, 파드(Pod)와 서비스(Service)가 잘 생성되었는지 확인해봅시다.

    
    kubectl get pods -o wide
    kubectl get svc
    

    kubectl get svc 결과에서 nginx-service의 NodePort를 확인해보세요. 예를 들어 30000번대 포트가 할당될 겁니다. 이제 워커 노드의 IP 주소와 할당된 NodePort를 이용하여 웹 브라우저에서 Nginx 웹 페이지에 접속할 수 있습니다. 예를 들어, http://<워커-노드-IP>:<NodePort> 이런 식으로요. 드디어 제 홈랩 k3s 클러스터에서 Nginx가 동작하는 걸 보니 정말 뿌듯하더라고요! 🎉

    k3s 클러스터에 Nginx 애플리케이션 배포 후 웹 브라우저 접속 화면 및 kubectl 명령 결과를 보여주는 스크린샷입니다.

    🚀 13년차 엔지니어의 제언: k3s 경량 쿠버네티스 활용처

    k3s를 직접 구축하고 사용해보니, 이 경량 쿠버네티스가 얼마나 유용한지 다시 한번 깨달았습니다. 제가 생각하는 k3s의 주요 활용처는 다음과 같습니다.

    활용 분야 k3s의 장점 예시
    홈랩 (Home Lab) 저렴한 비용으로 쿠버네티스 학습 및 개인 서비스 운영 라즈베리 파이 클러스터, 개인 웹서버, 미디어 서버
    엣지 컴퓨팅 (Edge Computing) 제한된 리소스 환경에서 컨테이너 애플리케이션 배포 및 관리 스마트 팩토리, 스마트 시티 IoT 게이트웨이, 지점 서버
    개발/테스트 환경 로컬 머신이나 가상 머신에 빠르고 가볍게 k3s 클러스터 구축 CI/CD 파이프라인 테스트, 개발자 개인 환경
    IoT (사물 인터넷) 수많은 IoT 디바이스의 애플리케이션 배포 및 업데이트 관리 자율주행 차량, 스마트 농장 센서 데이터 처리

    k3s는 쿠버네티스의 복잡성을 줄여주면서도 강력한 기능을 제공하기 때문에, 위와 같은 환경에서 컨테이너 기반 애플리케이션을 효율적으로 배포하고 관리하는 데 탁월한 선택이 될 수 있습니다. 저처럼 인프라를 직접 만져보고 실험하는 걸 좋아하는 분들에게는 정말 최고의 장난감이 될 거예요. 💡

    물론 k3s가 모든 상황에 완벽한 만능 해결책은 아니에요. 대규모 엔터프라이즈 환경에서는 여전히 표준 쿠버네티스 배포판이 더 적합할 수 있습니다. 하지만 소규모, 리소스 제한 환경에서는 k3s가 제공하는 가치는 엄청나다고 생각합니다. 저도 처음엔 ‘이 작은 걸로 뭘 할 수 있을까?’ 싶었는데, 지금은 제 홈랩의 핵심 인프라가 되었습니다. 다음 글에서는 k3s 위에 Helm(헬름)으로 애플리케이션을 배포하거나 Persistent Storage(영구 스토리지)를 구성하는 방법에 대해서도 다뤄볼 예정이니 기대해주세요!

    k3s의 주요 장점과 활용 사례를 간결하게 요약한 인포그래픽입니다.

    오늘 제가 공유한 삽질 경험과 해결 과정이 여러분의 k3s 경량 쿠버네티스 클러스터 구축에 조금이나마 도움이 되었기를 바랍니다. 혹시 궁금한 점이나 또 다른 삽질 경험이 있다면 언제든지 댓글로 남겨주세요! 13년차의 서버실은 언제나 열려있습니다. 다음 글에서 또 만나요! 👋

  • [AI] LangChain으로 AI 에이전트 구축: 복잡한 작업 자동화 실전 가이드

    [AI] LangChain으로 AI 에이전트 구축: 복잡한 작업 자동화 실전 가이드

    LangChain으로 AI 에이전트 구축: 복잡한 작업 자동화 실전 가이드

    왜 LangChain AI 에이전트가 필요할까요?

    안녕하세요, 13년차 서버실 지킴이입니다. 인프라 엔지니어로 일하면서 수많은 반복 작업과 복잡한 문제 해결에 시간과 에너지를 쏟았던 경험, 혹시 여러분도 있으신가요? 매번 똑같은 정보를 찾아보고, 여러 시스템을 오가며 데이터를 조합하고, 때로는 예측 불가능한 변수까지 고려해야 하는 상황들 말이죠. 저는 이런 작업들을 보면서 "이걸 좀 더 스마트하게 자동화할 수는 없을까?" 하는 고민을 늘 했었습니다. 특히 최근 LLM(Large Language Model, 대규모 언어 모델)의 발전은 이런 고민에 한 줄기 빛이 되어주었죠. 단순 반복을 넘어, 어느 정도의 추론과 판단까지 가능한 자동화 말입니다.

    그래서 제가 홈랩에서 직접 실험해본 결과 알게 된 게 바로 LangChain AI 에이전트의 강력함이었어요. 처음엔 이게 뭔가 싶었는데, 막상 써보니까 AI가 스스로 판단해서 필요한 도구(Tool)를 찾아 쓰고, 심지어 이전 대화 맥락(Memory)까지 기억하면서 복잡한 작업을 척척 해내더라고요. 정말 놀랐습니다. 오늘 이 글에서는 저의 삽질 경험을 바탕으로, 여러분도 LangChain을 활용해 나만의 AI 에이전트를 구축하고 복잡한 작업을 자동화하는 방법을 실전 가이드 형식으로 알려드리려고 합니다. 우리 함께 AI 자동화의 세계로 떠나볼까요? 🎉

    LangChain AI 에이전트가 어떻게 동작하는지 한눈에 보여주는 개념도입니다. LLM을 중심으로 Tools, Memory, Agent Executor가 유기적으로 연결되어 복잡한 작업을 수행합니다.

    LangChain AI 에이전트, 도대체 뭘까요?

    쉽게 말해, LangChain AI 에이전트는 LLM(대규모 언어 모델)을 ‘두뇌’ 삼아 다양한 ‘도구’를 사용하고, ‘기억’까지 하면서 사람처럼 생각하고 행동하는 AI 시스템을 말해요. 기존 LLM이 단순히 주어진 질문에 답만 했다면, 에이전트는 한 발 더 나아가 스스로 계획을 세우고, 필요한 정보를 찾아오고, 외부 시스템과 상호작용하면서 목표를 달성하는 거죠. 이 모든 과정을 쉽게 구현할 수 있도록 도와주는 프레임워크가 바로 LangChain(랭체인)이에요.

    • Agent (에이전트): LLM이 어떤 도구를 사용할지, 어떤 순서로 사용할지 결정하는 ‘두뇌’ 역할을 합니다. 사용자의 요청을 이해하고, 목표 달성을 위한 최적의 경로를 탐색하죠.
    • Tools (도구): 에이전트가 외부 세계와 상호작용하는 수단이에요. 예를 들어, 인터넷 검색(Web Search), 계산기(Calculator), 외부 API 호출(API Call), 코드 실행(Code Interpreter) 등이 될 수 있어요. LangChain은 다양한 기본 도구를 제공하고, 커스텀 도구를 만들기도 정말 쉽습니다.
    • Memory (메모리): 에이전트가 이전 대화 내용을 기억해서 컨텍스트(Context)를 유지하는 역할이에요. 덕분에 여러 번의 질문과 답변 속에서도 일관성 있는 행동을 할 수 있습니다. 마치 사람처럼 대화를 기억하는 거죠.
    • LangChain (랭체인): 이런 에이전트를 쉽게 만들고, 구성하고, 배포할 수 있도록 도와주는 파이썬(Python) 라이브러리이자 프레임워크예요. 다양한 LLM과 도구를 연결하고, 복잡한 워크플로우를 체인(Chain) 형태로 만들 수 있게 해줍니다.

    결국 LangChain AI 에이전트는 마치 유능한 비서처럼, 우리가 시키는 복잡한 일들을 스스로 판단하고 여러 도구를 활용해서 처리해주는 시스템이라고 생각하시면 돼요. 진짜 편하더라고요!

    LangChain으로 나만의 AI 에이전트 만들기 실전!

    자, 이제 직접 코드를 만져보면서 LangChain AI 에이전트를 만들어볼 시간입니다. 저는 간단한 정보 검색 에이전트를 만들어 볼 건데요, 이 에이전트는 사용자의 질문을 받으면 인터넷을 검색해서 최신 정보를 찾아오는 역할을 할 겁니다.

    준비물 챙기기: 개발 환경 설정

    Python 3.8+ 환경이 필요해요. 먼저 필요한 라이브러리들을 설치해볼까요?

    pip install langchain langchain-openai langchain-community python-dotenv
    
    • langchain: LangChain 프레임워크의 핵심 라이브러리예요.
    • langchain-openai: OpenAI의 LLM을 사용하기 위한 통합 라이브러리입니다. (다른 LLM을 사용한다면 해당 프로바이더 라이브러리를 설치하세요.)
    • langchain-community: 검색 도구(Tool)와 같은 커뮤니티 기반의 유용한 기능들을 담고 있어요.
    • python-dotenv: 환경 변수를 .env 파일에서 로드하기 위해 사용합니다. API 키 등을 코드에 직접 노출하지 않고 안전하게 관리할 수 있어요.

    다음으로, OpenAI API 키를 설정해야 해요. 프로젝트 루트 폴더에 .env 파일을 만들고 아래 내용을 추가해주세요.

    OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
    

    YOUR_OPENAI_API_KEY 부분에 여러분의 OpenAI API 키를 입력하시면 돼요. ⚠️ 절대 이 파일을 GitHub 같은 공개 저장소에 올리시면 안 됩니다!

    첫 번째 에이전트: 간단한 정보 검색 에이전트

    이제 agent_app.py라는 파일을 만들고 코드를 작성해봅시다. 이 에이전트는 DuckDuckGo Search 도구를 사용해서 웹 검색을 수행할 겁니다.

    import os
    from dotenv import load_dotenv
    from langchain_openai import ChatOpenAI
    from langchain.agents import AgentExecutor, create_react_agent
    from langchain_community.tools import DuckDuckGoSearchRun
    from langchain import hub
    
    # .env 파일에서 환경 변수 로드
    load_dotenv()
    
    # 1. LLM(Large Language Model) 설정
    # gpt-3.5-turbo 모델을 사용하고, 창의성을 낮추기 위해 temperature는 0으로 설정했어요.
    llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
    
    # 2. 도구(Tools) 정의
    # 웹 검색을 위한 DuckDuckGoSearchRun 도구를 사용합니다.
    # 이 도구는 별도의 API 키 없이 바로 사용할 수 있어서 편리해요.
    search_tool = DuckDuckGoSearchRun()
    tools = [search_tool]
    
    # 3. 에이전트의 프롬프트(Prompt) 로드
    # LangChain Hub에서 ReAct(Reasoning and Acting) 프롬프트를 가져옵니다.
    # ReAct는 LLM이 추론(Reason)하고 행동(Act)하는 과정을 반복하며 목표를 달성하도록 돕는 강력한 패턴이에요.
    prompt = hub.pull("hwchase17/react")
    
    # 4. 에이전트 생성
    # create_react_agent 함수는 LLM, Tools, Prompt를 받아서 에이전트의 로직을 생성합니다.
    agent = create_react_agent(llm, tools, prompt)
    
    # 5. Agent Executor 생성 및 실행
    # AgentExecutor는 에이전트를 실행하고, 에이전트가 도구를 사용하는 과정을 관리합니다.
    # verbose=True로 설정하면 에이전트의 생각(Thought) 과정과 도구 사용 내역을 자세히 볼 수 있어요.
    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)
    
    # 에이전트 실행 예시
    print("\n--- 에이전트 실행 시작 ---")
    response = agent_executor.invoke({"input": "2024년 파리 올림픽에 대한 최신 정보를 알려주고, 한국 선수단에 대한 내용도 포함해줘."})
    print("\n--- 에이전트 최종 답변 ---")
    print(response["output"])
    print("\n--- 에이전트 실행 종료 ---")
    
    # 또 다른 질문
    print("\n--- 두 번째 질문 실행 ---")
    response = agent_executor.invoke({"input": "AI 에이전트 개발 시 가장 중요한 고려사항은 무엇일까요?"})
    print("\n--- 에이전트 최종 답변 ---")
    print(response["output"])
    print("\n--- 두 번째 질문 종료 ---")
    

    코드를 저장하고 실행해보세요.

    python agent_app.py
    

    verbose=True 덕분에 에이전트가 어떤 생각(Thought)을 하고, 어떤 도구(Tool)를 사용하며 정보를 찾아나가는지 자세히 볼 수 있을 겁니다. 제가 처음 이걸 봤을 때, “와, 진짜 AI가 생각하고 있네!” 싶더라고요. LLM이 단순히 답변만 하는 게 아니라, 스스로 계획하고 실행하는 모습을 보니 정말 신기했어요.

    직접 작성한 LangChain 에이전트 코드와 그 실행 결과 스크린샷입니다. 에이전트의 추론 과정이 자세히 출력되는 것을 볼 수 있습니다.

    삽질 경험: 저도 이걸로 꽤나 고생했습니다 ⚠️

    LangChain AI 에이전트가 만능처럼 보이지만, 저도 처음부터 술술 만들었던 건 아니었어요. 여러 삽질을 거치면서 배운 점들이 많더라고요. 여러분은 저 같은 시행착오를 겪지 않으시길 바라며 몇 가지 주의사항과 트러블슈팅 팁을 공유해봅니다.

    • API Rate Limit (API 호출 제한): LLM API는 호출 횟수나 토큰 수에 제한이 있어요. 에이전트가 무한정 검색하거나 반복 작업을 수행하면 금세 제한에 걸리곤 하더라고요. 저도 테스트하다가 갑자기 API 에러를 만나서 당황했던 적이 많았어요. 💡 팁: 개발 단계에서는 temperature를 낮춰서 불필요한 추론을 줄이거나, 프롬프트 설계를 통해 에이전트의 ‘생각’ 과정을 효율적으로 유도하는 것이 중요합니다. 그리고 비용 모니터링은 필수죠!

    • Tool Selection (도구 선택의 어려움): 에이전트에게 너무 많은 도구를 주거나, 역할에 맞지 않는 도구를 주면 엉뚱한 방향으로 흘러가는 경우가 있어요. 처음엔 에이전트가 “계산기” 도구가 있는데도 굳이 검색으로 답을 찾으려고 해서 답답했던 적도 있거든요. 💡 팁: 에이전트의 목적에 맞는 최소한의 도구만 제공하고, 각 도구의 description을 명확하게 작성해서 LLM이 언제 어떤 도구를 써야 할지 정확히 알 수 있도록 도와줘야 합니다.

    • Prompt Engineering (프롬프트 설계의 중요성): 에이전트의 성능은 프롬프트에 크게 좌우돼요. ReAct 프롬프트처럼 잘 설계된 프롬프트는 에이전트가 훨씬 효율적으로 작동하게 만들어요. 하지만 잘못된 지시나 모호한 프롬프트는 에이전트를 길 잃게 만들 수 있어요. 💡 팁: LangChain Hub에서 제공하는 검증된 프롬프트를 참고하고, 에이전트의 역할과 목표를 명확하게 지시하는 것이 중요합니다.

    • Memory Management (메모리 관리): 장기적인 대화에서는 메모리 관리가 중요해요. 너무 많은 대화 기록을 메모리에 담으면 토큰 제한에 걸리거나 불필요한 비용이 발생할 수 있어요. 💡 팁: ConversationBufferWindowMemory 같은 특정 길이만 기억하는 메모리 타입을 사용하거나, 요약(Summarization) 기능을 활용해서 필요한 핵심 정보만 기억하도록 하는 것이 좋습니다.

    • Parsing Errors (파싱 에러): LLM이 도구 사용 형식을 제대로 지키지 않아서 발생하는 에러예요. create_react_agent 함수에 handle_parsing_errors=True 옵션을 주면 에러를 좀 더 부드럽게 처리할 수 있어요. 저도 이 옵션 덕분에 스트레스를 많이 줄일 수 있었네요.

    드디어! 에이전트가 똑똑하게 일합니다 🎉

    위의 삽질과정을 거쳐 에이전트가 제대로 작동하는 모습을 보면 정말 뿌듯해요. 실제로 “2024년 파리 올림픽에 대한 최신 정보를 알려주고, 한국 선수단에 대한 내용도 포함해줘.” 같은 복잡한 질의를 던졌을 때, 에이전트는 다음과 같은 과정을 거쳐 답변을 생성합니다.

    1. 사용자의 질문을 이해하고, “파리 올림픽”과 “한국 선수단”이라는 키워드를 추출합니다.
    2. 인터넷 검색 도구(DuckDuckGo Search)를 사용하기로 결정합니다.
    3. “2024 파리 올림픽 최신 정보”를 검색합니다.
    4. 검색 결과에서 주요 정보를 추출하고, 다시 “2024 파리 올림픽 한국 선수단”을 검색합니다.
    5. 두 가지 검색 결과를 종합하여 사용자에게 최신 정보와 한국 선수단 관련 내용을 포함한 답변을 제공합니다.

    제가 기대했던 것 이상으로 잘 해내더라고요. 특히 여러 단계에 걸쳐 정보를 수집하고 조합하는 능력은 기존 LLM 단독으로는 어려웠던 부분이라 더욱 인상 깊었어요. 이런 방식으로 단순 정보 검색을 넘어, 특정 문서 요약, 데이터 분석, 심지어는 간단한 코드 생성까지 다양한 작업을 자동화할 수 있습니다.

    LangChain 에이전트가 여러 도구를 활용하여 복합적인 질문에 답하는 모습입니다. 마치 사람이 생각하듯 정보를 수집하고 가공하여 최종 답변을 도출합니다.

    마무리하며: AI 자동화, 이제 시작입니다.

    오늘은 LangChain을 활용해서 AI 에이전트를 구축하고 복잡한 작업을 자동화하는 방법에 대해 저의 경험을 바탕으로 이야기해봤어요. 13년차 인프라 엔지니어로서 늘 어떻게 하면 더 효율적으로 일할 수 있을까 고민했는데, LangChain AI 에이전트가 그 해답 중 하나가 될 수 있다는 확신을 얻었어요. 여러분도 저처럼 홈랩에서 직접 만들어보시면 좋겠네요. 직접 코드를 만져보고 에이전트가 ‘생각’하는 과정을 지켜보는 것만으로도 정말 값진 경험이 될 겁니다.

    LangChain은 AI 에이전트 개발을 위한 강력하고 유연한 프레임워크예요. 오늘 다룬 내용은 아주 기초적인 시작에 불과해요. 앞으로는 커스텀 도구를 만들어서 사내 시스템과 연동하거나, 더 복잡한 추론 체인(Chain)을 구성해서 고도화된 자동화 시스템을 구축할 수도 있어요. AI 자동화의 시대는 이제 막 시작되었고, 그 가능성은 무궁무진합니다.

    다음 글에서는 더 복잡한 멀티모달(Multimodal) 에이전트나, 에이전트 간의 협업(Agent Collaboration)을 통해 더욱 강력한 자동화 시스템을 만드는 방법에 대해 다뤄볼 예정입니다. 기대해주세요! 궁금한 점이나 여러분의 삽질 경험이 있다면 댓글로 공유해주세요. 우리 함께 배우고 성장해나가요. 😊

    LangChain 에이전트의 핵심 기능과 다양한 활용 방안을 시각적으로 정리한 요약본입니다. 복잡한 작업 자동화를 위한 강력한 도구임을 보여줍니다.

  • [Proxmox] Proxmox 네트워크 설정: 브릿지, VLAN, 본딩 완벽 가이드

    [Proxmox] Proxmox 네트워크 설정: 브릿지, VLAN, 본딩 완벽 가이드

    안녕하세요, 13년차의 서버실 주인장입니다. 홈랩을 운영하면서 다양한 가상화 환경을 구축하고 테스트해보곤 하는데요. 그중에서도 Proxmox VE(Virtual Environment)는 제가 가장 즐겨 사용하는 하이퍼바이저(Hypervisor) 중 하나입니다.

    Proxmox VE는 뛰어난 성능과 유연성으로 많은 서버실 엔지니어들의 사랑을 받고 있죠. 하지만 Proxmox VE를 처음 접하는 분들이 가장 어려워하는 부분이 바로 네트워크 설정이 아닐까 싶습니다. 브릿지(Bridge)는 또 뭐고, VLAN(Virtual Local Area Network)은 어떻게 적용하며, 본딩(Bonding)은 왜 필요한지… 저도 처음엔 이게 뭔가 싶어서 꽤 삽질했었거든요. ㅎㅎ

    오늘은 제가 지난 13년간 인프라 엔지니어로서 쌓아온 경험과 홈랩에서 직접 Proxmox VE를 만지면서 얻은 노하우를 바탕으로, Proxmox 네트워크 설정의 핵심인 브릿지, VLAN, 본딩을 완벽하게 마스터할 수 있는 가이드를 준비했습니다. 이 글을 통해 여러분의 Proxmox VE 환경이 더욱 안정적이고 효율적으로 변모할 수 있기를 바랍니다!

    Proxmox VE 네트워크의 핵심 구성 요소들을 한눈에 볼 수 있는 다이어그램입니다. 브릿지, VLAN, 본딩이 물리 네트워크와 어떻게 연결되는지 시각적으로 보여줍니다.

    Proxmox 네트워크 설정, 왜 이렇게 복잡해 보일까요? (브릿지, VLAN, 본딩 개념 이해)

    본격적인 설정에 앞서, 우리가 다룰 세 가지 핵심 개념을 먼저 명확히 이해하고 넘어가는 것이 중요합니다. 이 개념들을 잘 알아두면 나중에 트러블슈팅할 때도 훨씬 수월할 거예요.

    1. 브릿지(Bridge): 가상 스위치의 시작

    Proxmox VE에서 브릿지(Bridge)는 물리 네트워크 인터페이스와 가상 머신(VM) 및 컨테이너(LXC)를 연결해주는 가상의 스위치(Switch) 역할을 합니다. 쉽게 말해, 물리 서버 안에 소프트웨어적으로 스위치를 하나 더 만들어서 여러 가상 머신들이 그 스위치에 연결되도록 하는 거죠.

    • vmbr0: Proxmox VE를 설치하면 기본적으로 생성되는 브릿지입니다. 보통 물리 이더넷 카드(예: eno1)와 연결되어 외부 네트워크와 통신하는 역할을 합니다.
    • 역할: 여러 가상 머신들이 하나의 물리 네트워크 인터페이스를 공유하며, 마치 같은 물리 스위치에 연결된 것처럼 서로 통신할 수 있게 해줍니다.

    2. VLAN(Virtual Local Area Network): 네트워크 분리의 마법

    VLAN(Virtual Local Area Network, 가상 근거리 통신망)은 하나의 물리 네트워크 스위치를 여러 개의 논리적인 네트워크로 분리하는 기술입니다. 보안, 성능, 관리 효율성 등 여러 이유로 네트워크를 분리하고 싶을 때 사용하죠. Proxmox VE에서는 가상 머신이나 컨테이너에 특정 VLAN ID를 할당하여 해당 VLAN에 속한 네트워크 트래픽만 주고받도록 설정할 수 있습니다.

    • 태깅(Tagging): 이더넷 프레임에 VLAN ID를 추가하여 어떤 VLAN에 속하는 트래픽인지 식별합니다. IEEE 802.1Q 표준을 따릅니다.
    • 활용: 웹 서버용 VLAN, DB 서버용 VLAN, 관리용 VLAN 등 용도에 따라 네트워크를 분리하여 보안을 강화하고 트래픽 혼잡을 줄일 수 있습니다.

    3. 본딩(Bonding) 또는 티밍(Teaming): 안정성과 성능 두 마리 토끼

    본딩(Bonding)은 여러 개의 물리 네트워크 인터페이스를 하나의 논리적인 인터페이스로 묶는 기술입니다. 윈도우 서버에서는 티밍(Teaming)이라고도 부르죠. 이 기술을 사용하면 두 가지 주요 이점을 얻을 수 있습니다.

    • 고가용성(High Availability, HA): 하나의 물리 NIC(Network Interface Card)에 장애가 발생하더라도 다른 NIC를 통해 네트워크 연결이 유지됩니다. (Failover)
    • 성능 향상: 여러 NIC의 대역폭을 합쳐 더 높은 처리량을 얻을 수 있습니다. (Load Balancing)

    본딩 모드에는 Active-Backup, LACP(Link Aggregation Control Protocol) 등 여러 가지가 있는데, 이 부분은 실전 가이드에서 자세히 다뤄볼게요. 저도 처음엔 아무 모드나 쓰면 되는 줄 알았는데, 스위치 설정과 연동이 안 돼서 한참 헤매던 기억이 있네요. 😅

    Proxmox 네트워크 설정 실전 가이드: 한 단계씩 따라 해봐요!

    이제 개념을 알았으니 직접 Proxmox VE에 네트워크 설정을 해볼 시간입니다. 저는 주로 CLI(Command Line Interface)를 선호하지만, Proxmox 웹 UI(User Interface)도 함께 보여드리면서 설명해 드릴게요. 여러분의 환경에 맞춰 선택하시면 됩니다.

    1단계: 네트워크 인터페이스 확인

    가장 먼저 현재 Proxmox 서버에 어떤 물리 네트워크 인터페이스가 있는지 확인해야 합니다. SSH로 Proxmox 서버에 접속하여 다음 명령어를 입력해 보세요.

    ip a
    

    보통 eno1, enpXsY, eth0 등과 같은 이름으로 표시될 겁니다. 제 홈랩 서버에는 eno1과 eno2 두 개의 기가비트 이더넷 포트가 있네요. 여러분의 서버에 맞는 인터페이스 이름을 확인해 두세요.

    2단계: 브릿지(Bridge) 설정하기

    기본적으로 Proxmox는 vmbr0이라는 브릿지를 하나 가지고 있습니다. 여기에 추가로 브릿지를 생성하거나, 기존 브릿지에 물리 NIC를 연결해볼게요.

    웹 UI를 이용한 설정

    1. Proxmox 웹 UI에 접속합니다. (https://[Proxmox_IP]:8006)
    2. 왼쪽 메뉴에서 Datacenter -> [노드 이름] -> System -> Network로 이동합니다.
    3. ‘Create’ -> ‘Linux Bridge’를 선택합니다.
    4. 설정 창에서 다음을 입력합니다:

      • Name: vmbr1 (새로운 브릿지 이름. vmbr0은 기본이니 vmbr1부터 시작하는 게 좋습니다.)
      • IPv4/CIDR: 비워둠 (브릿지에 직접 IP를 할당하지 않고, 가상 머신에서 IP를 받도록 할 경우) 또는 192.168.100.1/24 (브릿지에 IP를 할당하여 호스트에서 직접 해당 네트워크에 접근할 경우)
      • Bridge ports: eno2 (브릿지에 연결할 물리 네트워크 인터페이스 이름. 여러 개라면 공백으로 구분)
      • VLAN aware: ✅ 체크 (VLAN 기능을 사용할 예정이라면 반드시 체크해야 합니다!)
    5. ‘Create’ 버튼을 클릭하고, 상단의 ‘Apply Configuration’을 클릭하여 변경 사항을 적용합니다.

    CLI를 이용한 설정

    /etc/network/interfaces 파일을 직접 편집하여 설정할 수 있습니다. 저는 이 방법이 더 익숙하더라고요.

    sudo nano /etc/network/interfaces
    

    파일 내용은 대략 다음과 같을 겁니다. 기존 vmbr0 설정 아래에 vmbr1을 추가해볼게요.

    auto lo
    iface lo inet loopback
    
    iface eno1 inet manual
    # Proxmox 관리용 브릿지 (기본)
    auto vmbr0
    iface vmbr0 inet static
        address 192.168.1.10/24
        gateway 192.168.1.1
        bridge-ports eno1
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes # VLAN 사용 시 필수!
        #bridge-vids 20-30 # 특정 VLAN ID 범위 허용 (옵션)
    
    iface eno2 inet manual
    # 추가적인 브릿지 (데이터용 또는 VLAN 분리용)
    auto vmbr1
    iface vmbr1 inet manual # vmbr1 자체에는 IP를 할당하지 않고, VM이 직접 IP를 가져가도록
        bridge-ports eno2
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes # VLAN 사용 시 필수!
        #bridge-vids 100-200 # 특정 VLAN ID 범위 허용 (옵션)
    

    변경 후에는 네트워크 서비스를 재시작해야 하지만, Proxmox는 시스템 재부팅을 권장합니다. 특히 중요한 서버라면 Proxmox 웹 UI에서 ‘Apply Configuration’ 버튼을 누르거나, reboot 명령어를 사용하는 것이 가장 안전합니다.

    Proxmox 웹 UI에서 브릿지, VLAN, 본딩 설정을 마치고 ‘Apply Configuration’을 누르기 전의 화면입니다. 설정된 내용들을 한눈에 확인할 수 있습니다.

    3단계: VLAN(Virtual Local Area Network) 설정하기

    이제 vmbr1에 VLAN-aware 옵션을 활성화했으니, 이 브릿지를 사용하는 가상 머신에 VLAN ID를 할당해볼게요.

    가상 머신에 VLAN ID 할당 (웹 UI)

    1. 네트워크 설정을 적용할 가상 머신(VM) 또는 컨테이너(LXC)를 선택합니다.
    2. ‘Hardware’ 탭으로 이동합니다.
    3. 네트워크 장치(예: Network Device (net0))를 더블클릭합니다.
    4. 설정 창에서 다음을 확인/입력합니다:

      • Bridge: vmbr1 (앞서 생성한 VLAN-aware 브릿지)
      • VLAN Tag: 100 (할당하고 싶은 VLAN ID)
    5. ‘OK’를 클릭하고 VM을 재시작합니다.

    이렇게 설정하면 해당 VM은 vmbr1 브릿지를 통해 물리 NIC eno2로 나가되, 트래픽에 VLAN ID 100이 태깅되어 나갑니다. 물론, 이 VLAN 100을 인식하고 라우팅해줄 상위 네트워크 스위치도 그에 맞게 설정되어 있어야겠죠!

    4단계: 본딩(Bonding) 설정으로 안정성 확보하기

    여러 개의 물리 NIC를 묶어 본딩을 구성해볼게요. 저는 eno1과 eno2를 묶어 bond0을 만들고, 이 bond0을 vmbr0에 연결하여 Proxmox 관리 네트워크의 안정성을 높여보겠습니다.

    CLI를 이용한 본딩 설정

    sudo nano /etc/network/interfaces
    

    기존 eno1, eno2, vmbr0 설정을 수정하고 bond0을 추가합니다.

    auto lo
    iface lo inet loopback
    
    # 물리 NIC들은 manual로 설정하고 bond0에 포함시킵니다.
    iface eno1 inet manual
    iface eno2 inet manual
    
    # bond0 인터페이스 설정
    auto bond0
    iface bond0 inet manual
        bond-slaves eno1 eno2
        bond-miimon 100
        bond-mode active-backup # 가장 일반적이고 안정적인 모드 (Failover)
        # bond-mode 802.3ad # LACP (스위치에서 Link Aggregation 설정 필수)
        # bond-xmit-hash-policy layer2+3 # LACP 사용 시 권장
        
    # vmbr0 브릿지를 bond0에 연결합니다.
    auto vmbr0
    iface vmbr0 inet static
        address 192.168.1.10/24
        gateway 192.168.1.1
        bridge-ports bond0 # 물리 NIC 대신 bond0을 연결
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
    

    여기서 bond-mode가 중요한데요. 저는 주로 active-backup 모드를 사용합니다. 하나의 NIC가 Active로 작동하고, 다른 NIC는 Passive로 대기하다가 Active NIC에 문제가 생기면 자동으로 Passive NIC가 Active로 전환되는 방식이죠. 스위치 설정 변경 없이 Proxmox 서버 단독으로 구성할 수 있어 홈랩 환경에서 매우 편리합니다.

    만약 더 높은 성능을 원하고 스위치에서 LACP(Link Aggregation Control Protocol)를 지원한다면, 802.3ad 모드를 사용하고 스위치에서도 해당 포트들을 LACP 그룹으로 묶어줘야 합니다. 이 경우 bond-xmit-hash-policy 설정도 중요하구요. 이 부분에서 스위치 설정과 Proxmox 본딩 모드가 일치하지 않아 통신이 안 되는 삽질을 꽤 많이 했었습니다. ⚠️

    설정 변경 후에는 마찬가지로 Proxmox 웹 UI에서 ‘Apply Configuration’을 누르거나, 서버를 재부팅하여 변경 사항을 적용합니다.

    ⚠️ 삽질 대잔치! Proxmox 네트워크 설정 시 제가 겪었던 문제들

    네트워크 설정은 한 글자만 틀려도 통신이 안 되는 경우가 많아서 정말 골치 아프죠. 저도 수많은 삽질을 통해 깨달은 것들이 많습니다. 여러분은 저 같은 실수를 겪지 않으시길 바라며, 몇 가지 주의사항과 트러블슈팅 팁을 공유합니다.

    1. 잘못된 IP 설정으로 관리 인터페이스 접근 불가

    Proxmox 웹 UI에 접속하는 IP 주소(보통 vmbr0에 할당된 IP)를 잘못 설정하거나, 네트워크 서비스를 재시작했는데 IP가 적용이 안 되는 경우가 있습니다. 이럴 땐 정말 등골이 오싹해지죠. 😱

    • 해결책: 물리 서버에 직접 모니터와 키보드를 연결하여 콘솔에 접속합니다. ip a 명령어로 현재 IP 주소를 확인하고, /etc/network/interfaces 파일을 다시 확인하여 수정합니다. 변경 후에는 systemctl restart networking 명령어를 시도하거나, 안전하게 reboot 합니다.

    2. VLAN 태그 누락 또는 오설정

    분명 VLAN ID를 넣었는데 가상 머신에서 통신이 안 된다면 다음을 확인해 보세요.

    • Proxmox 브릿지에 bridge-vlan-aware yes 설정이 되어 있는지? 이 옵션이 없으면 VLAN 태그를 무시합니다.
    • 가상 머신 네트워크 설정에 VLAN Tag가 정확히 입력되었는지?
    • 물리 스위치 포트 설정: Proxmox 서버가 연결된 스위치 포트가 트렁크(Trunk) 모드로 설정되어 있고, 필요한 VLAN ID들이 모두 허용(Permit)되어 있는지 확인해야 합니다. 제가 초보 때 가장 많이 놓쳤던 부분입니다. 스위치 설정과 Proxmox 설정이 일치해야만 VLAN이 정상 작동합니다.

    3. 본딩 모드 선택의 중요성

    본딩 모드(bond-mode)를 잘못 선택하면 성능 저하는 물론, 아예 통신이 안 될 수도 있습니다.

    • active-backup: 가장 안전한 모드. 스위치 설정이 필요 없습니다. 단순 이중화 목적이라면 이 모드를 추천합니다.
    • 802.3ad (LACP): 성능 향상과 이중화를 동시에 얻을 수 있지만, 반드시 스위치에서도 해당 포트들을 LACP 그룹으로 묶어줘야 합니다. 스위치 설정이 없다면 통신이 안 되거나 불안정해질 수 있습니다. 스위치 매뉴얼을 꼼꼼히 확인하세요.

    4. 스위치 설정과의 연동

    가장 중요하면서도 간과하기 쉬운 부분입니다. Proxmox 서버의 네트워크 설정은 단독으로 작동하는 것이 아니라, 연결된 물리 스위치와 유기적으로 연동되어야 합니다. 특히 VLAN이나 LACP 본딩을 사용할 때는 스위치의 포트 설정(Access/Trunk 모드, 허용 VLAN, LACP 그룹)이 Proxmox 설정과 정확히 일치하는지 반드시 확인해야 합니다. 저는 이 부분 때문에 밤을 새워가며 삽질했던 경험이 정말 많습니다. 😅

    설정 완료! 제대로 작동하는지 확인해볼까요?

    모든 설정을 마치셨다면, 이제 정상적으로 작동하는지 확인해볼 차례입니다. “드디어 됐다!” 하는 희열을 느낄 수 있는 순간이죠. 🎉

    1. Proxmox 웹 UI 확인: Datacenter -> [노드 이름] -> System -> Network 탭에서 설정한 브릿지, 본딩 인터페이스가 정상적으로 올라와 있는지 확인합니다. 상태가 ‘active’여야 합니다.
    2. 가상 머신 통신 테스트: VLAN을 할당한 가상 머신에서 해당 VLAN의 게이트웨이나 다른 서버로 ping 테스트를 해봅니다.
    3. 본딩 Failover 테스트 (Active-Backup 모드): 본딩된 물리 NIC 중 하나를 케이블에서 뽑아봅니다. 잠시 후에도 Proxmox 서버가 네트워크에 연결되어 있고, 가상 머신 통신도 정상이라면 본딩이 잘 작동하는 것입니다. 물론 테스트 후에는 다시 케이블을 연결해야겠죠! 💡

    Proxmox 가상 머신에 VLAN을 설정하고, 해당 VLAN 네트워크 내에서 성공적으로 핑 테스트를 수행한 결과 화면입니다. 네트워크가 정상적으로 작동함을 보여줍니다.

    13년차 서버실의 마무리: Proxmox 네트워크 설정, 이제 두렵지 않아요!

    오늘은 Proxmox VE의 핵심인 브릿지, VLAN, 본딩에 대해 깊이 있게 다뤄봤습니다. 개념부터 실전 설정, 그리고 제가 직접 겪었던 삽질 경험과 해결책까지 솔직하게 공유해 드렸는데요.

    처음에는 복잡하게 느껴질 수 있지만, 몇 번 직접 해보고 트러블슈팅을 겪다 보면 금방 익숙해지실 겁니다. 특히 네트워크는 이론만으로는 부족하고, 직접 만져보고 겪어봐야 진짜 내 것이 되더라고요.

    Proxmox VE의 네트워크 설정은 여러분의 가상화 환경을 더욱 유연하고 안정적으로 만들어줄 것입니다. 이 글이 Proxmox VE를 사용하시는 모든 분들께 좋은 멘토가 되었으면 좋겠습니다. 다음번에는 Proxmox VE의 스토리지 설정이나 고가용성 클러스터 구축에 대한 경험담도 풀어볼까 합니다. 기대해 주세요!

    Proxmox 네트워크의 브릿지, VLAN, 본딩 개념과 주요 특징, 설정 팁을 요약하여 시각적으로 보여주는 인포그래픽입니다.

  • [Proxmox] Proxmox GPU 패스스루 완벽 가이드: 가상머신에서 그래픽카드 활용하기

    [Proxmox] Proxmox GPU 패스스루 완벽 가이드: 가상머신에서 그래픽카드 활용하기

    Proxmox GPU 패스스루 완벽 가이드: 가상머신에서 그래픽카드 활용하기

    안녕하세요, 13년차 서버실 지킴이 ’13년차의 서버실’입니다. 오늘은 많은 분들이 홈랩에서 꿈꾸는 로망 중 하나인 Proxmox GPU 패스스루에 대한 이야기를 해볼까 합니다. 하나의 강력한 서버로 여러 가상머신(VM)을 돌리면서, 특정 가상머신에 고성능 그래픽카드(GPU)를 통째로 할당해주는 기술이죠. 저도 처음엔 ‘이게 가능하다고?’ 싶었는데, 실제로 해보니 그 활용도가 무궁무진하더라고요. 게이밍 VM부터 AI/딥러닝 워크스테이션, 그리고 미디어 트랜스코딩 서버까지, 여러분의 상상력을 현실로 만들어줄 마법 같은 기술입니다. 저도 수많은 삽질 끝에 성공했고, 그 과정에서 얻은 소중한 경험과 팁들을 오늘 이 자리에서 아낌없이 풀어놓으려 합니다.

    혹시 여러분도 저처럼 비싼 장비 여러 대를 들이지 않고, 하나의 서버로 모든 것을 해결하고 싶은 꿈을 꾸고 계신가요? 그렇다면 이 글이 여러분의 길잡이가 되어줄 겁니다. 제가 직접 해보니 얻는 게 정말 많더라고요. 그럼, 함께 Proxmox GPU 패스스루의 세계로 떠나볼까요?

    Proxmox VE 환경에서 호스트 서버에 설치된 GPU가 가상머신으로 직접 연결되는 개념을 시각적으로 보여주는 다이어그램입니다.

    2. Proxmox GPU 패스스루, 대체 뭘까요? (개념 설명)

    Proxmox GPU 패스스루(GPU Passthrough)는 말 그대로 호스트 시스템(Proxmox VE)에 장착된 물리적인 그래픽카드를 특정 가상머신에 ‘통째로’ 넘겨주는 기술을 의미합니다. 보통 가상머신은 에뮬레이션된 가상 그래픽 장치를 사용하기 때문에 성능이 제한적이죠. 하지만 패스스루를 사용하면 가상머신이 마치 물리적인 컴퓨터에 그래픽카드가 직접 꽂혀있는 것처럼 GPU의 모든 성능을 활용할 수 있게 됩니다.

    이 기술의 핵심에는 IOMMU (Input/Output Memory Management Unit)라는 하드웨어 기능이 있습니다. IOMMU는 가상머신이 물리적인 장치에 직접 접근할 수 있도록 메모리 주소를 매핑해주는 역할을 해요. 그리고 리눅스 커널의 VFIO (Virtual Function I/O) 드라이버가 이 IOMMU 기능을 활용해서 PCI 장치(GPU 포함)를 가상머신으로 패스스루할 수 있도록 도와줍니다. 쉽게 말해, Proxmox VE 호스트가 GPU에 대한 통제권을 내려놓고, 그 통제권을 특정 가상머신에게 완전히 이양하는 것이라고 보시면 됩니다. 그래서 **Proxmox VE 그래픽카드**를 가상머신에서 완벽하게 활용할 수 있게 되는 거죠. 저도 처음엔 개념이 좀 어려웠는데, 직접 해보면서 ‘아, 이게 이런 원리로 돌아가는구나!’ 하고 깨달았어요.

    3. 실전 구현: Proxmox에서 GPU 패스스루 설정하기

    자, 이제 이론은 충분합니다. 저와 함께 실제로 Proxmox VE에 **가상머신 GPU** 패스스루를 설정해보는 시간을 가져볼까요? 제가 직접 해본 가장 안정적인 방법들을 단계별로 알려드릴게요. 저도 이 과정에서 수많은 시행착오를 겪었거든요. 하나하나 따라오시면 분명 성공하실 수 있을 겁니다! 💡

    1. BIOS/UEFI 설정 확인 및 활성화

      가장 먼저 할 일은 서버의 BIOS/UEFI에서 IOMMU 기능을 활성화하는 것입니다. AMD 시스템에서는 AMD-Vi, Intel 시스템에서는 Intel VT-d라는 이름으로 되어있을 거예요. 이 옵션이 꺼져있으면 GPU 패스스루는 아예 불가능합니다. 저도 처음에 이거 확인 안 했다가 시간 좀 날렸습니다. ⚠️

    2. Proxmox VE 호스트 설정: IOMMU 활성화 및 VFIO 모듈 로드

      Proxmox VE 호스트에서 IOMMU 기능을 커널에 알려주고, VFIO 모듈을 미리 로드해야 합니다. SSH로 접속해서 다음 명령어를 입력하세요.

      # GRUB 설정 파일 수정
      # Intel CPU의 경우
      sudo nano /etc/default/grub
      # GRUB_CMDLINE_LINUX_DEFAULT="quiet" 부분을 찾아 다음과 같이 수정합니다.
      # GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on iommu=pt"
      
      # AMD CPU의 경우
      # GRUB_CMDLINE_LINUX_DEFAULT="quiet amd_iommu=on iommu=pt"
      
      # 수정 후 GRUB 업데이트
      sudo update-grub
      
      # VFIO 모듈 로드 설정
      sudo nano /etc/modules
      # 다음 내용을 추가합니다.
      vfio
      vfio_iommu_type1
      vfio_pci
      vfio_virqfd
      
      # blacklist nouveau 드라이버 (NVIDIA GPU 사용 시 필수)
      sudo nano /etc/modprobe.d/blacklist.conf
      # 다음 내용을 추가합니다.
      blacklist nouveau
      options nouveau modeset=0
      
      # GPU 사운드 카드 드라이버 blacklist (HDMI/DisplayPort 오디오 사용 시 충돌 방지)
      sudo nano /etc/modprobe.d/pve-blacklist.conf
      # 다음 내용을 추가합니다.
      blacklist snd_hda_intel
      blacklist snd_hda_codec_hdmi
      blacklist i915 # Intel 내장 그래픽 사용 시 필요
      
      # 초기 램디스크(initramfs) 업데이트 (매우 중요!)
      sudo update-initramfs -u -a
      
      # 재부팅
      sudo reboot

      재부팅 후 `dmesg | grep -i iommu` 명령으로 IOMMU가 성공적으로 활성화되었는지 확인하세요. `DMAR: IOMMU enabled` 같은 메시지가 보이면 성공입니다. 👍

    3. GPU PCI ID 확인 및 VFIO에 바인딩

      이제 여러분의 GPU PCI ID를 확인하고, 이 ID를 VFIO 드라이버에 할당해야 합니다.

      # GPU PCI ID 확인 (VGA compatible controller와 Audio device 부분을 잘 보세요)
      # 예시: 01:00.0 VGA compatible controller: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti] (rev a1)
      #       01:00.1 Audio device: NVIDIA Corporation GP107GL High Definition Audio Controller (rev a1)
      sudo lspci -nnk | grep -i vga -A3
      sudo lspci -nnk | grep -i audio -A3
      
      # 위에 나온 벤더:디바이스 ID를 기록합니다. (예: 10de:1c8c, 10de:0fb9)
      
      # VFIO에 바인딩할 PCI ID 설정
      sudo nano /etc/modprobe.d/vfio.conf
      # 다음 내용을 추가합니다. (여러분의 PCI ID로 변경하세요!)
      options vfio-pci ids=10de:1c8c,10de:0fb9 disable_vga=1

      다시 `sudo update-initramfs -u -a` 명령으로 초기 램디스크를 업데이트하고, 재부팅합니다. 재부팅 후 `lspci -nnk | grep -i vga -A3` 명령으로 GPU가 `Kernel driver in use: vfio-pci`로 바인딩되었는지 확인하세요. 이게 제일 중요합니다! ✅

    4. 가상머신(VM) 설정

      이제 Proxmox VE 웹 인터페이스에서 GPU를 패스스루할 가상머신을 선택하고, 하드웨어 설정에 들어갑니다. 저도 이 화면에서 얼마나 많은 시도를 했는지 몰라요. ㅎㅎ

      1. 가상머신을 생성하거나 선택합니다. (Windows VM이 일반적입니다)
      2. 하드웨어 탭에서 Add > PCI Device를 클릭합니다.
      3. 이전에 확인했던 GPU의 PCI 장치(VGA compatible controller와 Audio device)를 각각 추가합니다.
      4. 옵션에서 Primary GPU, All Functions, PCI-Express(가능하다면)를 선택합니다.
      5. RomBar를 체크 해제하고, Advanced > PCI-Express를 활성화하는 것이 좋습니다.

    Proxmox VE 웹 인터페이스에서 가상머신의 ‘하드웨어’ 탭에서 ‘PCI Device’를 추가하는 과정과 설정 옵션들을 보여주는 스크린샷입니다.

    4. ⚠️ 삽질 대잔치! Proxmox GPU 패스스루 트러블슈팅 노하우

    솔직히 Proxmox GPU 패스스루는 한 번에 성공하기 쉽지 않습니다. 저도 수많은 밤을 새워가며 삽질 좀 했습니다. 특히 **Proxmox VE 그래픽카드**를 패스스루할 때 발생하는 몇 가지 고질적인 문제들이 있어요. 제가 겪었던 대표적인 문제들과 해결책을 공유해 드릴게요.

    • IOMMU 그룹 문제

      가장 흔한 문제입니다. GPU와 다른 장치들이 같은 IOMMU 그룹에 묶여 있으면, GPU만 단독으로 패스스루할 수 없습니다. `for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d##*/}; dev=$(lspci -nns $n); echo “IOMMU Group ${d%/devices/*##*/}”; echo ” $dev”; done` 명령으로 IOMMU 그룹을 확인해 보세요. 만약 GPU와 다른 장치가 같은 그룹에 있다면, BIOS/UEFI에서 PCI-E 슬롯별 IOMMU 분리가 가능한지 확인하거나, ACS Override 패치를 적용하는 방법도 있지만, 이는 커널을 수정해야 하는 고급 과정이라 초보자에게는 권장하지 않습니다. 저는 이 문제 때문에 메인보드까지 바꿀 뻔했습니다. 😭

    • NVIDIA Error 43

      Windows 가상머신에서 NVIDIA 드라이버를 설치했을 때 ‘코드 43’ 오류가 발생하는 경우가 많습니다. NVIDIA가 가상화 환경에서의 GPU 사용을 제한하려는 조치 때문인데요. 이 문제는 VM 설정에 `args: -cpu host,kvm=off,hv_vendor_id=null`을 추가하거나, 그래픽카드 펌웨어(ROM) 파일을 추출하여 `vga: all,romfile=/path/to/gpu_rom.bin` 옵션을 사용하는 방법으로 해결할 수 있습니다. 저는 `args` 옵션으로 해결했어요. 이거 하나 해결하고 얼마나 기뻤는지 모릅니다! 🎉

      # VM 설정 파일 수정 (VMID를 여러분의 VMID로 변경하세요)
      sudo nano /etc/pve/qemu-server/YOUR_VMID.conf
      # 맨 아래에 다음 줄을 추가합니다.
      args: -cpu host,kvm=off,hv_vendor_id=null
    • HDMI/DisplayPort 오디오 문제

      GPU 패스스루 후 HDMI나 DisplayPort를 통한 오디오 출력이 안 되는 경우가 있습니다. 이는 GPU의 오디오 컨트롤러가 제대로 패스스루되지 않았거나, 호스트의 사운드 드라이버와 충돌하기 때문인데요. 앞서 `pve-blacklist.conf`에 `snd_hda_intel` 등을 blacklist 하는 것으로 대부분 해결됩니다. 저도 이 때문에 한참을 헤맸는데, 간단한 설정으로 해결될 때의 쾌감이란! 😆

    • VM 부팅 시 블랙 스크린

      VM 부팅 시 화면이 아예 안 나오는 경우가 있습니다. 이는 주로 `vga` 옵션 설정 문제입니다. VM 설정에서 `Display`를 `none`으로 설정하고, `vga` 옵션에서 `all` 대신 `qxl` 또는 `virtio`를 시도해보세요. 그리고 패스스루한 GPU를 `Primary GPU`로 설정하는 것도 잊지 마세요.

    5. 드디어 성공! Proxmox 게이밍 VM (검증 및 결과)

    이 모든 삽질과 노력을 거쳐 드디어 **Proxmox 게이밍 VM**이 제 기능을 하는 순간을 맞이했습니다! 가상머신에 Windows를 설치하고, GPU 드라이버를 설치한 다음 장치 관리자를 열어보세요. 여러분의 물리적인 그래픽카드가 ‘디스플레이 어댑터’ 목록에 제대로 인식되어 있다면 성공입니다! ✅

    저는 Windows 10 VM에 NVIDIA RTX 3070을 패스스루해서 게임을 돌려봤는데, 놀랍게도 물리적인 PC에서 돌리는 것과 거의 차이 없는 성능을 보여주더라고요. 벤치마크 점수도 만족스러웠고요. 3DMark 같은 툴로 테스트해봐도 점수가 잘 나옵니다. 이 감동은 직접 경험해보셔야 알 수 있습니다. 드디어 하나의 서버로 게임도 하고, 서버도 돌리는 진정한 홈랩을 구축한 거죠! 😎

    Windows 가상머신 내부의 장치 관리자 화면으로, 패스스루된 NVIDIA 그래픽카드가 ‘디스플레이 어댑터’ 목록에 오류 없이 정상적으로 인식된 상태를 보여줍니다.

    6. Proxmox GPU 패스스루, 그래서 뭐가 좋을까요? (장단점)

    이렇게 힘들게 Proxmox GPU 패스스루를 설정했는데, 과연 어떤 장점과 단점이 있을까요? 제가 직접 경험해본 바를 바탕으로 정리해봤습니다.

    장점 (Pros) 단점 (Cons)
    고성능 그래픽 작업 가능: 가상머신에서 게임, 딥러닝, 영상 편집 등 GPU 자원이 필요한 작업을 원활하게 수행할 수 있습니다. 복잡한 설정 과정: 초기 설정이 매우 복잡하고, 트러블슈팅에 많은 시간과 지식이 필요합니다.
    하드웨어 통합 및 비용 절감: 여러 대의 물리적인 PC 대신 하나의 강력한 서버로 다양한 용도의 VM을 운영할 수 있어 공간과 비용을 절약할 수 있습니다. 호환성 문제: 모든 메인보드, CPU, GPU 조합이 완벽하게 호환되는 것은 아닙니다. 특히 IOMMU 그룹 문제가 발생하기 쉽습니다.
    유연한 자원 할당: 필요에 따라 GPU를 다른 VM으로 할당 변경하거나, 여러 GPU를 각각 다른 VM에 할당할 수 있습니다. 호스트 자원 제약: GPU 패스스루를 사용하면 호스트 시스템은 해당 GPU를 사용할 수 없으며, 호스트의 그래픽 출력이 제한될 수 있습니다.
    운영체제 독립성: Windows, Linux 등 원하는 운영체제에 GPU를 할당하여 사용할 수 있습니다. 전력 소모 및 발열 증가: 고성능 GPU를 사용하면 서버의 전력 소모와 발열이 크게 증가할 수 있습니다.

    Proxmox GPU 패스스루의 주요 장점(고성능 작업, 비용 절감, 유연성)과 단점(복잡한 설정, 호환성 문제, 자원 제약)을 시각적으로 요약한 인포그래픽입니다.

    7. 마무리하며: 다음 도전은 무엇일까요?

    오늘은 Proxmox VE에서 **Proxmox GPU 패스스루**를 통해 가상머신에서 그래픽카드를 활용하는 방법을 자세히 알아봤습니다. 쉽지 않은 과정이었지만, 한번 성공하고 나면 그 만족감은 정말 크실 겁니다. 저도 이 과정을 통해 하드웨어와 가상화 기술에 대한 이해를 한층 더 깊게 할 수 있었어요. 사실 이게 바로 홈랩의 매력이 아닐까 싶습니다. 직접 삽질하고 성공하면서 배우는 것들이 정말 많거든요.

    여러분도 이 가이드를 통해 성공적으로 **가상머신 GPU**를 설정하고, 꿈에 그리던 **Proxmox 게이밍 VM**이나 딥러닝 워크스테이션을 구축하시길 바랍니다. 혹시 더 궁금한 점이나 막히는 부분이 있다면 언제든지 댓글로 질문해주세요. 제가 아는 한에서 최대한 도와드리겠습니다.

    다음번에는 Proxmox에서 USB 패스스루를 통해 로컬 저장 장치나 보안 동글을 가상머신에 연결하는 방법에 대해서도 다뤄볼 예정입니다. 계속해서 ’13년차의 서버실’에 많은 관심 부탁드립니다! 감사합니다. 👋