콘텐츠로 이동

ArgoCD로 배포하기

이 Application 매니페스트들은 hermes-agent를 ArgoCD로 안전하게 배포하며, 같은 네임스페이스에 여러 인스턴스를 충돌 없이 둘 수도 있습니다.

각 파일은 대응하는 charts/hermes-agent/values-*.yaml 예제를 1:1로 그대로 옮긴 것이며, 시크릿은 단순 --set 대신 extraEnvFrom으로 연결합니다:

Application 파일 대응하는 예제 필요한 Secret
hermes-agent.yaml 차트 기본값(values.yaml) hermes-agent-secrets (OPENAI_API_KEY)
hermes-agent-openai.yaml values-openai.yaml hermes-agent-openai-secrets (OPENAI_API_KEY)
hermes-agent-openai-sealedsecret.yaml values-openai.yaml + GitOps SealedSecret을 통한 hermes-agent-openai-sealedsecret-secrets (extraResources)
hermes-agent-anthropic.yaml values-anthropic.yaml hermes-agent-anthropic-secrets (ANTHROPIC_API_KEY)
hermes-agent-gemini.yaml values-gemini.yaml hermes-agent-gemini-secrets (GOOGLE_API_KEY)
hermes-agent-upstage.yaml values-upstage.yaml(모델을 Solar Open 2로 재정의) hermes-agent-upstage-secrets (UPSTAGE_API_KEY)
hermes-agent-openrouter.yaml values-openrouter.yaml hermes-agent-openrouter-secrets (OPENROUTER_API_KEY)
hermes-agent-litellm.yaml values-litellm.yaml hermes-agent-litellm-secrets (OPENAI_API_KEY, 프록시 키)
hermes-agent-litellm-k8s.yaml values-litellm-k8s.yaml SealedSecret을 통한 hermes-agent-litellm-k8s-secrets (extraResources)
hermes-agent-anthropic-and-discord.yaml values-anthropic-and-discord.yaml hermes-agent-anthropic-discord-secrets (ANTHROPIC_API_KEY, DISCORD_BOT_TOKEN)
hermes-agent-openai-and-telegram.yaml values-openai-and-telegram.yaml hermes-agent-openai-telegram-secrets (OPENAI_API_KEY, TELEGRAM_BOT_TOKEN)
hermes-agent-nvidia-nim-and-discord.yaml values-nvidia-nim-and-discord.yaml hermes-agent-nim-discord-secrets (NVIDIA_API_KEY, DISCORD_BOT_TOKEN)
hermes-agent-nvidia-nim-and-discord-sealedsecret.yaml values-nvidia-nim-and-discord.yaml + GitOps SealedSecret을 통한 hermes-agent-nim-discord-sealedsecret-secrets (extraResources, NVIDIA_API_KEY + DISCORD_BOT_TOKEN)
hermes-agent-github-copilot.yaml values-github-copilot.yaml + GitOps SealedSecret을 통한 hermes-agent-copilot-secrets(DISCORD_BOT_TOKEN만: Copilot 토큰은 OAuth 기기 흐름으로 런타임에 발급)
hermes-agent-ingress.yaml values-ingress.yaml hermes-agent-ingress-secrets (OPENAI_API_KEY와 대시보드 사용자 이름, 비밀번호, 세션 secret. 대시보드 자체 비밀번호 provider이며 신뢰된 네트워크나 VPN용)
hermes-collab-pair.yaml values-multi-agent-collab.yaml(×2: planner+builder) hermes-planner-discord-secrets + hermes-builder-discord-secrets: @멘션으로 핸드오프하는 협업 페어. Hermes 협업 참고
hermes-team.yaml values-team-leader.yaml + values-team-member.yaml hermes-august-discord-secrets + hermes-may-discord-secrets + hermes-march-discord-secrets + 미리 준비된 hermes-team-knowledge RWX PVC: 리더 주도 팀(직렬화된 명시적 멘션, 리더 쓰기/멤버 읽기 전용 공유 지식, 파일 기반 작업 핸드오프 없음). Hermes 팀 참고

hermes-agent.yaml은 최소한의 시작점입니다 - 순수 차트 기본값과 시크릿 연결뿐입니다. 이걸 복사하고 valuesObject를 추가해 커스터마이즈하세요.

hermes-collab-pair.yaml은 첫 번째 다중 Application 예제입니다: 두 에이전트(LiteLLM 위의 planner와 Copilot 기기 흐름 위의 builder)가 하나의 Discord 채널을 공유하며 @멘션으로 핸드오프합니다. 핸드오프 프로토콜과 4개의 루프 브레이크 환경 변수는 Hermes 협업를 참고하세요.

