GitOps 저장소도 ArgoCD Application도 아직 없어, 그때까지 이 저장소가 push 방식 파이프라인(.gitea/workflows/)을 임시로 소유한다. 무엇을 포기하는지와 넘길 때 할 일은 deploy/README.md에 적었다. Chart는 portal과 bundles 두 배포 모델을 모두 렌더링한다. ADR-0013이 ADR-0007을 대체했으므로 운영은 portal이 기준이지만, bundles 경로를 언제 삭제할지는 아직 정하지 않았다. - .gitea/workflows/ci.yaml, deploy-openshift.yaml - deploy/ci/render-manifests.sh, deploy/examples/ - Chart: mode 분기, selectedDeployment/tier helper, imagePullSecrets, toolService.apiKeySecret 참조 - extension-points.md에 미결 항목 9~12 추가 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
278 lines
8.6 KiB
YAML
278 lines
8.6 KiB
YAML
# AX HUB MCP 서버 개발계 수동 배포 샘플
|
|
#
|
|
# 주의:
|
|
# - 이 파일은 Helm template이 아니라, AA와 값을 협의한 뒤 수동으로 적용할 Raw OpenShift YAML 샘플이다.
|
|
# - "{{대문자_이름}}"은 확정되지 않은 값이다. 모든 자리표시자를 실제 값으로 교체한 뒤 적용한다.
|
|
# - 비밀번호와 API Key를 담는 Secret 및 그 참조는 현재 사용하지 않으므로 포함하지 않았다.
|
|
# - Harbor 인증이 필요하면 AA가 별도로 ServiceAccount에 image pull secret을 연결해야 한다.
|
|
# - 이 파일은 임시 수동 배포용 예제이며, 배포 정의의 정본은 deploy/helm/mcp-server Chart다.
|
|
#
|
|
# AA와 협의할 값:
|
|
# - {{DEV_NAMESPACE}}: MCP 서버를 배포할 개발계 namespace
|
|
# - {{HARBOR_IMAGE_REPOSITORY}}: Harbor project를 포함한 이미지 경로. 예: harbor.example/axhub/axhub-mcp
|
|
# - {{IMAGE_TAG}}: AA가 Podman으로 만들어 Push한 이미지 tag
|
|
# - {{DEV_MCP_HOST}}: 개발계 OpenShift Route host
|
|
# - {{AGENT_BUILDER_EGRESS_CIDR}}: Route 접근을 허용할 Agent Builder의 고정 egress CIDR
|
|
# - {{AGENT_BUILDER_NAMESPACE}}: Agent Builder Pod이 있는 namespace
|
|
# - {{DEV_PORTAL_REGISTRY_URL}}: 개발계 Portal registry API 주소
|
|
# - {{REDIS_SERVICE_HOST}}: 개발계 Redis Service host 또는 FQDN
|
|
# - {{CONFIG_VERSION}}: ConfigMap을 바꿀 때마다 증가시키는 값. 예: 1, 2, 3
|
|
#
|
|
# 적용 전 자리표시자 확인 예시(PowerShell):
|
|
# Get-Content .\deploy\examples\axhub-mcp-dev-manual.template.yaml |
|
|
# Where-Object { $_ -notmatch '^\s*#' } |
|
|
# Select-String -Pattern '\{\{[A-Z0-9_]+\}\}'
|
|
#
|
|
# 적용 예시:
|
|
# oc apply -f .\deploy\examples\axhub-mcp-dev-manual.template.yaml
|
|
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: axhub-mcp-config
|
|
namespace: "{{DEV_NAMESPACE}}"
|
|
labels:
|
|
app: axhub-mcp
|
|
app.kubernetes.io/name: axhub-mcp
|
|
app.kubernetes.io/instance: axhub-mcp-dev
|
|
app.kubernetes.io/component: mcp-server
|
|
app.kubernetes.io/part-of: ax-hub
|
|
ax-hub/mode: portal
|
|
ax-hub/tier: critical
|
|
data:
|
|
# SPRING_PROFILES_ACTIVE=dev이므로 파일명도 application-dev.yml이어야 한다.
|
|
application-dev.yml: |
|
|
management:
|
|
server:
|
|
port: 9090
|
|
health:
|
|
redis:
|
|
enabled: false
|
|
|
|
mcp:
|
|
# 환경별 Redis key가 서로 겹치지 않도록 개발계 identity를 고정한다.
|
|
identity: axhub-mcp-dev
|
|
|
|
# Route가 경로를 변경하지 않고 그대로 전달하므로 Route path와 같아야 한다.
|
|
endpoint-path: "/mcp"
|
|
|
|
registry:
|
|
refresh-interval-seconds: 30
|
|
refresh-jitter-seconds: 5
|
|
|
|
discovery:
|
|
enabled: true
|
|
|
|
redis:
|
|
enabled: true
|
|
# Portal 조회 실패 시 사용하는 Redis fallback key다. Portal과 같은 key인지 확인한다.
|
|
portal-registry-key: "axhub:mcp:portal-registry"
|
|
|
|
portal:
|
|
enabled: true
|
|
registry-url: "{{DEV_PORTAL_REGISTRY_URL}}"
|
|
refresh-interval-seconds: 60
|
|
|
|
# Portal이 route와 Tool Server 주소를 제공하므로 정적 bundle은 두지 않는다.
|
|
bundles: []
|
|
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: axhub-mcp
|
|
namespace: "{{DEV_NAMESPACE}}"
|
|
labels:
|
|
app: axhub-mcp
|
|
app.kubernetes.io/name: axhub-mcp
|
|
app.kubernetes.io/instance: axhub-mcp-dev
|
|
app.kubernetes.io/component: mcp-server
|
|
app.kubernetes.io/part-of: ax-hub
|
|
ax-hub/mode: portal
|
|
ax-hub/tier: critical
|
|
spec:
|
|
# 개발계 임시 테스트이므로 Pod 한 개로 구성한다.
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: axhub-mcp
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: axhub-mcp
|
|
app.kubernetes.io/name: axhub-mcp
|
|
app.kubernetes.io/instance: axhub-mcp-dev
|
|
app.kubernetes.io/component: mcp-server
|
|
app.kubernetes.io/part-of: ax-hub
|
|
ax-hub/mode: portal
|
|
ax-hub/tier: critical
|
|
annotations:
|
|
# Raw YAML은 Helm checksum을 자동 생성하지 못한다. ConfigMap 변경 시 이 값을 올리면 Pod이 재기동된다.
|
|
ax-hub/config-version: "{{CONFIG_VERSION}}"
|
|
spec:
|
|
terminationGracePeriodSeconds: 45
|
|
containers:
|
|
- name: mcp-server
|
|
image: "{{HARBOR_IMAGE_REPOSITORY}}:{{IMAGE_TAG}}"
|
|
imagePullPolicy: IfNotPresent
|
|
ports:
|
|
- name: http
|
|
containerPort: 8080
|
|
protocol: TCP
|
|
- name: management
|
|
containerPort: 9090
|
|
protocol: TCP
|
|
env:
|
|
- name: SPRING_PROFILES_ACTIVE
|
|
value: dev
|
|
# ConfigMap의 application-dev.yml을 JAR 내부 설정보다 우선 적용한다.
|
|
- name: SPRING_CONFIG_ADDITIONAL_LOCATION
|
|
value: file:/opt/app/config/
|
|
- name: REDIS_HOST
|
|
value: "{{REDIS_SERVICE_HOST}}"
|
|
- name: REDIS_PORT
|
|
value: "16379"
|
|
- name: MANAGEMENT_SERVER_PORT
|
|
value: "9090"
|
|
volumeMounts:
|
|
- name: config
|
|
mountPath: /opt/app/config
|
|
readOnly: true
|
|
readinessProbe:
|
|
httpGet:
|
|
path: /actuator/health/readiness
|
|
port: management
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /actuator/health/liveness
|
|
port: management
|
|
initialDelaySeconds: 20
|
|
periodSeconds: 20
|
|
resources:
|
|
requests:
|
|
cpu: 250m
|
|
memory: 512Mi
|
|
limits:
|
|
cpu: "1"
|
|
memory: 1Gi
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
runAsNonRoot: true
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
volumes:
|
|
- name: config
|
|
configMap:
|
|
name: axhub-mcp-config
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: axhub-mcp
|
|
namespace: "{{DEV_NAMESPACE}}"
|
|
labels:
|
|
app: axhub-mcp
|
|
app.kubernetes.io/name: axhub-mcp
|
|
app.kubernetes.io/instance: axhub-mcp-dev
|
|
app.kubernetes.io/component: mcp-server
|
|
app.kubernetes.io/part-of: ax-hub
|
|
ax-hub/mode: portal
|
|
ax-hub/tier: critical
|
|
spec:
|
|
type: ClusterIP
|
|
selector:
|
|
app: axhub-mcp
|
|
ports:
|
|
- name: http
|
|
port: 8080
|
|
targetPort: http
|
|
protocol: TCP
|
|
|
|
---
|
|
apiVersion: route.openshift.io/v1
|
|
kind: Route
|
|
metadata:
|
|
name: axhub-mcp
|
|
namespace: "{{DEV_NAMESPACE}}"
|
|
labels:
|
|
app: axhub-mcp
|
|
app.kubernetes.io/name: axhub-mcp
|
|
app.kubernetes.io/instance: axhub-mcp-dev
|
|
app.kubernetes.io/component: mcp-server
|
|
app.kubernetes.io/part-of: ax-hub
|
|
ax-hub/mode: portal
|
|
ax-hub/tier: critical
|
|
annotations:
|
|
haproxy.router.openshift.io/timeout: 300s
|
|
# 이 서버는 자체 인증을 하지 않으므로 반드시 실제 Agent Builder 고정 egress CIDR로 제한한다.
|
|
haproxy.router.openshift.io/ip_allowlist: "{{AGENT_BUILDER_EGRESS_CIDR}}"
|
|
spec:
|
|
host: "{{DEV_MCP_HOST}}"
|
|
# /mcp/{routeKey} 요청도 prefix match로 이 Route에 들어온다. rewrite는 사용하지 않는다.
|
|
path: /mcp
|
|
to:
|
|
kind: Service
|
|
name: axhub-mcp
|
|
weight: 100
|
|
port:
|
|
targetPort: http
|
|
tls:
|
|
termination: edge
|
|
insecureEdgeTerminationPolicy: Redirect
|
|
wildcardPolicy: None
|
|
|
|
---
|
|
# MCP 서버는 자체 인증·인가를 하지 않으므로 NetworkPolicy를 제거하면 안 된다.
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: axhub-mcp-ingress
|
|
namespace: "{{DEV_NAMESPACE}}"
|
|
labels:
|
|
app: axhub-mcp
|
|
app.kubernetes.io/name: axhub-mcp
|
|
app.kubernetes.io/instance: axhub-mcp-dev
|
|
app.kubernetes.io/component: mcp-server
|
|
app.kubernetes.io/part-of: ax-hub
|
|
ax-hub/mode: portal
|
|
ax-hub/tier: critical
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app: axhub-mcp
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
# OpenShift Route를 통과한 요청을 8080 포트로 허용한다.
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
policy-group.network.openshift.io/ingress: ""
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8080
|
|
|
|
# 같은 클러스터 안에서 Agent Builder가 직접 호출하는 경우만 8080 포트로 허용한다.
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: "{{AGENT_BUILDER_NAMESPACE}}"
|
|
ports:
|
|
- protocol: TCP
|
|
port: 8080
|
|
|
|
# Actuator management 포트는 OpenShift 관제 namespace에서만 접근하도록 제한한다.
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: openshift-monitoring
|
|
ports:
|
|
- protocol: TCP
|
|
port: 9090
|