Compare commits
10 Commits
831a14f1a3
...
013140f02c
| Author | SHA1 | Date | |
|---|---|---|---|
| 013140f02c | |||
| 49083910c6 | |||
| d3dd1da442 | |||
| 0e9940f0a3 | |||
| 8719e1723c | |||
| 742d24ab55 | |||
| fa75dcc2ca | |||
| 171cf347f4 | |||
| 075ce3f35c | |||
| a50720e84d |
@@ -1,190 +0,0 @@
|
||||
name: Build Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: gitea0213.kro.kr
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
DOCKER_HOST: tcp://172.17.0.1:2375
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
outputs:
|
||||
image-tag: ${{ steps.meta.outputs.tags }}
|
||||
image-digest: ${{ steps.build.outputs.digest }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to Gitea Container Registry
|
||||
run: |
|
||||
echo "${{ secrets.GITEAREGISTRY }}" | docker login ${{ env.REGISTRY }} --username bluemayne --password-stdin
|
||||
|
||||
- name: Lowercase repository name
|
||||
id: lowercase
|
||||
run: |
|
||||
echo "repo=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Extract metadata (tags, labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ steps.lowercase.outputs.repo }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha,prefix={{branch}}-sha-,format=long
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build
|
||||
run: |
|
||||
TAGS="${{ steps.meta.outputs.tags }}"
|
||||
|
||||
# Build the image
|
||||
docker build \
|
||||
-t $(echo "$TAGS" | head -n 1) \
|
||||
-f ./deploy/docker/Dockerfile \
|
||||
./services/backend
|
||||
|
||||
# Tag all versions
|
||||
FIRST_TAG=$(echo "$TAGS" | head -n 1)
|
||||
echo "$TAGS" | while read tag; do
|
||||
if [ "$tag" != "$FIRST_TAG" ]; then
|
||||
docker tag "$FIRST_TAG" "$tag"
|
||||
fi
|
||||
done
|
||||
|
||||
# Push all tags
|
||||
echo "$TAGS" | while read tag; do
|
||||
docker push "$tag"
|
||||
done
|
||||
|
||||
# Get digest
|
||||
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "$FIRST_TAG" | cut -d'@' -f2)
|
||||
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Extract SHA tag
|
||||
id: extract-tag
|
||||
run: |
|
||||
# Extract the SHA-based tag from the tags list
|
||||
TAGS="${{ steps.meta.outputs.tags }}"
|
||||
echo "All tags:"
|
||||
echo "$TAGS"
|
||||
echo "---"
|
||||
|
||||
# Get commit SHA (full 40 characters)
|
||||
COMMIT_SHA="${{ github.sha }}"
|
||||
|
||||
# Get current branch name
|
||||
BRANCH_NAME="${{ github.ref_name }}"
|
||||
echo "Branch: $BRANCH_NAME"
|
||||
|
||||
# Method 1: Extract the full SHA tag from docker/metadata-action output
|
||||
# docker/metadata-action creates: <branch>-sha-<full-40-char-sha>
|
||||
SHA_TAG=$(echo "$TAGS" | grep -oE "${BRANCH_NAME}-sha-[a-f0-9]{40}" | head -n 1)
|
||||
|
||||
# Method 2: If not found, try to extract any branch-sha- tag (fallback)
|
||||
if [ -z "$SHA_TAG" ]; then
|
||||
SHA_TAG=$(echo "$TAGS" | grep -oE "${BRANCH_NAME}-sha-[a-f0-9]+" | head -n 1)
|
||||
if [ -n "$SHA_TAG" ]; then
|
||||
echo "⚠️ Found SHA tag (may not be full 40 chars): $SHA_TAG"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Method 3: Fallback to commit SHA directly (construct the tag)
|
||||
if [ -z "$SHA_TAG" ]; then
|
||||
SHA_TAG="${BRANCH_NAME}-sha-$COMMIT_SHA"
|
||||
echo "⚠️ Could not extract from tags, using commit SHA: $SHA_TAG"
|
||||
fi
|
||||
|
||||
if [ -z "$SHA_TAG" ]; then
|
||||
echo "❌ ERROR: Failed to extract SHA tag"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "sha-tag=$SHA_TAG" >> $GITHUB_OUTPUT
|
||||
echo "✅ Extracted SHA tag: $SHA_TAG"
|
||||
|
||||
- name: Update kustomization with new image tag
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEAREGISTRYTOKEN }}
|
||||
run: |
|
||||
git config --global user.name "gitea-actions[bot]"
|
||||
git config --global user.email "gitea-actions[bot]@users.noreply.gitea.com"
|
||||
|
||||
# Validate that SHA_TAG is not empty
|
||||
SHA_TAG="${{ steps.extract-tag.outputs.sha-tag }}"
|
||||
if [ -z "$SHA_TAG" ]; then
|
||||
echo "❌ ERROR: SHA_TAG is empty, cannot update kustomization"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# For mas, we only have prod overlay (main branch)
|
||||
BRANCH_NAME="${{ github.ref_name }}"
|
||||
if [ "$BRANCH_NAME" = "main" ]; then
|
||||
OVERLAY="prod"
|
||||
else
|
||||
echo "⚠️ Unknown branch: $BRANCH_NAME, skipping kustomization update"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
KUSTOMIZATION_FILE="deploy/k8s/overlays/$OVERLAY/kustomization.yaml"
|
||||
|
||||
# Check if kustomization file has images section
|
||||
if grep -q "images:" "$KUSTOMIZATION_FILE"; then
|
||||
echo "📝 Updating $KUSTOMIZATION_FILE with tag: $SHA_TAG"
|
||||
|
||||
# Update kustomization.yaml with new image tag
|
||||
# Handle both cases: newTag: (with value) and newTag: (empty)
|
||||
sed -i.bak "s|newTag:.*|newTag: $SHA_TAG|" "$KUSTOMIZATION_FILE"
|
||||
|
||||
# Verify the update was successful
|
||||
if grep -q "newTag: $SHA_TAG" "$KUSTOMIZATION_FILE"; then
|
||||
echo "✅ Successfully updated kustomization.yaml"
|
||||
rm -f "$KUSTOMIZATION_FILE.bak"
|
||||
else
|
||||
echo "❌ ERROR: Failed to update kustomization.yaml"
|
||||
cat "$KUSTOMIZATION_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Commit and push if there are changes
|
||||
if git diff --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
git add "$KUSTOMIZATION_FILE"
|
||||
git commit -m "Update $OVERLAY image to $SHA_TAG"
|
||||
git push
|
||||
echo "✅ Kustomization updated with new image tag: $SHA_TAG"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ $OVERLAY overlay uses base image (latest tag), skipping kustomization update"
|
||||
echo " Image built with tag: $SHA_TAG"
|
||||
fi
|
||||
|
||||
- name: Display image information
|
||||
run: |
|
||||
echo "✅ Image built and pushed successfully!"
|
||||
echo "📦 Image tags:"
|
||||
echo "${{ steps.meta.outputs.tags }}"
|
||||
echo "🔖 SHA tag: ${{ steps.extract-tag.outputs.sha-tag }}"
|
||||
echo "🔖 Digest: ${{ steps.build.outputs.digest }}"
|
||||
echo ""
|
||||
echo "🚀 Kustomization updated with new image tag"
|
||||
echo " ArgoCD will automatically detect and deploy this new image"
|
||||
echo " Monitor deployment at your ArgoCD dashboard"
|
||||
|
||||
67
.github/workflows/build.yml
vendored
Normal file
67
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
name: Build Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Lowercase repository name
|
||||
id: lowercase
|
||||
run: |
|
||||
echo "repo=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Extract metadata (tags, labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ steps.lowercase.outputs.repo }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=semver,pattern={{version}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push Docker image
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./langgraph
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
platforms: linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Display image information
|
||||
run: |
|
||||
echo "Image built and pushed successfully!"
|
||||
echo "Image tags:"
|
||||
echo "${{ steps.meta.outputs.tags }}"
|
||||
337
README.md
337
README.md
@@ -1,337 +0,0 @@
|
||||
# MAS (Multi-Agent System)
|
||||
|
||||
**K8s 인프라 분석 & 의사결정 시스템** - 클러스터를 분석하고 도구 도입 여부를 결정해주는 AI 시스템
|
||||
|
||||
## 🎯 What is this?
|
||||
|
||||
MAS는 Kubernetes 클러스터 상태를 분석하고, **도구 도입 추천/비추천을 결정**해주는 AI 에이전트 시스템입니다.
|
||||
|
||||
**사용 시나리오:**
|
||||
1. "Tekton 도입 여부를 결정해줘" → 클러스터 분석 → **도입 추천/비추천 결정**
|
||||
2. 한국어로 이유, 대안, 구현 가이드 제공
|
||||
3. 기술적 세부사항 없이 **명확한 결론** 제시
|
||||
|
||||
## 🤖 Agents
|
||||
|
||||
### Planning Agent (Claude 4.5)
|
||||
- 도구 요구사항 분석
|
||||
- 필요한 K8s 리소스 파악
|
||||
- 확인이 필요한 클러스터 정보 정의
|
||||
|
||||
### Research Agent (Groq Llama 3.3)
|
||||
- kubectl 명령어로 클러스터 상태 분석
|
||||
- 기존 도구 확인 (ArgoCD, Gitea, Prometheus 등)
|
||||
- 리소스 사용률 및 버전 확인
|
||||
|
||||
### Decision Agent (Claude 4.5)
|
||||
- **도입 추천/비추천 결정** (한국어)
|
||||
- 명확한 이유 제시
|
||||
- 대안 제시 (비추천인 경우)
|
||||
- 간단한 구현 가이드 (추천인 경우)
|
||||
|
||||
### Tech stack
|
||||
- **Backend**: LangGraph + LangChain + FastAPI
|
||||
- **UI**: Chainlit (chat-style UI)
|
||||
- **Database**: PostgreSQL (CNPG)
|
||||
- **Cache**: Redis
|
||||
- **LLMs**: Claude API (Orchestrator, Planning, Prompt Gen) + Groq Llama 3.3 (Research)
|
||||
- **Deploy**: Kubernetes + ArgoCD
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Local development
|
||||
|
||||
### 1. Run with Docker Compose
|
||||
|
||||
```bash
|
||||
cd deploy/docker
|
||||
|
||||
# Copy or create .env and fill in your API keys
|
||||
# (ANTHROPIC_API_KEY, GROQ_API_KEY, etc.)
|
||||
|
||||
# Start the full stack
|
||||
docker compose up -d
|
||||
|
||||
# Tail logs
|
||||
docker compose logs -f mas
|
||||
```
|
||||
|
||||
Open: `http://localhost:8000`
|
||||
|
||||
### 2. Run backend directly (Python)
|
||||
|
||||
```bash
|
||||
cd services/backend
|
||||
|
||||
# Create venv
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Windows: venv\Scripts\activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Environment variables
|
||||
cp .env.example .env
|
||||
# Edit .env and set your API keys
|
||||
|
||||
# Run Chainlit app
|
||||
chainlit run chainlit_app.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ☸️ Kubernetes deployment
|
||||
|
||||
### 1. Create namespace and secrets
|
||||
|
||||
```bash
|
||||
kubectl create namespace mas
|
||||
|
||||
kubectl create secret generic mas-api-keys \
|
||||
--from-literal=anthropic-api-key=YOUR_CLAUDE_KEY \
|
||||
--from-literal=openai-api-key=YOUR_OPENAI_KEY \
|
||||
--from-literal=google-api-key=YOUR_GEMINI_KEY \
|
||||
-n mas
|
||||
```
|
||||
|
||||
### 2. Deploy via ArgoCD
|
||||
|
||||
```bash
|
||||
# Create ArgoCD Application
|
||||
kubectl apply -f deploy/argocd/mas.yaml
|
||||
|
||||
# Sync and check status
|
||||
argocd app sync mas
|
||||
argocd app get mas
|
||||
```
|
||||
|
||||
### 3. Deploy from your server (example)
|
||||
|
||||
```bash
|
||||
# SSH into your k3s master
|
||||
ssh oracle-master
|
||||
|
||||
# Apply ArgoCD Application
|
||||
sudo kubectl apply -f /path/to/deploy/argocd/mas.yaml
|
||||
|
||||
# Check status
|
||||
sudo kubectl get pods -n mas
|
||||
sudo kubectl logs -f deployment/mas -n mas
|
||||
```
|
||||
|
||||
Ingress example (if configured): `https://mas.mayne.vcn`
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI customization
|
||||
|
||||
### Chainlit theme & behavior
|
||||
|
||||
You can customize the UI via `services/backend/.chainlit`:
|
||||
|
||||
```toml
|
||||
[UI]
|
||||
name = "MAS"
|
||||
show_readme_as_default = true
|
||||
default_collapse_content = true
|
||||
```
|
||||
|
||||
### Agent prompts
|
||||
|
||||
System prompts for each agent live in `services/backend/agents.py`.
|
||||
You can tune:
|
||||
- how the **Orchestrator** routes tasks
|
||||
- coding style of backend/frontend agents
|
||||
- SRE troubleshooting behavior
|
||||
|
||||
---
|
||||
|
||||
## 📊 Observability
|
||||
|
||||
### Prometheus ServiceMonitor (example)
|
||||
|
||||
```yaml
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: mas
|
||||
namespace: mas
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mas
|
||||
endpoints:
|
||||
- port: http
|
||||
path: /metrics
|
||||
```
|
||||
|
||||
### Grafana dashboards
|
||||
|
||||
Recommended panels:
|
||||
- LangGraph workflow metrics
|
||||
- Per-agent latency & error rate
|
||||
- Token usage and cost estimates
|
||||
- Backend API latency & 5xx rate
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Advanced features
|
||||
|
||||
### 1. MCP (Model Context Protocol) with Claude
|
||||
|
||||
Using Claude Code as Orchestrator, MAS can access:
|
||||
- Filesystem (read/write project files)
|
||||
- Git (status, commit, push, PR)
|
||||
- SSH (run remote commands on your servers)
|
||||
- PostgreSQL (schema inspection, migrations, queries)
|
||||
- Kubernetes (kubectl via MCP tool)
|
||||
|
||||
This allows fully automated workflows like:
|
||||
- “Create a new service, add deployment manifests, and deploy to k3s.”
|
||||
- “Debug failing pods and propose a fix, then open a PR.”
|
||||
|
||||
### 2. Multi-agent collaboration (LangGraph)
|
||||
|
||||
Typical workflow:
|
||||
|
||||
```text
|
||||
User request
|
||||
↓
|
||||
Claude Orchestrator
|
||||
↓ decides which agent(s) to call
|
||||
Backend Dev → Frontend Dev → SRE
|
||||
↓
|
||||
Claude Orchestrator (review & summary)
|
||||
↓
|
||||
Final answer to user
|
||||
```
|
||||
|
||||
Examples:
|
||||
- Full‑stack feature (API + UI + monitoring)
|
||||
- Infra rollout (Harbor, Tekton, CNPG, MetalLB) with validation
|
||||
|
||||
---
|
||||
|
||||
## 📝 Usage examples
|
||||
|
||||
### Example 1: Tekton 도입 여부 결정
|
||||
|
||||
```text
|
||||
User: "Tekton 도입 여부를 결정해줘"
|
||||
|
||||
🎼 Orchestrator → 조율
|
||||
|
||||
📋 Planning Agent:
|
||||
→ Tekton 요구사항: Namespace, CRDs, Controllers
|
||||
→ 필요 리소스: 2 CPU, 4GB RAM
|
||||
→ 확인 필요: 기존 CI/CD 도구, K8s 버전
|
||||
|
||||
🔍 Research Agent:
|
||||
→ kubectl get nodes: v1.33.6, 3 nodes ✓
|
||||
→ kubectl get pods -A: ArgoCD 운영 중 발견
|
||||
→ Gitea Actions 사용 가능 확인
|
||||
|
||||
💡 Decision Agent:
|
||||
❌ Tekton 도입 비추천
|
||||
|
||||
이유:
|
||||
- ArgoCD + Gitea Actions로 충분
|
||||
- 추가 리소스 소비 불필요
|
||||
- 학습 곡선 및 유지보수 부담
|
||||
|
||||
대안:
|
||||
- Gitea Actions 활용 (이미 설치됨)
|
||||
- ArgoCD로 배포 자동화 유지
|
||||
|
||||
✨ Output: 명확한 한국어 보고서
|
||||
```
|
||||
|
||||
### Example 2: Harbor 필요성 분석
|
||||
|
||||
```text
|
||||
User: "Harbor가 필요한지 분석해줘"
|
||||
|
||||
→ Planning: Harbor 요구사항 분석
|
||||
→ Research: 기존 registry 확인 (Gitea Container Registry 발견)
|
||||
→ Decision:
|
||||
❌ Harbor 도입 비추천
|
||||
이유: Gitea Container Registry로 충분
|
||||
|
||||
✨ 사용자 친화적 한국어 결론
|
||||
```
|
||||
|
||||
### Example 3: Prometheus 설치 여부
|
||||
|
||||
```text
|
||||
User: "Prometheus를 설치해야 할까?"
|
||||
|
||||
→ Planning: Monitoring stack 요구사항
|
||||
→ Research: 이미 Prometheus 운영 중 발견!
|
||||
→ Decision:
|
||||
✅ 이미 설치되어 있음
|
||||
현재 상태: monitoring namespace에서 정상 작동 중
|
||||
|
||||
✨ 중복 설치 방지
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Workflow
|
||||
|
||||
```
|
||||
User Input: "X 도입 여부를 결정해줘"
|
||||
↓
|
||||
Orchestrator (조율)
|
||||
↓
|
||||
Planning Agent (도구 요구사항 분석)
|
||||
↓
|
||||
Research Agent (클러스터 상태 분석)
|
||||
↓
|
||||
Decision Agent (한국어 의사결정 보고서)
|
||||
↓
|
||||
Output: ✅ 추천 또는 ❌ 비추천 (이유 포함)
|
||||
```
|
||||
|
||||
## 📊 출력 예시
|
||||
|
||||
```markdown
|
||||
# Tekton 도입 분석 결과
|
||||
|
||||
## 📊 현재 클러스터 상태
|
||||
- Kubernetes 버전: v1.33.6
|
||||
- 노드: 3개 (1 control-plane, 2 workers)
|
||||
- 기존 CI/CD: ArgoCD, Gitea Actions
|
||||
- 운영 애플리케이션: 15개
|
||||
|
||||
## 💡 권장사항: Tekton 도입 비추천
|
||||
|
||||
### ❌ 비추천 이유
|
||||
1. ArgoCD + Gitea Actions 조합으로 충분
|
||||
2. 추가 리소스 소비 (2 CPU, 4GB RAM)
|
||||
3. 학습 곡선 및 운영 부담 증가
|
||||
|
||||
### 🔄 권장 대안
|
||||
- Gitea Actions로 빌드 파이프라인 구성
|
||||
- ArgoCD로 GitOps 배포 유지
|
||||
- 필요시 GitHub Actions 연동
|
||||
|
||||
## 🎯 결론
|
||||
현재 인프라로 충분하며, Tekton 도입은 불필요합니다.
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Contributions are welcome:
|
||||
- Improve Planning Agent prompts for better folder structures
|
||||
- Enhance Research Agent kubectl commands
|
||||
- Add more infrastructure tools (Harbor, Tekton, CNPG, MetalLB, etc.)
|
||||
- Better Markdown template for Prompt Generator
|
||||
|
||||
Feel free to open issues or PRs in your Git repository.
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: mas-prod
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/Mayne0213/mas.git
|
||||
targetRevision: main
|
||||
path: deploy/k8s/overlays/prod
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: mas
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
revisionHistoryLimit: 10
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: mas
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: default
|
||||
|
||||
source:
|
||||
repoURL: https://github.com/Mayne0213/mas.git
|
||||
targetRevision: main
|
||||
path: deploy/argocd
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: argocd
|
||||
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false
|
||||
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
||||
retry:
|
||||
limit: 5
|
||||
backoff:
|
||||
duration: 5s
|
||||
factor: 2
|
||||
maxDuration: 3m
|
||||
|
||||
revisionHistoryLimit: 10
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
# App of Apps Application (self-managing)
|
||||
- application.yaml
|
||||
|
||||
# Application deployments
|
||||
- application-prod.yaml
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mas
|
||||
namespace: mas
|
||||
labels:
|
||||
app: mas
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mas
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mas
|
||||
spec:
|
||||
hostPID: true # 호스트 프로세스 네임스페이스 접근
|
||||
serviceAccountName: mas
|
||||
containers:
|
||||
- name: mas
|
||||
image: github.com/Mayne0213/mas:latest
|
||||
imagePullPolicy: Always
|
||||
securityContext:
|
||||
privileged: true # nsenter 사용을 위한 권한
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
env:
|
||||
- name: ANTHROPIC_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mas-api-keys
|
||||
key: anthropic-api-key
|
||||
- name: GROQ_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mas-api-keys
|
||||
key: groq-api-key
|
||||
- name: GROQ_API_BASE
|
||||
value: "https://api.groq.com/openai/v1"
|
||||
# Chainlit uses asyncpg directly (not SQLAlchemy)
|
||||
- name: CHAINLIT_DATABASE_URL
|
||||
value: "postgresql://bluemayne:$(POSTGRES_PASSWORD)@postgresql-cnpg-rw.postgresql-cnpg.svc.cluster.local:5432/mas"
|
||||
# SQLAlchemy format (if needed)
|
||||
- name: DATABASE_URL
|
||||
value: "postgresql://bluemayne:$(POSTGRES_PASSWORD)@postgresql-cnpg-rw.postgresql-cnpg.svc.cluster.local:5432/mas"
|
||||
- name: POSTGRES_HOST
|
||||
value: "postgresql-cnpg-rw.postgresql-cnpg.svc.cluster.local"
|
||||
- name: POSTGRES_PORT
|
||||
value: "5432"
|
||||
- name: POSTGRES_USER
|
||||
value: "bluemayne"
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgresql-password
|
||||
key: password
|
||||
- name: GITEA_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mas-api-keys
|
||||
key: gitea-token
|
||||
optional: true
|
||||
- name: REDIS_URL
|
||||
value: "redis://redis:6379/0"
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
# cpu: removed to prevent throttling
|
||||
volumeMounts:
|
||||
- name: projects
|
||||
mountPath: /mnt/projects
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: mayne-vcn # Only run on master node where Projects directory exists
|
||||
volumes:
|
||||
- name: projects
|
||||
hostPath:
|
||||
path: /home/ubuntu/Projects
|
||||
type: Directory
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- serviceaccount.yaml
|
||||
|
||||
commonLabels:
|
||||
app.kubernetes.io/name: mas
|
||||
app.kubernetes.io/component: platform
|
||||
|
||||
images:
|
||||
- name: github.com/Mayne0213/mas
|
||||
newTag: latest
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mas
|
||||
namespace: mas
|
||||
labels:
|
||||
app: mas
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8000
|
||||
targetPort: 8000
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: mas
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: mas
|
||||
namespace: mas
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: mas-viewer
|
||||
rules:
|
||||
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
|
||||
resources: ["*"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: mas-writer
|
||||
rules:
|
||||
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
|
||||
resources: ["*"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: mas-viewer-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: mas-viewer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: mas
|
||||
namespace: mas
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: mas-writer-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: mas-writer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: mas
|
||||
namespace: mas
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: mas-api-keys
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: vault-backend
|
||||
target:
|
||||
name: mas-api-keys
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: anthropic-api-key
|
||||
remoteRef:
|
||||
key: mas/api-keys
|
||||
property: ANTHROPIC_API_KEY
|
||||
- secretKey: groq-api-key
|
||||
remoteRef:
|
||||
key: mas/api-keys
|
||||
property: GROQ_API_KEY
|
||||
---
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: postgresql-password
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: vault-backend
|
||||
target:
|
||||
name: postgresql-password
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: databases/postgresql-cnpg
|
||||
property: PASSWORD
|
||||
- secretKey: postgres-password
|
||||
remoteRef:
|
||||
key: databases/postgresql-cnpg
|
||||
property: POSTGRES_PASSWORD
|
||||
---
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: postgresql-root-password
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: vault-backend
|
||||
target:
|
||||
name: postgresql-root-password
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: databases/postgresql-cnpg
|
||||
property: POSTGRES_PASSWORD
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: mas-ingress
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
# WebSocket 지원 (Chainlit UI에 필요)
|
||||
nginx.ingress.kubernetes.io/websocket-services: "mas"
|
||||
# 프록시 타임아웃 설정 (장시간 스트리밍 응답)
|
||||
nginx.ingress.kubernetes.io/proxy-connect-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
# 프록시 버퍼링 비활성화 (스트리밍 응답)
|
||||
nginx.ingress.kubernetes.io/proxy-buffering: "off"
|
||||
spec:
|
||||
ingressClassName: haproxy
|
||||
tls:
|
||||
- hosts:
|
||||
- mas0213.kro.kr
|
||||
- www.mas0213.kro.kr
|
||||
secretName: mas-tls
|
||||
rules:
|
||||
- host: mas0213.kro.kr
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: mas
|
||||
port:
|
||||
number: 8000
|
||||
- host: www.mas0213.kro.kr
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: mas
|
||||
port:
|
||||
number: 8000
|
||||
@@ -1,19 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: mas
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
- resourcequota.yaml
|
||||
- externalsecret.yaml
|
||||
- ingress.yaml
|
||||
|
||||
commonLabels:
|
||||
environment: production
|
||||
|
||||
# 이미지 태그 설정 (ArgoCD Image Updater가 자동으로 업데이트)
|
||||
images:
|
||||
- name: github.com/Mayne0213/mas
|
||||
newTag: main-sha-004c30bfa872c37dd3da5ad8501589c415807da8
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: mas
|
||||
labels:
|
||||
name: mas
|
||||
environment: production
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ResourceQuota
|
||||
metadata:
|
||||
name: mas-quota
|
||||
spec:
|
||||
hard:
|
||||
requests.cpu: "4"
|
||||
requests.memory: 8Gi
|
||||
# limits.cpu: "8" # Removed to prevent quota enforcement (we removed CPU limits cluster-wide)
|
||||
limits.memory: 16Gi
|
||||
persistentvolumeclaims: "5"
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: mas
|
||||
namespace: mas
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: mas-viewer
|
||||
rules:
|
||||
# Read-only access to most resources
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- pods
|
||||
- pods/log
|
||||
- services
|
||||
- endpoints
|
||||
- namespaces
|
||||
- nodes
|
||||
- persistentvolumeclaims
|
||||
- configmaps
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
- apiGroups: ["apps"]
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
- daemonsets
|
||||
- replicasets
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
- apiGroups: ["batch"]
|
||||
resources:
|
||||
- jobs
|
||||
- cronjobs
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources:
|
||||
- ingresses
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
- apiGroups: ["argoproj.io"]
|
||||
resources:
|
||||
- applications
|
||||
verbs: ["get", "list", "watch"]
|
||||
|
||||
# Describe resources
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- pods/status
|
||||
- services/status
|
||||
verbs: ["get"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: mas-viewer-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: mas-viewer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: mas
|
||||
namespace: mas
|
||||
---
|
||||
# YAML Manager용 write 권한 (Groq 에이전트)
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: mas-writer
|
||||
rules:
|
||||
# Write access for YAML Manager
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- pods
|
||||
- services
|
||||
- configmaps
|
||||
- secrets
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
- apiGroups: ["apps"]
|
||||
resources:
|
||||
- deployments
|
||||
- statefulsets
|
||||
- daemonsets
|
||||
- replicasets
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources:
|
||||
- ingresses
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
- apiGroups: ["batch"]
|
||||
resources:
|
||||
- jobs
|
||||
- cronjobs
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
|
||||
# Namespace management
|
||||
- apiGroups: [""]
|
||||
resources:
|
||||
- namespaces
|
||||
verbs: ["create", "update", "patch"]
|
||||
|
||||
# ArgoCD Application management
|
||||
- apiGroups: ["argoproj.io"]
|
||||
resources:
|
||||
- applications
|
||||
verbs: ["create", "update", "patch", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: mas-writer-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: mas-writer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: mas
|
||||
namespace: mas
|
||||
|
||||
Reference in New Issue
Block a user