본문 바로가기
WIKI 기술 지식 베이스

WatchOperation

원문 보기 위키 갱신

WatchOperation (리소스 변경 감지 Operations)

이 기능은 v2에서 도입되었습니다. 자세한 내용은 Crossplane 기능 생명주기 문서를 참고하세요.

출처: 문서

본문

WatchOperation은 감시 중인 Kubernetes 리소스가 변경될 때 Operations를 생성합니다. 데이터베이스를 삭제하기 전에 백업하거나, 업데이트 후 구성을 검증하거나, 리소스 실패 시 알림을 트리거하는 등 반응형 운영 워크플로에 WatchOperations를 사용하세요.

WatchOperations는 어떻게 동작하나요? (How WatchOperations work)

WatchOperations는 특정 Kubernetes 리소스를 감시하며, 해당 리소스가 변경될 때마다 새 Operations를 생성합니다. 변경된 리소스는 함수가 처리할 수 있도록 Operation에 자동으로 주입됩니다.

apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: config-validator
spec:
  watch:
    apiVersion: v1
    kind: ConfigMap
    matchLabels:
      validate: "true"
  concurrencyPolicy: Allow
  operationTemplate:
    spec:
      mode: Pipeline
      pipeline:
      - step: validate
        functionRef:
          name: function-config-validator
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: ConfigValidatorInput
          rules:
          - required: ["database.url", "database.port"]
          - format: "email"
            field: "notification.email"
      - step: notify
        functionRef:
          name: function-slack-notifier
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: SlackNotifierInput
          channel: "#alerts"
          severity: "warning"

⚠️ 중요: WatchOperations는 alpha 기능입니다. Crossplane 인자에 --enable-operations를 추가해서 Operations를 반드시 활성화해야 해요.

주요 특징 (Key features)

  • 모든 Kubernetes 리소스 유형 감시 - Crossplane 리소스로 제한되지 않음
  • 네임스페이스와 라벨 필터링 지원 - 특정 리소스 대상 지정
  • 변경된 리소스 자동 주입 - 함수가 트리거 리소스를 받음
  • 구성 가능한 동시성 정책 - Operation 생성 제어

리소스 감시 (Resource watching)

WatchOperations는 유연한 필터링으로 어떤 Kubernetes 리소스든 감시할 수 있습니다.

특정 유형의 모든 리소스 감시

spec:
  watch:
    apiVersion: apps/v1
    kind: Deployment

특정 네임스페이스의 리소스 감시

spec:
  watch:
    apiVersion: v1
    kind: ConfigMap
    namespace: production

특정 라벨이 있는 리소스 감시

spec:
  watch:
    apiVersion: example.org/v1
    kind: Database
    matchLabels:
      backup: "enabled"
      environment: "production"

클러스터 범위 리소스 감시

spec:
  watch:
    apiVersion: v1
    kind: Node
    matchLabels:
      node-role.kubernetes.io/worker: ""

리소스 주입 (Resource injection)

WatchOperation이 Operation을 생성할 때, 특별한 요구사항 이름 ops.crossplane.io/watched-resource로 변경된 리소스를 자동으로 주입합니다. 함수는 명시적으로 요청하지 않아도 이 리소스에 접근할 수 있어요.

예를 들어, 라벨이 validate: "true"인 ConfigMap이 변경되면 WatchOperation은 이런 Operation을 생성합니다.

apiVersion: ops.crossplane.io/v1alpha1
kind: Operation
metadata:
  name: config-validator-abc123
spec:
  mode: Pipeline
  pipeline:
  - step: validate
    functionRef:
      name: function-config-validator
    requirements:
      requiredResources:
      - requirementName: ops.crossplane.io/watched-resource
        apiVersion: v1
        kind: ConfigMap
        name: my-config
        namespace: default
    # ... other pipeline steps from operationTemplate

감시된 리소스는 함수의 req.required_resources에서 특별한 이름 ops.crossplane.io/watched-resource 아래 자동으로 사용 가능합니다.

동시성 정책 (Concurrency policies)