hermes-team.yaml은 그 패턴을 확장합니다: 팀 리더(august)용 Application 하나와, 멤버 명부(may, march)가 list-generator 항목인 ApplicationSet 하나로 구성됩니다 - 팀원 추가는 한 줄짜리 diff입니다. 에이전트들은 하나의 Discord 채널을 공유합니다(스타 토폴로지: 멘션은 리더 ↔ 멤버 사이로만 흐릅니다). 스레드가 조정 버스이자 감사 로그입니다. 별도의 RWX PVC가 지속적인 재사용 지식을 저장하지만(리더 읽기/쓰기, 멤버 읽기 전용), 작업, 상태, 중간 결과, 완료 신호는 절대 담지 않습니다. Hermes 팀 → "리더 주도 팀" 참고.

모든 예제는 OCI 레지스트리 소스 형식(ghcr.io를 가리키는 repoURL/chart/targetRevision)을 씁니다. Git 저장소에 커밋된 차트를 추적하고 싶다면 Git 소스 형식(repoURL/targetRevision/path)도 동작합니다 - 둘을 자유롭게 바꿔 쓰세요.

hermes-agent-litellm-k8s.yaml이 가장 완전한 예제입니다: 전체 GitOps 패턴(extraResources를 통한 SealedSecret + extraEnvFrom + 기본값이 아닌 persistence.storageClass)을 보여줍니다. 나머지는 out-of-band로 생성한 평범한 Secret을 씁니다(정확한 kubectl create secret 명령은 각 파일의 헤더 주석 참고).

모든 Application은 destination.namespace: hermes-agent를 대상으로 하며 서로 다른 releaseName을 사용합니다 - 같은 네임스페이스에 함께 적용해도 충돌하지 않습니다(아래 참고).

유일한 규칙: 인스턴스마다 고유한 fullname

모든 차트 리소스는 Helm fullname({fullname}, {fullname}-0, {fullname}-config, {fullname}-env, {fullname}-headless, {fullname}-test, data-{fullname}-0)으로 이름 지어집니다. 두 Application이 같은 네임스페이스에서 같은 fullname을 렌더링할 때만 충돌합니다.

그러니 각 Application에 서로 다른 spec.source.helm.releaseName을 주고:

metadata.name == spec.source.helm.releaseName으로 설정하세요.

이렇게 하면 두 가지가 해결됩니다: 1. 인스턴스마다 fullname이 고유해집니다(이름 충돌 없음). 2. 차트의 app.kubernetes.io/instance 값이 Application과 같아집니다: 직관적이고 일관됩니다.

여기서 추적 레이블이 충돌하지 않는 이유

ArgoCD는 application.instanceLabelKey로 설정된 키를 가진 레이블로 소유권을 추적합니다. 이 클러스터는 argocd.argoproj.io/instance(ArgoCD 전용 키)를 쓰는 반면, 차트는 자체 셀렉터에 app.kubernetes.io/instance를 씁니다. 키가 다르므로 → 기본 label 추적 방식이어도 충돌하지 않습니다. (만약 클러스터가 추적에 app.kubernetes.io/instance를 쓴다면, argocd-cm에서 application.resourceTrackingMethod: annotation으로 바꿔 annotation 추적으로 전환하세요.)

Secret(키를 커밋하지 마세요)

차트의 env.<KEY> 값은 플레이스홀더로 두고, 실제 키는 out-of-band로 생성한 Secret에서(또는 sealed-secrets / external-secrets를 통해) 주입하세요. 각 Application의 헤더 주석에 정확한 명령이 있습니다. 예:

kubectl create namespace hermes-agent
kubectl create secret generic hermes-agent-openai-secrets -n hermes-agent \
  --from-literal=OPENAI_API_KEY='sk-<your-key>'

extraEnvFrom이 그 Secret을 참조합니다. 차트 자체의 env Secret보다 나중에 적용되므로 이 값들이 우선합니다.

SealedSecret 따라 하기(NVIDIA NIM + Discord)

hermes-agent-nvidia-nim-and-discord-sealedsecret.yaml은 hermes-agent-nvidia-nim-and-discord.yaml의 GitOps 버전입니다: out-of-band로 평범한 Secret을 만드는 대신, bitnami SealedSecret을 extraResources에 싣습니다. sealed-secrets 컨트롤러가 클러스터 안에서 이를 복호화해 일반 Secret(hermes-agent-nim-discord-sealedsecret-secrets)으로 만들고, extraEnvFrom이 이를 마운트합니다. 암호화된 blob은 Git에 커밋해도 안전합니다 - 컨트롤러의 개인 키(여러분의 클러스터가 보유)만 복호화할 수 있습니다.

이 가이드는 키 두 개(NVIDIA_API_KEY와 DISCORD_BOT_TOKEN)를 한 번에 봉인합니다. 키 하나만 봉인하는 더 간단한 kubeseal --raw --from-file 방식은 hermes-agent-openai-sealedsecret.yaml을 참고하세요.