WatchOperations는 CronOperations와 동일한 동시성 정책을 지원합니다.

  • Allow (기본값): 여러 Operations가 동시에 실행될 수 있음. 서로 간섭하지 않는 작업에 사용하세요.
  • Forbid: 이전 작업이 아직 실행 중이면 새 Operations가 시작되지 않음. 동시에 실행할 수 없는 작업에 사용하세요.
  • Replace: 새 Operations가 시작하기 전에 실행 중인 작업을 중지함. 항상 최신 작업을 실행하려 할 때 사용하세요.

일반적인 사용 사례 (Common use cases)

📝 참고: 아래 예제는 설명을 위한 가상의 함수를 사용합니다. 출시 시점에는 function-python만 operations를 지원해요.

구성 검증 (Configuration validation)

ConfigMap이 변경될 때 검증하세요.

apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: config-validator
spec:
  watch:
    apiVersion: v1
    kind: ConfigMap
    matchLabels:
      validate: "true"
  operationTemplate:
    spec:
      mode: Pipeline
      pipeline:
      - step: validate-config
        functionRef:
          name: function-config-validator
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: ConfigValidatorInput
          rules:
          - required: ["database.host", "database.port"]
          - format: "email"
            field: "notification.email"

삭제 시 데이터베이스 백업 (Database backup on deletion)

데이터베이스가 삭제되기 전에 백업하세요.

apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: backup-on-deletion
spec:
  watch:
    apiVersion: rds.aws.m.upbound.io/v1beta1
    kind: Instance
    # Note: Watching for deletion requires function logic
    # to check deletion timestamp
  operationTemplate:
    spec:
      mode: Pipeline
      pipeline:
      - step: create-backup
        functionRef:
          name: function-rds-backup
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: RDSBackupInput
          retentionDays: 30

리소스 실패 알림 (Resource failure alerting)

리소스가 실패 상태에 들어가면 알림을 보내세요.

apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: failure-alerts
spec:
  watch:
    apiVersion: example.org/v1
    kind: App
    matchLabels:
      alert: "enabled"
  operationTemplate:
    spec:
      mode: Pipeline
      pipeline:
      - step: check-status
        functionRef:
          name: function-status-checker
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: StatusCheckerInput
          alertConditions:
          - type: "Ready"
            status: "False"
      - step: send-alert
        functionRef:
          name: function-alertmanager
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: AlertInput
          severity: "critical"

고급 구성 (Advanced configuration)

고급 감시 패턴 (Advanced watch patterns)

여러 조건이 있는 복잡한 리소스 감시입니다.

# Watch Deployments in specific namespaces with multiple label conditions
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: multi-condition-watcher
spec:
  watch:
    apiVersion: apps/v1
    kind: Deployment
    namespace: production  # Only production namespace
    matchLabels:
      app.kubernetes.io/managed-by: "crossplane"
      environment: "prod"
      backup-required: "true"
  operationTemplate:
    spec:
      mode: Pipeline
      pipeline:
      - step: backup-deployment
        functionRef:
          name: function-deployment-backup
# Watch custom resources across all namespaces
apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: database-lifecycle-manager
spec:
  watch:
    apiVersion: database.example.io/v1
    kind: PostgreSQLInstance
    # No namespace specified = watch all namespaces
    matchLabels:
      lifecycle-management: "enabled"
  operationTemplate:
    spec:
      mode: Pipeline
      pipeline:
      - step: lifecycle-check
        functionRef:
          name: function-database-lifecycle
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: DatabaseLifecycleInput
          checkDeletionTimestamp: true
          autoBackup: true

교차 리소스 워크플로 (Cross-resource workflows)

WatchOperations는 하나의 리소스 유형을 감시하면서 관련 리소스를 동적으로 가져올 수 있습니다. Ingress를 감시하고 인증서를 관리하는 WatchOperation 예입니다.

apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: ingress-certificate-manager
spec:
  watch:
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    matchLabels:
      auto-cert: "enabled"
  operationTemplate:
    spec:
      mode: Pipeline
      pipeline:
      - step: manage-certificates
        functionRef:
          name: function-cert-manager
        input:
          apiVersion: fn.crossplane.io/v1beta1
          kind: CertManagerInput
          issuer: "letsencrypt-prod"
          renewBefore: "720h"  # 30 days

함수는 감시된 Ingress를 검사하고 관련 리소스를 동적으로 요청합니다.

from crossplane.function import request, response

def operate(req, rsp):
    # Access the watched Ingress resource
    ingress = request.get_required_resource(req, "ops.crossplane.io/watched-resource")
    if not ingress:
        response.fatal(rsp, "No watched resource found")
        return
    
    # Extract the service name from the Ingress backend
    rules = ingress.get("spec", {}).get("rules", [])
    if not rules:
        response.fatal(rsp, "Could not extract service name from ingress")
        return
        
    backend = rules[0].get("http", {}).get("paths", [{}])[0].get("backend", {})
    service_name = backend.get("service", {}).get("name")
    if not service_name:
        response.fatal(rsp, "Could not extract service name from ingress")
        return
        
    ingress_namespace = ingress.get("metadata", {}).get("namespace", "default")
    
    # CRITICAL: Always request the same resources to ensure requirement
    # stabilization. Crossplane calls the function repeatedly until 
    # requirements don't change.
    response.require_resources(
        rsp, 
        name="related-service",
        api_version="v1",
        kind="Service",
        match_name=service_name,
        namespace=ingress_namespace
    )
    
    # Check if the service is available and process accordingly
    service = request.get_required_resource(req, "related-service")
    if service:
        # Success: Both resources available
        response.set_output(rsp, {
            "status": "success",
            "message": "Certificate management completed",
            "ingress_host": ingress.get("spec", {}).get("rules", [{}])[0].get("host"),
            "service_name": service.get("metadata", {}).get("name")
        })
        return
        
    # Waiting: Service not available yet
    response.set_output(rsp, {
        "status": "waiting", 
        "message": f"Waiting for service '{service_name}' to be available"
    })

⚠️ 중요: 핵심 리소스 안정화 패턴입니다. 함수는 완료를 알리기 위해 매 반복마다 동일한 요구사항을 반환해야 해요. 앞의 예제 함수는 서비스 존재 여부와 무관하게 항상 response.require_resources()를 호출합니다. 이렇게 해야 Crossplane이 언제 함수 호출을 멈출지 알 수 있어요.

흔한 실수: 리소스가 없을 때만 요청하면 안정화 계약이 깨져서 시간 초과 오류가 발생합니다.

이 패턴을 통해 함수는 다음을 할 수 있습니다.

  • 감시된 리소스 검사 (자동 주입됨)
  • 함수가 필요로 하는 다른 리소스를 동적으로 결정
  • response.require_resources()로 해당 리소스를 일관되게 요청
  • 사용 가능할 때 모든 리소스를 처리하거나, 대기 중이면 상태 제공

상태와 모니터링 (Status and monitoring)

WatchOperations는 감시에 대한 상태 정보를 제공합니다.

status:
  conditions:
  - type: Synced
    status: "True"
    reason: ReconcileSuccess
  - type: Watching
    status: "True"
    reason: WatchActive
  watchingResources: 12
  runningOperationRefs:
  - name: config-validator-anjda
  - name: config-validator-f0d92

주요 상태 필드:

  • Conditions: 표준 Crossplane 조건(Synced)과 WatchOperation 고유 조건. Watching: WatchOperation이 리소스를 적극적으로 감시하면 True, 일시 중지되거나 실패하면 False.
  • watchingResources: 감시 중인 리소스 수.
  • runningOperationRefs: 이 WatchOperation이 만든 실행 중인 Operations.

이벤트 (Events)

WatchOperations는 중요한 활동에 대해 이벤트를 발생시킵니다.

  • EstablishWatched (Warning) - 감시 설정 실패
  • TerminateWatched (Warning) - 감시 종료 실패
  • GarbageCollectOperations (Warning) - Operation 정리 실패
  • CreateOperation (Warning) - Operation 생성 실패
  • ReplaceRunningOperation (Warning) - Operation 교체 실패