사전 준비: - 대상 클러스터에 sealed-secrets 컨트롤러가 설치되어 있어야 합니다(예: helm install sealed-secrets sealed-secrets/sealed-secrets -n kube-system). - 로컬에 kubeseal CLI가 설치되어 있고, 컨트롤러의 공개 인증서와 일치하거나 가져올 수 있어야 합니다.

1. 평문 Secret 매니페스트를 작성하되, 적용하지는 마세요:

cat > /tmp/hermes-agent-nim-discord-secret.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: hermes-agent-nim-discord-sealedsecret-secrets
  namespace: hermes-agent
type: Opaque
stringData:
  NVIDIA_API_KEY: "nvapi-<your-real-key>"
  DISCORD_BOT_TOKEN: "<your-real-bot-token>"
EOF

2. 봉인합니다. kubeseal -o yaml은 Secret 매니페스트를 읽어 그에 상응하는 SealedSecret을 출력하며, data/stringData의 모든 항목을 컨트롤러의 공개 키로 암호화합니다(클러스터에서 자동으로 가져오거나, 오프라인 인증서라면 --cert <pub-cert.pem>을 넘기세요):

kubeseal --scope namespace-wide \
  -o yaml < /tmp/hermes-agent-nim-discord-secret.yaml \
  > /tmp/hermes-agent-nim-discord-sealedsecret.yaml

이렇게 하면 spec.encryptedData.NVIDIA_API_KEY와 spec.encryptedData.DISCORD_BOT_TOKEN을 가진 SealedSecret이 만들어지며, 각각 긴 AgB... / AgD... base64 blob입니다. --scope namespace-wide는 재봉인 없이 hermes-agent 안에서 SealedSecret의 이름을 바꾸거나 옮길 수 있게 해줍니다 - 이 디렉터리의 다른 SealedSecret 예제들과 같은 관례입니다.

3. encryptedData 값 두 개를 hermes-agent-nvidia-nim-and-discord-sealedsecret.yaml의 extraResources[0].spec.encryptedData에 끼워 넣어, AgDUMMY_replace_with_kubeseal_output== 플레이스홀더를 교체하세요:

extraResources:
  - apiVersion: bitnami.com/v1alpha1
    kind: SealedSecret
    metadata:
      name: hermes-agent-nim-discord-sealedsecret-secrets
    spec:
      encryptedData:
        NVIDIA_API_KEY: AgB...      # <- 2단계에서
        DISCORD_BOT_TOKEN: AgD...   # <- 2단계에서
      template:
        metadata:
          name: hermes-agent-nim-discord-sealedsecret-secrets
        type: Opaque

extraEnv[].value(DISCORD_HOME_CHANNEL, DISCORD_ALLOWED_USERS)에도 실제 값을 채우고, 여러분의 계정으로 접근 가능한 NVIDIA NIM 모델을 고르세요.

4. Application을 적용하고 확인합니다:

kubectl apply -f examples/argocd/hermes-agent-nvidia-nim-and-discord-sealedsecret.yaml

# 컨트롤러가 SealedSecret을 복호화해 Secret으로 만들어야 합니다:
kubectl get sealedsecret,secret -n hermes-agent hermes-agent-nim-discord-sealedsecret-secrets

# 그리고 파드는 extraEnvFrom을 통해 두 키를 모두 받아야 합니다
# (fullnameOverride: hermes-agent가 리소스 이름을 짧게 유지합니다.
# 파일의 valuesObject 참고):
kubectl exec -n hermes-agent deploy/hermes-agent -- \
  env | grep -E '^(NVIDIA_API_KEY|DISCORD_BOT_TOKEN)='

kubectl get secret에 아무 Secret도 보이지 않으면 컨트롤러 로그를 확인하세요(kubectl logs -n kube-system -l app.kubernetes.io/name=sealed-secrets) - 가장 흔한 원인은 잘못된 클러스터의 인증서로 봉인한 경우입니다.

적용

kubectl apply -f examples/argocd/hermes-agent-openai.yaml

(위 표의 다른 파일로 바꿔 쓰세요.)

같은 네임스페이스에 여러 인스턴스 두기

이름 == releaseName만 다르게 해서 Application을 복제하면 됩니다. 첫 인스턴스와 나란히, 둘 다 hermes-agent에 두는 두 번째 인스턴스 예:

metadata:
  name: hermes-agent-staging      # 다른 이름
spec:
  source:
    helm:
      releaseName: hermes-agent-staging   # == metadata.name
  destination:
    namespace: hermes-agent       # 같은 네임스페이스도 괜찮음

리소스는 hermes-agent-staging-*로 렌더링되어(파드 hermes-agent-staging-0, hermes-agent-staging-config, data-hermes-agent-staging-0 등) hermes-agent-* 집합과 겹치지 않습니다. 각각 자기 PVC를 갖기 때문에, 두 인스턴스는 지식 베이스를 공유하지 않습니다.