모니터링 (Monitoring)

다음을 사용해 WatchOperations를 모니터링하세요.

# Check WatchOperation status
$ kubectl get watchoperation my-watchop

# View recent Operations created by the WatchOperation
$ kubectl get operations -l crossplane.io/watchoperation=my-watchop

# Check watched resource count
$ kubectl describe watchoperation my-watchop

# Check events
$ kubectl get events --field-selector involvedObject.name=my-watchop

베스트 프랙틱스 (Best practices)

리소스 선택

  • 특정 라벨 셀렉터 사용: 정밀한 필터링으로 불필요한 Operations 방지.
  • 자주 변경되는 리소스 피하기: 빈번하게 변경되는 리소스를 감시할 때는 주의.
  • 작게 시작: 좁은 셀렉터로 시작해 필요에 따라 확장.

이벤트 처리

  • 이벤트 필터링 구현: 불필요한 변경을 처리하지 않도록 generation, 삭제 타임스탬프, 상태 조건을 확인.
  • Operation 볼륨 모니터링: 인기 있는 리소스는 많은 Operations를 만들 수 있음.

동시성 정책

  • 적절한 동시성 정책 선택: 병렬로 실행할 수 있는 독립 처리에는 Allow.
  • 새 변경을 처리하기 전에 완료해야 하는 작업에는 Forbid.
  • 최신 상태만 중요한 상태 확인이나 모니터링에는 Replace.

히스토리 관리 (History management)

CronOperations처럼, WatchOperations도 완료된 Operations를 자동으로 정리합니다.

apiVersion: ops.crossplane.io/v1alpha1
kind: WatchOperation
metadata:
  name: config-validator
spec:
  watch:
    apiVersion: v1
    kind: ConfigMap
  successfulHistoryLimit: 10  # Keep 10 successful Operations (default: 3)
  failedHistoryLimit: 5       # Keep 5 failed Operations (default: 1)
  operationTemplate:
    # Operation template here

감시 리소스 주입 (Watched resource injection)

WatchOperations는 특별한 요구사항 이름 ops.crossplane.io/watched-resource를 사용해 변경된 리소스를 생성된 Operation에 자동으로 주입합니다.

from crossplane.function import request, response

def operate(req, rsp):
    # Access the resource that triggered this Operation
    watched_resource = request.get_required_resource(req, "ops.crossplane.io/watched-resource")
    if not watched_resource:
        response.set_output(rsp, {"error": "No watched resource found"})
        return
    
    # Process based on the watched resource
    if watched_resource["kind"] == "ConfigMap":
        config_data = watched_resource["data"]
        # Validate configuration...

감시된 리소스는 Operation 템플릿에 선언할 필요 없이 함수의 required_resources 맵에서 사용 가능합니다.

함수 개발과 운영 고려 사항을 포함한 일반적인 Operations 베스트 프랙틱스는 Operation 베스트 프랙틱스를 참고하세요.

트러블슈팅 (Troubleshooting)

WatchOperation이 Operations를 생성하지 않음

  • WatchOperation이 Watching=True 조건을 갖는지 확인.
  • 감시된 리소스가 존재하고 셀렉터와 일치하는지 확인.
  • 리소스가 실제로 변경되고 있는지 확인.
  • 감시 설정 실패를 나타내는 이벤트를 찾아보세요.

너무 많은 Operations 생성됨

  • 라벨 셀렉터를 더 적은 리소스와 일치하도록 정제.
  • Forbid 또는 Replace 동시성 정책 사용 고려.
  • 리소스가 예상보다 더 자주 변경되는지 확인.
  • 함수 로직이 리소스 업데이트를 유발하지 않는지 검토.

감시 리소스를 처리하지 못하는 Operations

  • 함수 능력에 operation이 포함되는지 확인.
  • 함수가 ops.crossplane.io/watched-resource를 처리하는지 확인.
  • 처리 오류에 대한 함수 로그를 검토.
  • 함수가 감시 중인 특정 리소스 유형을 처리할 수 있는지 확인.

다음 단계 (Next steps)

더 알아보기 (Learn more)